001package com.nimbusds.infinispan.persistence.ldap; 002 003 004import java.util.Properties; 005 006import com.nimbusds.common.config.*; 007import com.thetransactioncompany.util.PropertyParseException; 008import com.thetransactioncompany.util.PropertyRetriever; 009import com.unboundid.ldap.sdk.DN; 010import com.unboundid.ldap.sdk.LDAPException; 011import net.jcip.annotations.Immutable; 012import org.apache.commons.collections4.MapUtils; 013import org.infinispan.commons.configuration.BuiltBy; 014import org.infinispan.commons.configuration.ConfigurationFor; 015import org.infinispan.commons.util.StringPropertyReplacer; 016import org.infinispan.configuration.cache.AbstractStoreConfiguration; 017import org.infinispan.configuration.cache.AsyncStoreConfiguration; 018import org.infinispan.configuration.cache.SingletonStoreConfiguration; 019 020 021/** 022 * LDAP store configuration. It's typically derived from a Java key / value 023 * properties file. The configuration is stored as public fields which become 024 * immutable (final) after their initialisation. 025 * 026 * <p>Example LDAP store configuration: 027 * 028 * <pre> 029 * # LDAP server details # 030 * ldapServer.url = ldap://localhost:1389 ldap://remotehost:1389 031 * ldapServer.selectionAlgorithm = FAILOVER 032 * ldapServer.connectTimeout = 100 033 * ldapServer.responseTimeout = 100 034 * ldapServer.security = NONE 035 * ldapServer.trustSelfSignedCerts = false 036 * ldapServer.connectionPoolSize = 5 037 * ldapServer.connectionPoolInitialSize = 0 038 * ldapServer.connectionPoolMaxWaitTime = 100 039 * ldapServer.connectionMaxAge = 0 040 * 041 * # LDAP user details # 042 * ldapUser.dn = cn=Directory Manager 043 * ldapUser.password = secret 044 * 045 * # LDAP directory entry details # 046 * ldapDirectory.baseDN = ou=authorizations, dc=wonderland, dc=net 047 * ldapDirectory.pageSize = 500 048 * ldapDirectory.entryTransformer = com.nimbusds.infinispan.persistence.ldap.UserEntityTransformer 049 * 050 * # Custom LDAP sever trust and key store # 051 * customTrustStore.enable = false 052 * customTrustStore.file = 053 * customTrustStore.password = 054 * customTrustStore.type = 055 * 056 * customKeyStore.enable = false 057 * customKeyStore.file = 058 * customKeyStore.password = 059 * customKeyStore.type = 060 * </pre> 061 */ 062@Immutable 063@BuiltBy(LDAPStoreConfigurationBuilder.class) 064@ConfigurationFor(LDAPStore.class) 065public class LDAPStoreConfiguration extends AbstractStoreConfiguration implements LoggableConfiguration { 066 067 068 /** 069 * LDAP directory entry configuration. 070 */ 071 public static class LDAPDirectory implements LoggableConfiguration { 072 073 074 /** 075 * The distinguished name (DN) of the base directory entry 076 * under which the persisted entries are stored. 077 * 078 * <p>Property key: ldapDirectory.baseDN 079 */ 080 public final DN baseDN; 081 082 083 /** 084 * The page size to use when retrieving multiple directory 085 * entries. 086 * 087 * <p>See LDAP Control Extension for Simple Paged Results 088 * Manipulation (RFC 2696). 089 * 090 * <p>Property key: ldapDirectory.pageSize 091 */ 092 public final int pageSize; 093 094 095 096 /** 097 * The name of the class for transforming between Infinispan 098 * entries (key / value pair and optional metadata) and a 099 * corresponding LDAP entry (DN with attributes). 100 * 101 * <p>See {@link LDAPEntryTransformer}. 102 * 103 * <p>Property key: ldapDirectory.entryTransformer 104 */ 105 public final String entryTransformer; 106 107 108 /** 109 * Creates a new LDAP directory entry configuration from the 110 * specified properties. 111 * 112 * @param props The properties. Must not be {@code null}. 113 * 114 * @throws PropertyParseException On a missing or invalid 115 * property. 116 */ 117 public LDAPDirectory(final Properties props) 118 throws PropertyParseException { 119 120 PropertyRetriever pr = new PropertyRetriever(props); 121 122 String dnString = null; 123 try { 124 dnString = pr.getString("ldapDirectory.baseDN"); 125 baseDN = new DN(dnString); 126 } catch (LDAPException e) { 127 throw new PropertyParseException("Invalid DN", "ldapDirectory.baseDN", dnString); 128 } 129 130 pageSize = pr.getInt("ldapDirectory.pageSize"); 131 132 if (pageSize < 0) { 133 throw new PropertyParseException("The page size must be 0 (paging disabled) or positive", "ldapDirectory.pageSize", pageSize + ""); 134 } 135 136 entryTransformer = pr.getString("ldapDirectory.entryTransformer"); 137 } 138 139 140 @Override 141 public void log() { 142 143 Loggers.MAIN_LOG.info("[IL0000] Infinispan LDAP store: Directory base DN: {} ", baseDN); 144 Loggers.MAIN_LOG.info("[IL0001] Infinispan LDAP store: Page size: {} ", pageSize); 145 Loggers.MAIN_LOG.info("[IL0002] Infinispan LDAP store: Entry transformer class: {} ", entryTransformer); 146 } 147 } 148 149 150 /** 151 * The LDAP server details. 152 * 153 * <p>Property key: ldapServer 154 */ 155 public final LDAPServerConnectionPoolDetails ldapServer; 156 157 158 /** 159 * The LDAP directory user details. The user must have read and add 160 * permissions to the directory tree containing the persisted entries. 161 * 162 * <p>Property key: ldapUser 163 */ 164 public final DirectoryUser ldapUser; 165 166 167 /** 168 * The LDAP directory entry details. 169 * 170 * <p>Property key: ldapDirectory 171 */ 172 public final LDAPDirectory ldapDirectory; 173 174 175 /** 176 * The custom trust store for secure LDAP server connections. 177 * 178 * <p>Property key: customTrustStore 179 */ 180 public final CustomTrustStoreConfiguration customTrustStore; 181 182 183 /** 184 * The custom key store for secure LDAP server connections. 185 * 186 * <p>Property key: customKeyStore 187 */ 188 public final CustomKeyStoreConfiguration customKeyStore; 189 190 191 /** 192 * Creates a new LDAP store configuration from the specified 193 * properties. All other settings assume defaults. 194 * 195 * @param properties The LDAP store specific configuration properties. 196 * Must not be {@code null}. 197 */ 198 public LDAPStoreConfiguration(final Properties properties) { 199 200 this( 201 false, // purgeOnStartup 202 false, // fetchPersistentState 203 false, // ignoreModifications 204 null, // AsyncStoreConfiguration 205 null, // SingletonStoreConfiguration 206 false, // preload 207 true, // shared 208 properties); 209 } 210 211 212 /** 213 * Creates a new LDAP store configuration. 214 * 215 * @param purgeOnStartup If {@code true} the cache store will be 216 * purged when it starts up. 217 * @param fetchPersistentState If {@code true} the persistent state 218 * be fetched when joining a cluster. 219 * @param ignoreModifications If {@code true} any operation that 220 * modifies the cache (put, remove, clear, 221 * store...etc) won't be applied to the 222 * cache store. This means that the cache 223 * store could become out of sync with the 224 * cache. 225 * @param async Configuration for the async cache 226 * loader. 227 * @param singletonStore Configuration for a singleton store. 228 * @param preload If {@code true} when the cache starts 229 * data stored in the cache loader will be 230 * pre-loaded into memory. 231 * @param shared If {@code true} the cache store is 232 * shared among all cache instances. 233 * @param properties The LDAP store specific configuration 234 * properties. Must not be {@code null}. 235 */ 236 public LDAPStoreConfiguration(final boolean purgeOnStartup, 237 final boolean fetchPersistentState, 238 final boolean ignoreModifications, 239 final AsyncStoreConfiguration async, 240 final SingletonStoreConfiguration singletonStore, 241 final boolean preload, 242 final boolean shared, 243 final Properties properties) { 244 245 super( 246 purgeOnStartup, 247 fetchPersistentState, 248 ignoreModifications, 249 async, 250 singletonStore, 251 preload, 252 shared, 253 properties); 254 255 if (MapUtils.isEmpty(properties)) { 256 throw new ConfigurationException("Missing LDAP store configuration properties, check the service documentation"); 257 } 258 259 // Interpolate with system properties where ${sysPropName} is found 260 Properties interpolatedProps = new Properties(); 261 for (String name: properties.stringPropertyNames()) { 262 interpolatedProps.setProperty(name, StringPropertyReplacer.replaceProperties(properties.getProperty(name))); 263 } 264 265 try { 266 ldapServer = new LDAPServerConnectionPoolDetails("ldapServer.", interpolatedProps); 267 ldapUser = new DirectoryUser("ldapUser.", interpolatedProps); 268 ldapDirectory = new LDAPDirectory(interpolatedProps); 269 customTrustStore = new CustomTrustStoreConfiguration("customTrustStore.", interpolatedProps); 270 customKeyStore = new CustomKeyStoreConfiguration("customKeyStore.", interpolatedProps); 271 272 } catch (PropertyParseException e) { 273 throw new ConfigurationException(e.getMessage() + 274 ": Property: " + e.getPropertyKey() + 275 ": Value: " + e.getPropertyValue()); 276 } 277 } 278 279 280 @Override 281 public void log() { 282 ldapServer.log(); 283 ldapUser.log(); 284 ldapDirectory.log(); 285 customTrustStore.log(); 286 customKeyStore.log(); 287 } 288}