1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package no.feide.moria.directory.backend;
21
22 import java.io.File;
23 import java.security.Security;
24
25 import no.feide.moria.directory.DirectoryManagerConfigurationException;
26
27 import org.jdom.Element;
28
29 /***
30 * Factory class for JNDI backends.
31 */
32 public class JNDIBackendFactory
33 implements DirectoryManagerBackendFactory {
34
35 /***
36 * The number of seconds before a backend connection times out. Default is
37 * 15 seconds.
38 */
39 private int backendTimeouts = 15;
40
41 /***
42 * Whether the backend should use SSL. Default is <code>false</code>.
43 */
44 private boolean useSSL = false;
45
46 /*** The name of the attribute containing the username. */
47 private String usernameAttribute;
48
49 /***
50 * The name of the attribute used to guess the user element's (R)DN, if it
51 * cannot be found by searching.
52 */
53 private String guessedAttribute;
54
55
56 /***
57 * Sets the factory-specific configuration. Must be called before creating a
58 * new backend, or the backend will not work as intended. <br>
59 * <br>
60 * Note that using this method with an updated configuration that modifies
61 * any JVM global settings (currently all settings in the
62 * <code>Security</code> element) is likely to cause any open backend
63 * connections to fail.
64 * @param config
65 * The configuration for this backend factory. The root node must
66 * be a <code>JNDI</code> element, containing an optional
67 * <code>Security</code> element, which in turn may contain an
68 * optional <code>Truststore</code> element. If the
69 * <code>Truststore</code> exists, it must contain the
70 * attributes <code>filename</code> and <code>password</code>,
71 * giving the truststore file location and password,
72 * respectively. The <code>JNDI</code> element may contain an
73 * optional attribute <code>timeout</code>, which gives the
74 * number of seconds before a backend connection should time out.
75 * If this value is a negative number, the timeout value will be
76 * set to zero (meaning the connection will never time out).
77 * Also, the <code>JNDI</code> element may contain an attribute
78 * <code>guessedAttribute</code>, which should give the name
79 * of an attribute used to construct "guessed" user element
80 * (R)DNs if the actual element cannot be found by searching (the
81 * default value is <code>uid</code>). Finally the
82 * <code>JNDI</code> element must contain an attribute
83 * <code>usernameAttribute</code>, which should give the name
84 * of the attribute holding the username.
85 * @throws IllegalArgumentException
86 * If <code>config</code> is null.
87 * @throws DirectoryManagerConfigurationException
88 * If the configuration element is not a <code>JNDI</code>
89 * <code>Backend</code>
90 * element, or if the optional <code>Truststore</code> element
91 * is found, but without either of the <code>filename</code>
92 * or <code>password</code> attributes. Also thrown if the
93 * <code>timeout</code> attribute contains an illegal timeout
94 * value, if the <code>username</code> attribute does not
95 * exist, if the <code>guess</code> attribute does not exist,
96 * or if the <code>filename</code> file does not exist.
97 * @see DirectoryManagerBackendFactory#setConfig(Element)
98 */
99 public final synchronized void setConfig(final Element config) throws DirectoryManagerConfigurationException {
100
101
102 if (config == null)
103 throw new IllegalArgumentException("Configuration cannot be NULL");
104 if (!config.getName().equalsIgnoreCase("Backend"))
105 throw new DirectoryManagerConfigurationException("Cannot find backend configuration element");
106
107
108 final Element jndiElement = config.getChild("JNDI");
109 if (jndiElement == null)
110 throw new DirectoryManagerConfigurationException("Cannot find JNDI configuration element");
111
112
113 String timeout = jndiElement.getAttributeValue("timeout");
114 if (timeout != null) {
115 try {
116 backendTimeouts = Integer.parseInt(timeout);
117 } catch (NumberFormatException e) {
118 throw new DirectoryManagerConfigurationException("\"" + timeout + "\" is not a legal timeout value", e);
119 }
120 }
121 if (backendTimeouts < 0)
122 backendTimeouts = 0;
123
124
125 usernameAttribute = jndiElement.getAttributeValue("usernameAttribute");
126 if (usernameAttribute == null)
127 throw new DirectoryManagerConfigurationException("Attribute \"usernameAttribute\" not found in JNDI element");
128 guessedAttribute = jndiElement.getAttributeValue("guessedAttribute", "uid");
129
130
131
132
133 final Element securityElement = jndiElement.getChild("Security");
134 if (securityElement != null) {
135
136
137 final Element trustStoreElement = securityElement.getChild("Truststore");
138 if (trustStoreElement != null) {
139
140
141 String value = trustStoreElement.getAttributeValue("filename");
142 if (value == null)
143 throw new DirectoryManagerConfigurationException("Attribute \"filename\" not found in Truststore element");
144 if (!(new File(value).exists()))
145 throw new DirectoryManagerConfigurationException("Truststore file " + value + " does not exist");
146
147
148 com.sun.net.ssl.internal.ssl.Provider.install();
149 Security.insertProviderAt(new com.sun.net.ssl.internal.ssl.Provider(), 1);
150 System.setProperty("java.protocol.handler.pkgs", "javax.net.ssl");
151
152
153 System.setProperty("javax.net.ssl.trustStore", value);
154
155
156 value = trustStoreElement.getAttributeValue("password");
157 if (value == null)
158 throw new DirectoryManagerConfigurationException("Attribute \"password\" not found in Truststore element");
159 System.setProperty("javax.net.ssl.trustStorePassword", value);
160
161
162 Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
163 useSSL = true;
164
165 }
166
167 }
168
169 }
170
171
172 /***
173 * Creates a new <code>JNDIBackend</code> instance.
174 * @param sessionTicket
175 * The session ticket passed on to instances of
176 * <code>DirectoryManagerBackend</code> (actually
177 * <code>JNDIBackend</code> instances) for logging purposes.
178 * May be <code>null</code> or an empty string.
179 * @return The new JNDIBackend.
180 * @see DirectoryManagerBackendFactory#createBackend(String)
181 */
182 public final synchronized DirectoryManagerBackend createBackend(final String sessionTicket) {
183
184 return new JNDIBackend(sessionTicket, backendTimeouts, useSSL, usernameAttribute, guessedAttribute);
185
186 }
187
188 }