View Javadoc

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.name.DnParser;
21  import org.apache.ldap.common.name.LdapName;
22  import org.apache.ldap.common.filter.SimpleNode;
23  import org.apache.ldap.common.filter.BranchNode;
24  import org.apache.ldap.server.DirectoryServiceConfiguration;
25  import org.apache.ldap.server.schema.ConcreteNameComponentNormalizer;
26  import org.apache.ldap.server.partition.DirectoryPartitionNexus;
27  import org.slf4j.Logger;
28  import org.slf4j.LoggerFactory;
29  
30  import javax.naming.Name;
31  import javax.naming.NamingException;
32  import javax.naming.NamingEnumeration;
33  import javax.naming.directory.*;
34  import java.util.*;
35  
36  
37  /***
38   * A cache for tracking static group membership.
39   *
40   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
41   * @version $Rev$
42   */
43  public class GroupCache
44  {
45      /*** the attribute id for an object class: objectClass */
46      private static final String OC_ATTR = "objectClass";
47      /*** the member attribute for a groupOfNames: member */
48      private static final String MEMBER_ATTR = "member";
49      /*** the member attribute for a groupOfUniqueNames: uniqueMember */
50      private static final String UNIQUEMEMBER_ATTR = "uniqueMember";
51      /*** the groupOfNames objectClass: groupOfNames */
52      private static final String GROUPOFNAMES_OC = "groupOfNames";
53      /*** the groupOfUniqueNames objectClass: groupOfUniqueNames */
54      private static final String GROUPOFUNIQUENAMES_OC = "groupOfUniqueNames";
55      /*** the logger for this class */
56      private static final Logger log = LoggerFactory.getLogger( GroupCache.class );
57  
58      /*** String key for the DN of a group to a Set (HashSet) for the Strings of member DNs */
59      private final Map groups = new HashMap();
60      /*** a handle on the partition nexus */
61      private final DirectoryPartitionNexus nexus;
62      /*** the env to use for searching */
63      private final Hashtable env;
64      /*** the normalizing Dn parser for member names */
65      private DnParser parser;
66  
67  
68      /***
69       * Creates a static group cache.
70       *
71       * @param factoryCfg the context factory configuration for the server
72       */
73      public GroupCache( DirectoryServiceConfiguration factoryCfg ) throws NamingException
74      {
75          this.nexus = factoryCfg.getPartitionNexus();
76          this.env = ( Hashtable ) factoryCfg.getEnvironment().clone();
77          this.parser = new DnParser( new ConcreteNameComponentNormalizer(
78                  factoryCfg.getGlobalRegistries().getAttributeTypeRegistry() ) );
79          initialize();
80      }
81  
82  
83      private void initialize() throws NamingException
84      {
85          // search all naming contexts for static groups and generate
86          // normalized sets of members to cache within the map
87  
88          BranchNode filter = new BranchNode( BranchNode.OR );
89          filter.addNode( new SimpleNode( OC_ATTR, GROUPOFNAMES_OC, SimpleNode.EQUALITY ) );
90          filter.addNode( new SimpleNode( OC_ATTR, GROUPOFUNIQUENAMES_OC, SimpleNode.EQUALITY ) );
91  
92          Iterator suffixes = nexus.listSuffixes( true );
93          while ( suffixes.hasNext() )
94          {
95              String suffix = ( String ) suffixes.next();
96              Name baseDn = new LdapName( suffix );
97              SearchControls ctls = new SearchControls();
98              ctls.setSearchScope( SearchControls.SUBTREE_SCOPE );
99              NamingEnumeration results = nexus.search( baseDn, env, filter, ctls );
100 
101             while ( results.hasMore() )
102             {
103                 SearchResult result = ( SearchResult ) results.next();
104                 String groupDn = result.getName();
105                 groupDn = parser.parse( groupDn ).toString();
106                 Attribute members = getMemberAttribute( result.getAttributes() );
107 
108                 if ( members != null )
109                 {
110                     Set memberSet = new HashSet( members.size() );
111                     addMembers( memberSet, members );
112                     groups.put( groupDn, memberSet );
113                 }
114                 else
115                 {
116                     log.warn( "Found group '" + groupDn + "' without any member or uniqueMember attributes" );
117                 }
118             }
119             results.close();
120         }
121     }
122 
123 
124     /***
125      * Gets the member attribute regardless of whether groupOfNames or
126      * groupOfUniqueNames is used.
127      *
128      * @param entry the entry inspected for member attributes
129      * @return the member attribute
130      */
131     private Attribute getMemberAttribute( Attributes entry )
132     {
133         Attribute oc = entry.get( OC_ATTR );
134 
135         if ( oc == null )
136         {
137             if ( entry.get( MEMBER_ATTR ) != null )
138             {
139                 return entry.get( MEMBER_ATTR );
140             }
141 
142             if ( entry.get( UNIQUEMEMBER_ATTR ) != null )
143             {
144                 return entry.get( UNIQUEMEMBER_ATTR );
145             }
146 
147             return null;
148         }
149 
150         if ( oc.contains( GROUPOFNAMES_OC ) )
151         {
152             return entry.get( MEMBER_ATTR );
153         }
154 
155         if ( oc.contains( GROUPOFUNIQUENAMES_OC ) )
156         {
157             return entry.get( UNIQUEMEMBER_ATTR );
158         }
159 
160         return null;
161     }
162 
163 
164     /***
165      * Adds normalized member DNs to the set of normalized member names.
166      *
167      * @param memberSet the set of member Dns (Strings)
168      * @param members the member attribute values being added
169      * @throws NamingException if there are problems accessing the attr values
170      */
171     private void addMembers( Set memberSet, Attribute members ) throws NamingException
172     {
173         for ( int ii = 0; ii < members.size(); ii++ )
174         {
175             // get and normalize the DN of the member
176             String memberDn = ( String ) members.get( ii );
177 
178             try
179             {
180                 memberDn = parser.parse( memberDn ).toString();
181             }
182             catch ( NamingException e )
183             {
184                 log.warn( "Malformed member DN in groupOf[Unique]Names entry.  Member not added to GroupCache.", e );
185             }
186 
187             memberSet.add( memberDn );
188         }
189     }
190 
191 
192     /***
193      * Removes a set of member names from an existing set.
194      *
195      * @param memberSet the set of normalized member DNs
196      * @param members the set of member values
197      * @throws NamingException if there are problems accessing the attr values
198      */
199     private void removeMembers( Set memberSet, Attribute members ) throws NamingException
200     {
201         for ( int ii = 0; ii < members.size(); ii++ )
202         {
203             // get and normalize the DN of the member
204             String memberDn = ( String ) members.get( ii );
205 
206             try
207             {
208                 memberDn = parser.parse( memberDn ).toString();
209             }
210             catch ( NamingException e )
211             {
212                 log.warn( "Malformed member DN in groupOf[Unique]Names entry.  Member not removed from GroupCache.", e );
213             }
214 
215             memberSet.remove( memberDn );
216         }
217     }
218 
219 
220     /***
221      * Adds a groups members to the cache.  Called by interceptor to account for new
222      * group additions.
223      *
224      * @param upName the user provided name for the group entry
225      * @param normName the normalized name for the group entry
226      * @param entry the group entry's attributes
227      * @throws NamingException if there are problems accessing the attr values
228      */
229     public void groupAdded( String upName, Name normName, Attributes entry ) throws NamingException
230     {
231         Attribute members = getMemberAttribute( entry );
232 
233         if ( members == null )
234         {
235             return;
236         }
237 
238         Set memberSet = new HashSet( members.size() );
239         addMembers( memberSet, members );
240         groups.put( normName.toString(), memberSet );
241     }
242 
243 
244     /***
245      * Deletes a group's members from the cache.  Called by interceptor to account for
246      * the deletion of groups.
247      *
248      * @param name the normalized DN of the group entry
249      * @param entry the attributes of entry being deleted
250      */
251     public void groupDeleted( Name name, Attributes entry )
252     {
253         Attribute members = getMemberAttribute( entry );
254 
255         if ( members == null )
256         {
257             return;
258         }
259 
260         groups.remove( name.toString() );
261     }
262 
263 
264     /***
265      * Utility method to modify a set of member names based on a modify operation
266      * that changes the members of a group.
267      *
268      * @param memberSet the set of members to be altered
269      * @param modOp the type of modify operation being performed
270      * @param members the members being added, removed or replaced
271      * @throws NamingException if there are problems accessing attribute values
272      */
273     private void modify( Set memberSet, int modOp, Attribute members ) throws NamingException
274     {
275 
276         switch ( modOp )
277         {
278             case ( DirContext.ADD_ATTRIBUTE ):
279                 addMembers( memberSet, members );
280                 break;
281             case ( DirContext.REPLACE_ATTRIBUTE ):
282                 if ( members.size() > 0 )
283                 {
284                     memberSet.clear();
285                     addMembers( memberSet, members );
286                 }
287                 break;
288             case ( DirContext.REMOVE_ATTRIBUTE ):
289                 removeMembers( memberSet, members );
290                 break;
291             default:
292                 throw new InternalError( "Undefined modify operation value of " + modOp );
293         }
294     }
295 
296 
297     /***
298      * Modifies the cache to reflect changes via modify operations to the group entries.
299      * Called by the interceptor to account for modify ops on groups.
300      *
301      * @param name the normalized name of the group entry modified
302      * @param mods the modification operations being performed
303      * @param entry the group entry being modified
304      * @throws NamingException if there are problems accessing attribute  values
305      */
306     public void groupModified( Name name, ModificationItem[] mods, Attributes entry ) throws NamingException
307     {
308         Attribute members = null;
309         String memberAttrId = null;
310         Attribute oc = entry.get( OC_ATTR );
311 
312         if ( oc.contains( GROUPOFNAMES_OC ) )
313         {
314             members = entry.get( MEMBER_ATTR );
315             memberAttrId = MEMBER_ATTR;
316         }
317 
318         if ( oc.contains( GROUPOFUNIQUENAMES_OC ) )
319         {
320             members = entry.get( UNIQUEMEMBER_ATTR );
321             memberAttrId = UNIQUEMEMBER_ATTR;
322         }
323 
324         if ( members == null )
325         {
326             return;
327         }
328 
329         for ( int ii = 0; ii < mods.length; ii++ )
330         {
331             if ( memberAttrId.equalsIgnoreCase( mods[ii].getAttribute().getID() ) )
332             {
333                 Set memberSet = ( Set ) groups.get( name.toString() );
334                 if ( memberSet != null )
335                 {
336                     modify( memberSet, mods[ii].getModificationOp(), members );
337                 }
338                 break;
339             }
340         }
341     }
342 
343 
344     /***
345      * Modifies the cache to reflect changes via modify operations to the group entries.
346      * Called by the interceptor to account for modify ops on groups.
347      *
348      * @param name the normalized name of the group entry modified
349      * @param modOp the modify operation being performed
350      * @param mods the modifications being performed
351      * @param entry the entry being modified
352      * @throws NamingException if there are problems accessing attribute  values
353      */
354     public void groupModified( Name name, int modOp, Attributes mods, Attributes entry ) throws NamingException
355     {
356         Attribute members = getMemberAttribute( mods );
357 
358         if ( members == null )
359         {
360             return;
361         }
362 
363         Set memberSet = ( Set ) groups.get( name.toString() );
364         if ( memberSet != null )
365         {
366             modify( memberSet, modOp, members );
367         }
368     }
369 
370 
371     /***
372      * Gets the set of groups a user is a member of.  The groups are returned
373      * as normalized Name objects within the set.
374      *
375      * @param member the member (user) to get the groups for
376      * @return a Set of Name objects representing the groups
377      * @throws NamingException if there are problems accessing attribute  values
378      */
379     public Set getGroups( String member ) throws NamingException
380     {
381         try
382         {
383             member = parser.parse( member ).toString();
384         }
385         catch ( NamingException e )
386         {
387             log.warn( "Malformed member DN.  Could not find groups for member in GroupCache. Returning empty set for groups!", e );
388             return Collections.EMPTY_SET;
389         }
390 
391         Set memberGroups = null;
392 
393         Iterator list = groups.keySet().iterator();
394         while ( list.hasNext() )
395         {
396             String group = ( String ) list.next();
397             Set members = ( Set ) groups.get( group );
398 
399             if ( members == null )
400             {
401                 continue;
402             }
403 
404             if ( members.contains( member ) )
405             {
406                 if ( memberGroups == null )
407                 {
408                     memberGroups = new HashSet();
409                 }
410 
411                 memberGroups.add( new LdapName( group ) );
412             }
413         }
414 
415         if ( memberGroups == null )
416         {
417             return Collections.EMPTY_SET;
418         }
419 
420         return memberGroups;
421     }
422 
423 
424     public void groupRenamed( Name oldName, Name newName )
425     {
426         groups.put( newName.toString(), groups.remove( oldName.toString() ) );
427     }
428 }