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.core.authz;
21
22
23 import javax.naming.NoPermissionException;
24
25 import java.util.HashSet;
26 import java.util.Map;
27 import java.util.Set;
28
29 import org.apache.directory.server.constants.ServerDNConstants;
30 import org.apache.directory.server.core.CoreSession;
31 import org.apache.directory.server.core.DefaultCoreSession;
32 import org.apache.directory.server.core.DirectoryService;
33 import org.apache.directory.server.core.authn.LdapPrincipal;
34 import org.apache.directory.server.core.entry.ClonedServerEntry;
35 import org.apache.directory.server.core.entry.ServerEntry;
36 import org.apache.directory.server.core.filtering.EntryFilter;
37 import org.apache.directory.server.core.filtering.EntryFilteringCursor;
38 import org.apache.directory.server.core.interceptor.BaseInterceptor;
39 import org.apache.directory.server.core.interceptor.Interceptor;
40 import org.apache.directory.server.core.interceptor.NextInterceptor;
41 import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
42 import org.apache.directory.server.core.interceptor.context.ListOperationContext;
43 import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
44 import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
45 import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
46 import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
47 import org.apache.directory.server.core.interceptor.context.OperationContext;
48 import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
49 import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
50 import org.apache.directory.server.core.interceptor.context.SearchingOperationContext;
51 import org.apache.directory.server.core.partition.PartitionNexus;
52 import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
53 import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
54 import org.apache.directory.shared.ldap.constants.SchemaConstants;
55 import org.apache.directory.shared.ldap.entry.EntryAttribute;
56 import org.apache.directory.shared.ldap.entry.Value;
57 import org.apache.directory.shared.ldap.exception.LdapNoPermissionException;
58 import org.apache.directory.shared.ldap.name.LdapDN;
59 import org.apache.directory.shared.ldap.schema.AttributeType;
60 import org.apache.directory.shared.ldap.schema.OidNormalizer;
61 import org.slf4j.Logger;
62 import org.slf4j.LoggerFactory;
63
64
65
66
67
68
69
70
71
72
73
74
75
76 public class DefaultAuthorizationInterceptor extends BaseInterceptor
77 {
78
79 private static final Logger LOG = LoggerFactory.getLogger( DefaultAuthorizationInterceptor.class );
80
81
82
83
84 private static LdapDN USER_BASE_DN;
85
86
87
88
89 private static LdapDN GROUP_BASE_DN;
90
91
92
93
94 private static LdapDN ADMIN_GROUP_DN;
95
96
97
98
99 private boolean enabled = true;
100
101 private Set<String> administrators = new HashSet<String>(2);
102
103
104 private Map<String, OidNormalizer> normalizerMapping;
105
106 private PartitionNexus nexus;
107
108
109 private AttributeType uniqueMemberAT;
110
111
112
113
114
115 public DefaultAuthorizationInterceptor()
116 {
117 }
118
119
120 public void init( DirectoryService directoryService ) throws Exception
121 {
122 nexus = directoryService.getPartitionNexus();
123 normalizerMapping = directoryService.getRegistries().getAttributeTypeRegistry().getNormalizerMapping();
124
125
126 enabled = ! directoryService.isAccessControlEnabled();
127
128 USER_BASE_DN = PartitionNexus.getUsersBaseName();
129 USER_BASE_DN.normalize( normalizerMapping );
130
131 GROUP_BASE_DN = PartitionNexus.getGroupsBaseName();
132 GROUP_BASE_DN.normalize( normalizerMapping );
133
134 ADMIN_GROUP_DN = new LdapDN( ServerDNConstants.ADMINISTRATORS_GROUP_DN );
135 ADMIN_GROUP_DN.normalize( normalizerMapping );
136
137 AttributeTypeRegistry attrRegistry = directoryService.getRegistries().getAttributeTypeRegistry();
138
139 uniqueMemberAT = attrRegistry.lookup( SchemaConstants.UNIQUE_MEMBER_AT_OID );
140
141 loadAdministrators( directoryService );
142 }
143
144
145 private void loadAdministrators( DirectoryService directoryService ) throws Exception
146 {
147
148 Set<String> newAdministrators = new HashSet<String>( 2 );
149 LdapDN adminDn = new LdapDN( ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
150 adminDn.normalize( directoryService.getRegistries().getAttributeTypeRegistry().getNormalizerMapping() );
151 CoreSession adminSession = new DefaultCoreSession(
152 new LdapPrincipal( adminDn, AuthenticationLevel.STRONG ), directoryService );
153
154 ServerEntry adminGroup = nexus.lookup( new LookupOperationContext( adminSession, ADMIN_GROUP_DN ) );
155
156 if ( adminGroup == null )
157 {
158 return;
159 }
160
161 EntryAttribute uniqueMember = adminGroup.get( uniqueMemberAT );
162
163 for ( Value<?> value:uniqueMember )
164 {
165 LdapDN memberDn = new LdapDN( ( String ) value.get() );
166 memberDn.normalize( normalizerMapping );
167 newAdministrators.add( memberDn.getNormName() );
168 }
169
170 administrators = newAdministrators;
171 }
172
173
174
175
176
177
178 public void delete( NextInterceptor nextInterceptor, DeleteOperationContext opContext ) throws Exception
179 {
180 LdapDN name = opContext.getDn();
181
182 if ( !enabled )
183 {
184 nextInterceptor.delete( opContext );
185 return;
186 }
187
188 LdapDN principalDn = getPrincipal().getJndiName();
189
190 if ( name.isEmpty() )
191 {
192 String msg = "The rootDSE cannot be deleted!";
193 LOG.error( msg );
194 throw new LdapNoPermissionException( msg );
195 }
196
197 if ( name.getNormName().equals( ADMIN_GROUP_DN.getNormName() ) )
198 {
199 String msg = "The Administrators group cannot be deleted!";
200 LOG.error( msg );
201 throw new LdapNoPermissionException( msg );
202 }
203
204 if ( isTheAdministrator( name ) )
205 {
206 String msg = "User " + principalDn.getUpName();
207 msg += " does not have permission to delete the admin account.";
208 msg += " No one not even the admin can delete this account!";
209 LOG.error( msg );
210 throw new LdapNoPermissionException( msg );
211 }
212
213 if ( name.size() > 2 )
214 {
215 if ( !isAnAdministrator( principalDn ) )
216 {
217 if ( name.startsWith( USER_BASE_DN ) )
218 {
219 String msg = "User " + principalDn.getUpName();
220 msg += " does not have permission to delete the user account: ";
221 msg += name.getUpName() + ". Only the admin can delete user accounts.";
222 LOG.error( msg );
223 throw new LdapNoPermissionException( msg );
224 }
225
226 if ( name.startsWith( GROUP_BASE_DN ) )
227 {
228 String msg = "User " + principalDn.getUpName();
229 msg += " does not have permission to delete the group entry: ";
230 msg += name.getUpName() + ". Only the admin can delete groups.";
231 LOG.error( msg );
232 throw new LdapNoPermissionException( msg );
233 }
234 }
235 }
236
237 nextInterceptor.delete( opContext );
238 }
239
240
241 private boolean isTheAdministrator( LdapDN normalizedDn )
242 {
243 return normalizedDn.getNormName().equals( ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
244 }
245
246
247 private boolean isAnAdministrator( LdapDN normalizedDn )
248 {
249 return isTheAdministrator( normalizedDn ) || administrators.contains( normalizedDn.getNormName() );
250
251 }
252
253
254
255
256
257
258
259
260
261
262
263
264 public void modify( NextInterceptor nextInterceptor, ModifyOperationContext opContext )
265 throws Exception
266 {
267 if ( enabled )
268 {
269 LdapDN dn = opContext.getDn();
270
271 protectModifyAlterations( dn );
272 nextInterceptor.modify( opContext );
273
274
275 if ( dn.getNormName().equals( ADMIN_GROUP_DN.getNormName() ) )
276 {
277 loadAdministrators( opContext.getSession().getDirectoryService() );
278 }
279 }
280 else
281 {
282 nextInterceptor.modify( opContext );
283 }
284 }
285
286
287 private void protectModifyAlterations( LdapDN dn ) throws Exception
288 {
289 LdapDN principalDn = getPrincipal().getJndiName();
290
291 if ( dn.isEmpty() )
292 {
293 String msg = "The rootDSE cannot be modified!";
294 LOG.error( msg );
295 throw new LdapNoPermissionException( msg );
296 }
297
298 if ( ! isAnAdministrator( principalDn ) )
299 {
300
301 if ( dn.getNormName().equals( getPrincipal().getJndiName().getNormName() ) )
302 {
303 return;
304 }
305
306 if ( dn.getNormName().equals( ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED ) )
307 {
308 String msg = "User " + principalDn.getUpName();
309 msg += " does not have permission to modify the account of the";
310 msg += " admin user.";
311 LOG.error( msg );
312 throw new LdapNoPermissionException( msg );
313 }
314
315 if ( dn.size() > 2 )
316 {
317 if ( dn.startsWith( USER_BASE_DN ) )
318 {
319 String msg = "User " + principalDn.getUpName();
320 msg += " does not have permission to modify the account of the";
321 msg += " user " + dn.getUpName() + ".\nEven the owner of an account cannot";
322 msg += " modify it.\nUser accounts can only be modified by the";
323 msg += " administrator.";
324 LOG.error( msg );
325 throw new LdapNoPermissionException( msg );
326 }
327
328 if ( dn.startsWith( GROUP_BASE_DN ) )
329 {
330 String msg = "User " + principalDn.getUpName();
331 msg += " does not have permission to modify the group entry ";
332 msg += dn.getUpName() + ".\nGroups can only be modified by the admin.";
333 LOG.error( msg );
334 throw new LdapNoPermissionException( msg );
335 }
336 }
337 }
338 }
339
340
341
342
343
344
345
346
347
348
349
350 public void rename( NextInterceptor nextInterceptor, RenameOperationContext opContext )
351 throws Exception
352 {
353 if ( enabled )
354 {
355 protectDnAlterations( opContext.getDn() );
356 }
357
358 nextInterceptor.rename( opContext );
359 }
360
361
362 public void move( NextInterceptor nextInterceptor, MoveOperationContext opContext ) throws Exception
363 {
364 if ( enabled )
365 {
366 protectDnAlterations( opContext.getDn() );
367 }
368
369 nextInterceptor.move( opContext );
370 }
371
372
373 public void moveAndRename( NextInterceptor nextInterceptor, MoveAndRenameOperationContext opContext ) throws Exception
374 {
375 if ( enabled )
376 {
377 protectDnAlterations( opContext.getDn() );
378 }
379
380 nextInterceptor.moveAndRename( opContext );
381 }
382
383
384 private void protectDnAlterations( LdapDN dn ) throws Exception
385 {
386 LdapDN principalDn = getPrincipal().getJndiName();
387
388 if ( dn.isEmpty() )
389 {
390 String msg = "The rootDSE cannot be moved or renamed!";
391 LOG.error( msg );
392 throw new LdapNoPermissionException( msg );
393 }
394
395 if ( dn.getNormName().equals( ADMIN_GROUP_DN.getNormName() ) )
396 {
397 String msg = "The Administrators group cannot be moved or renamed!";
398 LOG.error( msg );
399 throw new LdapNoPermissionException( msg );
400 }
401
402 if ( isTheAdministrator( dn ) )
403 {
404 String msg = "User '" + principalDn.getUpName();
405 msg += "' does not have permission to move or rename the admin";
406 msg += " account. No one not even the admin can move or";
407 msg += " rename " + dn.getUpName() + "!";
408 LOG.error( msg );
409 throw new LdapNoPermissionException( msg );
410 }
411
412 if ( dn.size() > 2 && dn.startsWith( USER_BASE_DN ) && !isAnAdministrator( principalDn ) )
413 {
414 String msg = "User '" + principalDn.getUpName();
415 msg += "' does not have permission to move or rename the user";
416 msg += " account: " + dn.getUpName() + ". Only the admin can move or";
417 msg += " rename user accounts.";
418 LOG.error( msg );
419 throw new LdapNoPermissionException( msg );
420 }
421
422 if ( dn.size() > 2 && dn.startsWith( GROUP_BASE_DN ) && !isAnAdministrator( principalDn ) )
423 {
424 String msg = "User " + principalDn.getUpName();
425 msg += " does not have permission to move or rename the group entry ";
426 msg += dn.getUpName() + ".\nGroups can only be moved or renamed by the admin.";
427 throw new LdapNoPermissionException( msg );
428 }
429 }
430
431
432 public ClonedServerEntry lookup( NextInterceptor nextInterceptor, LookupOperationContext opContext ) throws Exception
433 {
434 ClonedServerEntry serverEntry = nextInterceptor.lookup( opContext );
435
436 if ( !enabled || ( serverEntry == null ) )
437 {
438 return serverEntry;
439 }
440
441 protectLookUp( opContext.getSession().getEffectivePrincipal().getJndiName(), opContext.getDn() );
442 return serverEntry;
443 }
444
445
446 private void protectLookUp( LdapDN principalDn, LdapDN normalizedDn ) throws Exception
447 {
448 if ( !isAnAdministrator( principalDn ) )
449 {
450 if ( normalizedDn.size() > 2 )
451 {
452 if( normalizedDn.startsWith( USER_BASE_DN ) )
453 {
454
455 if ( normalizedDn.getNormName().equals( principalDn.getNormName() ) )
456 {
457 return;
458 }
459
460 String msg = "Access to user account '" + normalizedDn.getUpName() + "' not permitted";
461 msg += " for user '" + principalDn.getUpName() + "'. Only the admin can";
462 msg += " access user account information";
463 LOG.error( msg );
464 throw new LdapNoPermissionException( msg );
465 }
466
467 if ( normalizedDn.startsWith( GROUP_BASE_DN ) )
468 {
469
470 if ( normalizedDn.getNormName().equals( principalDn.getNormName() ) )
471 {
472 return;
473 }
474
475 String msg = "Access to group '" + normalizedDn.getUpName() + "' not permitted";
476 msg += " for user '" + principalDn.getUpName() + "'. Only the admin can";
477 msg += " access group information";
478 LOG.error( msg );
479 throw new LdapNoPermissionException( msg );
480 }
481 }
482
483 if ( isTheAdministrator( normalizedDn ) )
484 {
485
486 if ( normalizedDn.getNormName().equals( principalDn.getNormName() ) )
487 {
488 return;
489 }
490
491 String msg = "Access to admin account not permitted for user '";
492 msg += principalDn.getUpName() + "'. Only the admin can";
493 msg += " access admin account information";
494 LOG.error( msg );
495 throw new LdapNoPermissionException( msg );
496 }
497 }
498 }
499
500
501 public EntryFilteringCursor search( NextInterceptor nextInterceptor, SearchOperationContext opContext ) throws Exception
502 {
503 EntryFilteringCursor cursor = nextInterceptor.search( opContext );
504
505 if ( !enabled )
506 {
507 return cursor;
508 }
509
510 cursor.addEntryFilter( new EntryFilter() {
511 public boolean accept( SearchingOperationContext operation, ClonedServerEntry result ) throws Exception
512 {
513 return DefaultAuthorizationInterceptor.this.isSearchable( operation, result );
514 }
515 } );
516 return cursor;
517 }
518
519
520 public EntryFilteringCursor list( NextInterceptor nextInterceptor, ListOperationContext opContext ) throws Exception
521 {
522 EntryFilteringCursor cursor = nextInterceptor.list( opContext );
523
524 if ( !enabled )
525 {
526 return cursor;
527 }
528
529 cursor.addEntryFilter( new EntryFilter()
530 {
531 public boolean accept( SearchingOperationContext operation, ClonedServerEntry entry ) throws Exception
532 {
533 return DefaultAuthorizationInterceptor.this.isSearchable( operation, entry );
534 }
535 } );
536 return cursor;
537 }
538
539
540 private boolean isSearchable( OperationContext opContext, ClonedServerEntry result ) throws Exception
541 {
542 LdapDN principalDn = opContext.getSession().getEffectivePrincipal().getJndiName();
543 LdapDN dn = result.getDn();
544
545 if ( !dn.isNormalized() )
546 {
547 dn.normalize( normalizerMapping );
548 }
549
550
551 if ( isAnAdministrator( principalDn ) )
552 {
553 return true;
554 }
555
556
557 boolean isSelfRead = dn.getNormName().equals( principalDn.getNormName() );
558
559 if ( isSelfRead )
560 {
561 return true;
562 }
563
564
565 if ( dn.size() > 2 )
566 {
567
568
569
570 if ( dn.getNormName().endsWith( USER_BASE_DN.getNormName() )
571 || dn.getNormName().endsWith( GROUP_BASE_DN.getNormName() ) )
572 {
573 return false;
574 }
575 }
576
577
578 return ! isTheAdministrator( dn );
579
580 }
581 }