KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > ldap > server > authz > AuthorizationService


1 /*
2  * Copyright 2004 The Apache Software Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */

17 package org.apache.ldap.server.authz;
18
19
20 import org.apache.ldap.common.exception.LdapNoPermissionException;
21 import org.apache.ldap.common.name.DnParser;
22 import org.apache.ldap.server.BackingStore;
23 import org.apache.ldap.server.SystemPartition;
24 import org.apache.ldap.server.interceptor.BaseInterceptor;
25 import org.apache.ldap.server.interceptor.InterceptorContext;
26 import org.apache.ldap.server.interceptor.NextInterceptor;
27 import org.apache.ldap.server.db.ResultFilteringEnumeration;
28 import org.apache.ldap.server.db.SearchResultFilter;
29 import org.apache.ldap.server.invocation.*;
30 import org.apache.ldap.server.jndi.ServerContext;
31 import org.apache.ldap.server.schema.AttributeTypeRegistry;
32 import org.apache.ldap.server.schema.ConcreteNameComponentNormalizer;
33
34 import javax.naming.Name JavaDoc;
35 import javax.naming.NamingEnumeration JavaDoc;
36 import javax.naming.NamingException JavaDoc;
37 import javax.naming.directory.Attributes JavaDoc;
38 import javax.naming.directory.SearchControls JavaDoc;
39 import javax.naming.directory.SearchResult JavaDoc;
40 import javax.naming.ldap.LdapContext JavaDoc;
41
42
43 /**
44  * An {@link org.apache.ldap.server.interceptor.Interceptor} that controls access to {@link BackingStore}
45  * operations. If a user tries to perform any operations that requires
46  * permission he or she doesn't have, {@link NamingException} will be
47  * thrown and therefore the current invocation chain will terminate.
48  *
49  * @author <a HREF="mailto:dev@directory.apache.org">Apache Directory Project</a>
50  * @version $Rev: 169198 $, $Date: 2005-05-08 20:05:59 -0400 (Sun, 08 May 2005) $
51  */

52 public class AuthorizationService extends BaseInterceptor
53 {
54     /**
55      * the administrator's distinguished {@link Name}
56      */

57     private static final Name JavaDoc ADMIN_DN = SystemPartition.getAdminDn();
58
59     /**
60      * the base distinguished {@link Name} for all users
61      */

62     private static final Name JavaDoc USER_BASE_DN = SystemPartition.getUsersBaseDn();
63
64     /**
65      * the base distinguished {@link Name} for all groups
66      */

67     private static final Name JavaDoc GROUP_BASE_DN = SystemPartition.getGroupsBaseDn();
68
69     /**
70      * the name parser used by this service
71      */

72     private DnParser dnParser;
73
74
75     /**
76      * Creates an authorization service interceptor.
77      */

78     public AuthorizationService()
79     {
80     }
81
82
83     public void init( InterceptorContext ctx ) throws NamingException JavaDoc
84     {
85         AttributeTypeRegistry atr = ctx.getGlobalRegistries().getAttributeTypeRegistry();
86         dnParser = new DnParser( new ConcreteNameComponentNormalizer( atr ) );
87     }
88
89
90     public void destroy()
91     {
92     }
93
94
95     public void process( NextInterceptor nextInterceptor, Invocation call ) throws NamingException JavaDoc
96     {
97         super.process( nextInterceptor, call );
98     }
99
100     // Note:
101
// Lookup, search and list operations need to be handled using a filter
102
// and so we need access to the filter service.
103

104     protected void process( NextInterceptor nextInterceptor, Delete call ) throws NamingException JavaDoc
105     {
106         Name JavaDoc name = call.getName();
107         Name JavaDoc principalDn = getPrincipal( call ).getDn();
108
109         if ( name.toString().equals( "" ) )
110         {
111             String JavaDoc msg = "The rootDSE cannot be deleted!";
112             throw new LdapNoPermissionException( msg );
113         }
114
115         if ( name == ADMIN_DN || name.equals( ADMIN_DN ) )
116         {
117             String JavaDoc msg = "User " + principalDn;
118             msg += " does not have permission to delete the admin account.";
119             msg += " No one not even the admin can delete this account!";
120             throw new LdapNoPermissionException( msg );
121         }
122
123         if ( name.size() > 2 && name.startsWith( USER_BASE_DN )
124                 && !principalDn.equals( ADMIN_DN ) )
125         {
126             String JavaDoc msg = "User " + principalDn;
127             msg += " does not have permission to delete the user account: ";
128             msg += name + ". Only the admin can delete user accounts.";
129             throw new LdapNoPermissionException( msg );
130         }
131
132         if ( name.size() > 2 && name.startsWith( GROUP_BASE_DN )
133                 && !principalDn.equals( ADMIN_DN ) )
134         {
135             String JavaDoc msg = "User " + principalDn;
136             msg += " does not have permission to delete the group entry: ";
137             msg += name + ". Only the admin can delete groups.";
138             throw new LdapNoPermissionException( msg );
139         }
140
141         nextInterceptor.process( call );
142     }
143
144
145     /**
146      * Note that we do nothing here. First because this is not an externally
147      * exposed function via the JNDI interfaces. It is used internally by
148      * the provider for optimization purposes so there is no reason for us to
149      * start to constrain it.
150      */

151     protected void process( NextInterceptor nextInterceptor, HasEntry call ) throws NamingException JavaDoc
152     {
153         super.process( nextInterceptor, call );
154     }
155
156
157     // ------------------------------------------------------------------------
158
// Entry Modification Operations
159
// ------------------------------------------------------------------------
160

161
162     /**
163      * This policy needs to be really tight too because some attributes may take
164      * part in giving the user permissions to protected resources. We do not want
165      * users to self access these resources. As far as we're concerned no one but
166      * the admin needs access.
167      */

168     protected void process( NextInterceptor nextInterceptor, Modify call ) throws NamingException JavaDoc
169     {
170         protectModifyAlterations( call, call.getName() );
171         nextInterceptor.process( call );
172     }
173
174
175     /**
176      * This policy needs to be really tight too because some attributes may take part
177      * in giving the user permissions to protected resources. We do not want users to
178      * self access these resources. As far as we're concerned no one but the admin
179      * needs access.
180      */

181     protected void process( NextInterceptor nextInterceptor, ModifyMany call ) throws NamingException JavaDoc
182     {
183         protectModifyAlterations( call, call.getName() );
184         nextInterceptor.process( call );
185     }
186
187
188     private void protectModifyAlterations( Invocation call, Name JavaDoc dn ) throws LdapNoPermissionException
189     {
190         Name JavaDoc principalDn = getPrincipal( call ).getDn();
191
192         if ( dn.toString().equals( "" ) )
193         {
194             String JavaDoc msg = "The rootDSE cannot be modified!";
195             throw new LdapNoPermissionException( msg );
196         }
197
198         if ( !principalDn.equals( ADMIN_DN ) )
199         {
200             if ( dn == ADMIN_DN || dn.equals( ADMIN_DN ) )
201             {
202                 String JavaDoc msg = "User " + principalDn;
203                 msg += " does not have permission to modify the admin account.";
204                 throw new LdapNoPermissionException( msg );
205             }
206
207             if ( dn.size() > 2 && dn.startsWith( USER_BASE_DN ) )
208             {
209                 String JavaDoc msg = "User " + principalDn;
210                 msg += " does not have permission to modify the account of the";
211                 msg += " user " + dn + ".\nEven the owner of an account cannot";
212                 msg += " modify it.\nUser accounts can only be modified by the";
213                 msg += " administrator.";
214                 throw new LdapNoPermissionException( msg );
215             }
216
217             if ( dn.size() > 2 && dn.startsWith( GROUP_BASE_DN ) )
218             {
219                 String JavaDoc msg = "User " + principalDn;
220                 msg += " does not have permission to modify the group entry ";
221                 msg += dn + ".\nGroups can only be modified by the admin.";
222                 throw new LdapNoPermissionException( msg );
223             }
224         }
225     }
226
227
228     // ------------------------------------------------------------------------
229
// DN altering operations are a no no for any user entry. Basically here
230
// are the rules of conduct to follow:
231
//
232
// o No user should have the ability to move or rename their entry
233
// o Only the administrator can move or rename non-admin user entries
234
// o The administrator entry cannot be moved or renamed by anyone
235
// ------------------------------------------------------------------------
236

237
238     protected void process( NextInterceptor nextInterceptor, ModifyRN call ) throws NamingException JavaDoc
239     {
240         protectDnAlterations( call, call.getName() );
241         nextInterceptor.process( call );
242     }
243
244
245     protected void process( NextInterceptor nextInterceptor, Move call ) throws NamingException JavaDoc
246     {
247         protectDnAlterations( call, call.getName() );
248         nextInterceptor.process( call );
249     }
250
251
252     protected void process( NextInterceptor nextInterceptor, MoveAndModifyRN call ) throws NamingException JavaDoc
253     {
254         protectDnAlterations( call, call.getName() );
255         nextInterceptor.process( call );
256     }
257
258
259     private void protectDnAlterations( Invocation call, Name JavaDoc dn ) throws LdapNoPermissionException
260     {
261         Name JavaDoc principalDn = getPrincipal( call ).getDn();
262
263         if ( dn.toString().equals( "" ) )
264         {
265             String JavaDoc msg = "The rootDSE cannot be moved or renamed!";
266             throw new LdapNoPermissionException( msg );
267         }
268
269         if ( dn == ADMIN_DN || dn.equals( ADMIN_DN ) )
270         {
271             String JavaDoc msg = "User '" + principalDn;
272             msg += "' does not have permission to move or rename the admin";
273             msg += " account. No one not even the admin can move or";
274             msg += " rename " + dn + "!";
275             throw new LdapNoPermissionException( msg );
276         }
277
278         if ( dn.size() > 2 && dn.startsWith( USER_BASE_DN ) && !principalDn.equals( ADMIN_DN ) )
279         {
280             String JavaDoc msg = "User '" + principalDn;
281             msg += "' does not have permission to move or rename the user";
282             msg += " account: " + dn + ". Only the admin can move or";
283             msg += " rename user accounts.";
284             throw new LdapNoPermissionException( msg );
285         }
286
287         if ( dn.size() > 2 && dn.startsWith( GROUP_BASE_DN ) && !principalDn.equals( ADMIN_DN ) )
288         {
289             String JavaDoc msg = "User " + principalDn;
290             msg += " does not have permission to move or rename the group entry ";
291             msg += dn + ".\nGroups can only be moved or renamed by the admin.";
292             throw new LdapNoPermissionException( msg );
293         }
294     }
295
296
297     protected void process( NextInterceptor nextInterceptor, Lookup call ) throws NamingException JavaDoc
298     {
299         super.process( nextInterceptor, call );
300
301         Attributes JavaDoc attributes = ( Attributes JavaDoc ) call.getReturnValue();
302         if ( attributes == null )
303         {
304             return;
305         }
306
307         Attributes JavaDoc retval = ( Attributes JavaDoc ) attributes.clone();
308         LdapContext JavaDoc ctx = ( LdapContext JavaDoc ) call.getContextStack().peek();
309         protectLookUp( ctx, call.getName() );
310         call.setReturnValue( retval );
311     }
312
313
314     protected void process( NextInterceptor nextInterceptor, LookupWithAttrIds call ) throws NamingException JavaDoc
315     {
316         super.process( nextInterceptor, call );
317
318         Attributes JavaDoc attributes = ( Attributes JavaDoc ) call.getReturnValue();
319         if ( attributes == null )
320         {
321             return;
322         }
323
324         Attributes JavaDoc retval = ( Attributes JavaDoc ) attributes.clone();
325         LdapContext JavaDoc ctx = ( LdapContext JavaDoc ) call.getContextStack().peek();
326         protectLookUp( ctx, call.getName() );
327         call.setReturnValue( retval );
328     }
329
330
331     private void protectLookUp( LdapContext JavaDoc ctx, Name JavaDoc dn ) throws NamingException JavaDoc
332     {
333         Name JavaDoc principalDn = ( ( ServerContext ) ctx ).getPrincipal().getDn();
334
335         if ( !principalDn.equals( ADMIN_DN ) )
336         {
337             if ( dn.size() > 2 && dn.startsWith( USER_BASE_DN ) )
338             {
339                 // allow for self reads
340
if ( dn.toString().equals( principalDn.toString() ) )
341                 {
342                     return;
343                 }
344
345                 String JavaDoc msg = "Access to user account '" + dn + "' not permitted";
346                 msg += " for user '" + principalDn + "'. Only the admin can";
347                 msg += " access user account information";
348                 throw new LdapNoPermissionException( msg );
349             }
350
351             if ( dn.size() > 2 && dn.startsWith( GROUP_BASE_DN ) )
352             {
353                 // allow for self reads
354
if ( dn.toString().equals( principalDn.toString() ) )
355                 {
356                     return;
357                 }
358
359                 String JavaDoc msg = "Access to group '" + dn + "' not permitted";
360                 msg += " for user '" + principalDn + "'. Only the admin can";
361                 msg += " access group information";
362                 throw new LdapNoPermissionException( msg );
363             }
364
365             if ( dn.equals( ADMIN_DN ) )
366             {
367                 // allow for self reads
368
if ( dn.toString().equals( principalDn.toString() ) )
369                 {
370                     return;
371                 }
372
373                 String JavaDoc msg = "Access to admin account not permitted for user '";
374                 msg += principalDn + "'. Only the admin can";
375                 msg += " access admin account information";
376                 throw new LdapNoPermissionException( msg );
377             }
378         }
379     }
380
381
382     protected void process( NextInterceptor nextInterceptor, Search call ) throws NamingException JavaDoc
383     {
384         super.process( nextInterceptor, call );
385
386         SearchControls JavaDoc searchControls = call.getControls();
387         if ( searchControls.getReturningAttributes() != null )
388         {
389             return;
390         }
391
392         NamingEnumeration JavaDoc e;
393         ResultFilteringEnumeration retval;
394         LdapContext JavaDoc ctx = ( LdapContext JavaDoc ) call.getContextStack().peek();
395         e = ( NamingEnumeration JavaDoc ) call.getReturnValue();
396         retval = new ResultFilteringEnumeration( e, searchControls, ctx,
397                 new SearchResultFilter()
398                 {
399                     public boolean accept( LdapContext JavaDoc ctx, SearchResult JavaDoc result,
400                                            SearchControls JavaDoc controls )
401                             throws NamingException JavaDoc
402                     {
403                         return AuthorizationService.this.isSearchable( ctx, result );
404                     }
405                 } );
406
407         call.setReturnValue( retval );
408     }
409
410
411     protected void process( NextInterceptor nextInterceptor, List call ) throws NamingException JavaDoc
412     {
413         super.process( nextInterceptor, call );
414
415         NamingEnumeration JavaDoc e;
416         ResultFilteringEnumeration retval;
417         LdapContext JavaDoc ctx = ( LdapContext JavaDoc ) call.getContextStack().peek();
418         e = ( NamingEnumeration JavaDoc ) call.getReturnValue();
419         retval = new ResultFilteringEnumeration( e, null, ctx,
420             new SearchResultFilter()
421             {
422                 public boolean accept( LdapContext JavaDoc ctx, SearchResult JavaDoc result,
423                                        SearchControls JavaDoc controls )
424                         throws NamingException JavaDoc
425                 {
426                     return AuthorizationService.this.isSearchable( ctx, result );
427                 }
428             } );
429
430         call.setReturnValue( retval );
431     }
432
433
434     private boolean isSearchable( LdapContext JavaDoc ctx, SearchResult JavaDoc result )
435             throws NamingException JavaDoc
436     {
437         Name JavaDoc dn;
438
439         synchronized ( dnParser )
440         {
441             dn = dnParser.parse( result.getName() );
442         }
443
444         Name JavaDoc principalDn = ( ( ServerContext ) ctx ).getPrincipal().getDn();
445         if ( !principalDn.equals( ADMIN_DN ) )
446         {
447             if ( dn.size() > 2 )
448             {
449                 if ( dn.startsWith( USER_BASE_DN ) || dn.startsWith( GROUP_BASE_DN ) )
450                 {
451                     return false;
452                 }
453             }
454
455             if ( dn.equals( ADMIN_DN ) )
456             {
457                 return false;
458             }
459
460         }
461
462         return true;
463     }
464 }
465
Popular Tags