1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.directory.server.kerberos;
21
22
23 import org.apache.directory.server.core.DefaultDirectoryService;
24 import org.apache.directory.server.core.DirectoryService;
25 import org.apache.directory.server.core.entry.ServerEntry;
26 import org.apache.directory.server.core.integ.IntegrationUtils;
27 import org.apache.directory.server.core.integ.Level;
28 import org.apache.directory.server.core.integ.annotations.ApplyLdifs;
29 import org.apache.directory.server.core.integ.annotations.CleanupLevel;
30 import org.apache.directory.server.core.integ.annotations.Factory;
31 import org.apache.directory.server.core.interceptor.Interceptor;
32 import org.apache.directory.server.core.kerberos.PasswordPolicyInterceptor;
33 import org.apache.directory.server.core.partition.Partition;
34 import org.apache.directory.server.xdbm.Index;
35 import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndex;
36 import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;
37 import org.apache.directory.server.integ.LdapServerFactory;
38 import org.apache.directory.server.integ.SiRunner;
39 import org.apache.directory.server.ldap.LdapService;
40 import org.apache.directory.server.ldap.handlers.bind.MechanismHandler;
41 import org.apache.directory.server.ldap.handlers.bind.cramMD5.CramMd5MechanismHandler;
42 import org.apache.directory.server.ldap.handlers.bind.digestMD5.DigestMd5MechanismHandler;
43 import org.apache.directory.server.ldap.handlers.bind.gssapi.GssapiMechanismHandler;
44 import org.apache.directory.server.ldap.handlers.bind.ntlm.NtlmMechanismHandler;
45 import org.apache.directory.server.ldap.handlers.bind.plain.PlainMechanismHandler;
46 import org.apache.directory.server.ldap.handlers.extended.StoredProcedureExtendedOperationHandler;
47 import org.apache.directory.server.protocol.shared.SocketAcceptor;
48 import org.apache.directory.shared.ldap.constants.SupportedSaslMechanisms;
49 import org.apache.mina.util.AvailablePortFinder;
50 import org.junit.After;
51 import org.junit.Before;
52 import org.junit.Test;
53 import org.junit.runner.RunWith;
54 import static org.junit.Assert.fail;
55 import static org.junit.Assert.assertTrue;
56 import static org.junit.Assert.assertFalse;
57
58 import javax.naming.NamingException;
59 import javax.naming.directory.Attribute;
60 import javax.naming.directory.Attributes;
61 import javax.naming.directory.BasicAttribute;
62 import javax.naming.directory.BasicAttributes;
63 import javax.naming.directory.DirContext;
64 import javax.naming.directory.InitialDirContext;
65
66 import java.util.HashMap;
67 import java.util.HashSet;
68 import java.util.Hashtable;
69 import java.util.List;
70 import java.util.Map;
71 import java.util.Set;
72
73
74
75
76
77
78
79
80 @RunWith ( SiRunner.class )
81 @CleanupLevel ( Level.CLASS )
82 @Factory ( PasswordPolicyServiceIT.Factory.class )
83 @ApplyLdifs( {
84
85 "dn: dc=example,dc=com\n" +
86 "dc: example\n" +
87 "objectClass: top\n" +
88 "objectClass: domain\n\n"
89 }
90 )
91 public class PasswordPolicyServiceIT
92 {
93 private DirContext ctx;
94 private DirContext users;
95
96
97 public static LdapService ldapService;
98
99
100 public static class Factory implements LdapServerFactory
101 {
102 public LdapService newInstance() throws Exception
103 {
104 DirectoryService service = new DefaultDirectoryService();
105 IntegrationUtils.doDelete( service.getWorkingDirectory() );
106 service.getChangeLog().setEnabled( true );
107 service.setAllowAnonymousAccess( false );
108 service.setShutdownHookEnabled( false );
109
110 Set<Partition> partitions = new HashSet<Partition>();
111 JdbmPartition partition = new JdbmPartition();
112 partition.setId( "example" );
113 partition.setSuffix( "dc=example,dc=com" );
114
115 Set<Index<?,ServerEntry>> indexedAttrs = new HashSet<Index<?,ServerEntry>>();
116 indexedAttrs.add( new JdbmIndex<String,ServerEntry>( "ou" ) );
117 indexedAttrs.add( new JdbmIndex<String,ServerEntry>( "dc" ) );
118 indexedAttrs.add( new JdbmIndex<String,ServerEntry>( "objectClass" ) );
119 partition.setIndexedAttributes( indexedAttrs );
120
121 partitions.add( partition );
122 service.setPartitions( partitions );
123
124 List<Interceptor> list = service.getInterceptors();
125 list.add( new PasswordPolicyInterceptor() );
126 service.setInterceptors( list );
127
128
129
130
131
132 LdapService ldapService = new LdapService();
133 ldapService.setDirectoryService( service );
134 ldapService.setSocketAcceptor( new SocketAcceptor( null ) );
135 ldapService.setIpPort( AvailablePortFinder.getNextAvailable( 1024 ) );
136 ldapService.setAllowAnonymousAccess( false );
137 ldapService.addExtendedOperationHandler( new StoredProcedureExtendedOperationHandler() );
138
139
140
141 Map<String, MechanismHandler> mechanismHandlerMap = new HashMap<String,MechanismHandler>();
142 mechanismHandlerMap.put( SupportedSaslMechanisms.PLAIN, new PlainMechanismHandler() );
143
144 CramMd5MechanismHandler cramMd5MechanismHandler = new CramMd5MechanismHandler();
145 mechanismHandlerMap.put( SupportedSaslMechanisms.CRAM_MD5, cramMd5MechanismHandler );
146
147 DigestMd5MechanismHandler digestMd5MechanismHandler = new DigestMd5MechanismHandler();
148 mechanismHandlerMap.put( SupportedSaslMechanisms.DIGEST_MD5, digestMd5MechanismHandler );
149
150 GssapiMechanismHandler gssapiMechanismHandler = new GssapiMechanismHandler();
151 mechanismHandlerMap.put( SupportedSaslMechanisms.GSSAPI, gssapiMechanismHandler );
152
153 NtlmMechanismHandler ntlmMechanismHandler = new NtlmMechanismHandler();
154 mechanismHandlerMap.put( SupportedSaslMechanisms.NTLM, ntlmMechanismHandler );
155 mechanismHandlerMap.put( SupportedSaslMechanisms.GSS_SPNEGO, ntlmMechanismHandler );
156
157 ldapService.setSaslMechanismHandlers( mechanismHandlerMap );
158 ldapService.setSaslHost( "localhost" );
159
160 return ldapService;
161 }
162 }
163
164
165
166
167
168
169 @Before
170 public void setUp() throws Exception
171 {
172 Attributes attrs;
173 Hashtable<String, String> env = new Hashtable<String, String>();
174 env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
175 env.put( "java.naming.provider.url", "ldap://localhost:" + ldapService.getIpPort() + "/dc=example,dc=com" );
176 env.put( "java.naming.security.principal", "uid=admin,ou=system" );
177 env.put( "java.naming.security.credentials", "secret" );
178 env.put( "java.naming.security.authentication", "simple" );
179 ctx = new InitialDirContext( env );
180
181 attrs = getOrgUnitAttributes( "users" );
182 users = ctx.createSubcontext( "ou=users", attrs );
183 }
184
185
186
187
188
189 @Test
190 public void testLength()
191 {
192 Attributes attrs = getPersonAttributes( "Nelson", "Horatio Nelson", "hnelson", "HN1" );
193 try
194 {
195 users.createSubcontext( "uid=hnelson", attrs );
196 fail( "Shouldn't have gotten here." );
197 }
198 catch ( NamingException ne )
199 {
200 assertTrue( ne.getMessage().contains( "length too short" ) );
201 assertFalse( ne.getMessage().contains( "insufficient character mix" ) );
202 assertFalse( ne.getMessage().contains( "contains portions of username" ) );
203 }
204 }
205
206
207
208
209
210 @Test
211 public void testCharacterMix()
212 {
213 Attributes attrs = getPersonAttributes( "Nelson", "Horatio Nelson", "hnelson", "secret" );
214 try
215 {
216 users.createSubcontext( "uid=hnelson", attrs );
217 fail( "Shouldn't have gotten here." );
218 }
219 catch ( NamingException ne )
220 {
221 assertFalse( ne.getMessage().contains( "length too short" ) );
222 assertTrue( ne.getMessage().contains( "insufficient character mix" ) );
223 assertFalse( ne.getMessage().contains( "contains portions of username" ) );
224 }
225 }
226
227
228
229
230
231 @Test
232 public void testContainsUsername()
233 {
234 Attributes attrs = getPersonAttributes( "Nelson", "Horatio Nelson", "hnelson", "A1nelson" );
235 try
236 {
237 users.createSubcontext( "uid=hnelson", attrs );
238 fail( "Shouldn't have gotten here." );
239 }
240 catch ( NamingException ne )
241 {
242 assertFalse( ne.getMessage().contains( "length too short" ) );
243 assertFalse( ne.getMessage().contains( "insufficient character mix" ) );
244 assertTrue( ne.getMessage().contains( "contains portions of username" ) );
245 }
246 }
247
248
249
250
251
252
253 @Test
254 public void testCharacterMixAndLength()
255 {
256 Attributes attrs = getPersonAttributes( "Nelson", "Horatio Nelson", "hnelson", "hi" );
257 try
258 {
259 users.createSubcontext( "uid=hnelson", attrs );
260 fail( "Shouldn't have gotten here." );
261 }
262 catch ( NamingException ne )
263 {
264 assertTrue( ne.getMessage().contains( "length too short" ) );
265 assertTrue( ne.getMessage().contains( "insufficient character mix" ) );
266 assertFalse( ne.getMessage().contains( "contains portions of username" ) );
267 }
268 }
269
270
271
272
273
274
275 @Test
276 public void testLengthAndContainsUsername()
277 {
278 Attributes attrs = getPersonAttributes( "Bush", "William Bush", "wbush", "bush1" );
279 try
280 {
281 users.createSubcontext( "uid=wbush", attrs );
282 fail( "Shouldn't have gotten here." );
283 }
284 catch ( NamingException ne )
285 {
286 assertTrue( ne.getMessage().contains( "length too short" ) );
287 assertFalse( ne.getMessage().contains( "insufficient character mix" ) );
288 assertTrue( ne.getMessage().contains( "contains portions of username" ) );
289 }
290 }
291
292
293
294
295
296
297 @Test
298 public void testCharacterMixAndContainsUsername()
299 {
300 Attributes attrs = getPersonAttributes( "Nelson", "Horatio Nelson", "hnelson", "hnelson" );
301 try
302 {
303 users.createSubcontext( "uid=hnelson", attrs );
304 fail( "Shouldn't have gotten here." );
305 }
306 catch ( NamingException ne )
307 {
308 assertFalse( ne.getMessage().contains( "length too short" ) );
309 assertTrue( ne.getMessage().contains( "insufficient character mix" ) );
310 assertTrue( ne.getMessage().contains( "contains portions of username" ) );
311 }
312 }
313
314
315
316
317
318
319 @Test
320 public void testCharacterMixAndLengthAndContainsUsername()
321 {
322 Attributes attrs = getPersonAttributes( "Bush", "William Bush", "wbush", "bush" );
323 try
324 {
325 users.createSubcontext( "uid=wbush", attrs );
326 fail( "Shouldn't have gotten here." );
327 }
328 catch ( NamingException ne )
329 {
330 assertTrue( ne.getMessage().contains( "length too short" ) );
331 assertTrue( ne.getMessage().contains( "insufficient character mix" ) );
332 assertTrue( ne.getMessage().contains( "contains portions of username" ) );
333 }
334 }
335
336
337
338
339
340 @After
341 public void tearDown() throws Exception
342 {
343 ctx.close();
344 ctx = null;
345 }
346
347
348
349
350
351 protected Attributes getPersonAttributes( String sn, String cn, String uid, String userPassword )
352 {
353 Attributes attrs = new BasicAttributes( true );
354 Attribute ocls = new BasicAttribute( "objectClass" );
355 ocls.add( "top" );
356 ocls.add( "person" );
357 ocls.add( "inetOrgPerson" );
358 attrs.put( ocls );
359 attrs.put( "cn", cn );
360 attrs.put( "sn", sn );
361 attrs.put( "uid", uid );
362 attrs.put( "userPassword", userPassword );
363
364 return attrs;
365 }
366
367
368
369
370
371 protected Attributes getOrgUnitAttributes( String ou )
372 {
373 Attributes attrs = new BasicAttributes( true );
374 Attribute ocls = new BasicAttribute( "objectClass" );
375 ocls.add( "top" );
376 ocls.add( "organizationalUnit" );
377 attrs.put( ocls );
378 attrs.put( "ou", ou );
379
380 return attrs;
381 }
382 }