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}