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 java.text.ParseException;
21  import java.util.*;
22  
23  import javax.naming.Name;
24  import javax.naming.NamingEnumeration;
25  import javax.naming.NamingException;
26  import javax.naming.directory.Attribute;
27  import javax.naming.directory.Attributes;
28  import javax.naming.directory.ModificationItem;
29  import javax.naming.directory.SearchControls;
30  import javax.naming.directory.SearchResult;
31  
32  import org.apache.ldap.common.aci.ACIItem;
33  import org.apache.ldap.common.aci.ACIItemParser;
34  import org.apache.ldap.common.exception.LdapInvalidAttributeValueException;
35  import org.apache.ldap.common.exception.LdapSchemaViolationException;
36  import org.apache.ldap.common.filter.ExprNode;
37  import org.apache.ldap.common.filter.SimpleNode;
38  import org.apache.ldap.common.message.ResultCodeEnum;
39  import org.apache.ldap.common.name.LdapName;
40  import org.apache.ldap.server.DirectoryServiceConfiguration;
41  import org.apache.ldap.server.partition.DirectoryPartitionNexus;
42  import org.apache.ldap.server.schema.ConcreteNameComponentNormalizer;
43  import org.slf4j.Logger;
44  import org.slf4j.LoggerFactory;
45  
46  
47  /***
48   * A cache for tuple sets which responds to specific events to perform
49   * cache house keeping as access control subentries are added, deleted
50   * and modified.
51   *
52   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
53   * @version $Rev$
54   */
55  public class TupleCache
56  {
57      /*** the attribute id for prescriptive aci: prescriptiveACI */
58      private static final String ACI_ATTR = "prescriptiveACI";
59      /*** the attribute id for an object class: objectClass */
60      private static final String OC_ATTR = "objectClass";
61      /*** the object class for access control subentries: accessControlSubentry */
62      private static final String ACSUBENTRY_OC = "accessControlSubentry";
63  
64      /*** the logger for this class */
65      private static final Logger log = LoggerFactory.getLogger( TupleCache.class );
66  
67      /*** cloned startup environment properties we use for subentry searching */
68      private final Hashtable env;
69      /*** a map of strings to ACITuple collections */
70      private final Map tuples = new HashMap();
71      /*** a handle on the partition nexus */
72      private final DirectoryPartitionNexus nexus;
73      /*** a normalizing ACIItem parser */
74      private final ACIItemParser aciParser;
75  
76  
77      /***
78       * Creates a ACITuple cache.
79       *
80       * @param factoryCfg the context factory configuration for the server
81       */
82      public TupleCache( DirectoryServiceConfiguration factoryCfg ) throws NamingException
83      {
84          this.nexus = factoryCfg.getPartitionNexus();
85          aciParser = new ACIItemParser( new ConcreteNameComponentNormalizer(
86                  factoryCfg.getGlobalRegistries().getAttributeTypeRegistry() ) );
87          env = ( Hashtable ) factoryCfg.getEnvironment().clone();
88          initialize();
89      }
90  
91  
92      private void initialize() throws NamingException
93      {
94          // search all naming contexts for access control subentenries
95          // generate ACITuple Arrays for each subentry
96          // add that subentry to the hash
97          Iterator suffixes = nexus.listSuffixes( true );
98          while ( suffixes.hasNext() )
99          {
100             String suffix = ( String ) suffixes.next();
101             Name baseDn = new LdapName( suffix );
102             ExprNode filter = new SimpleNode( OC_ATTR, ACSUBENTRY_OC, SimpleNode.EQUALITY );
103             SearchControls ctls = new SearchControls();
104             ctls.setSearchScope( SearchControls.SUBTREE_SCOPE );
105             NamingEnumeration results = nexus.search( baseDn, env, filter, ctls );
106             while ( results.hasMore() )
107             {
108                 SearchResult result = ( SearchResult ) results.next();
109                 String subentryDn = result.getName();
110                 Attribute aci = result.getAttributes().get( ACI_ATTR );
111                 if ( aci == null )
112                 {
113                     log.warn( "Found accessControlSubentry '" + subentryDn + "' without any " + ACI_ATTR );
114                     continue;
115                 }
116 
117                 subentryAdded( subentryDn, new LdapName( subentryDn ), result.getAttributes() );
118             }
119             results.close();
120         }
121     }
122 
123 
124     private boolean hasPrescriptiveACI( Attributes entry ) throws NamingException
125     {
126         // only do something if the entry contains prescriptiveACI
127         Attribute aci = entry.get( ACI_ATTR );
128         if ( aci == null && entry.get( OC_ATTR ).contains( ACSUBENTRY_OC ) )
129         {
130             // should not be necessary because of schema interceptor but schema checking
131             // can be turned off and in this case we must protect against being able to
132             // add access control information to anything other than an AC subentry
133             throw new LdapSchemaViolationException( "", ResultCodeEnum.OBJECTCLASSVIOLATION );
134         }
135         else if ( aci == null )
136         {
137             return false;
138         }
139         return true;
140     }
141 
142 
143     public void subentryAdded( String upName, Name normName, Attributes entry ) throws NamingException
144     {
145         // only do something if the entry contains prescriptiveACI
146         Attribute aci = entry.get( ACI_ATTR );
147         if ( ! hasPrescriptiveACI( entry ) )
148         {
149             return;
150         }
151 
152         List entryTuples = new ArrayList();
153         for ( int ii = 0; ii < aci.size(); ii++ )
154         {
155             ACIItem item = null;
156 
157             try
158             {
159                 item = aciParser.parse( ( String ) aci.get( ii ) );
160             }
161             catch ( ParseException e )
162             {
163                 String msg = "ACIItem parser failure on '"+item+"'. Cannnot add ACITuples to TupleCache.";
164                 log.warn( msg, e );
165             }
166 
167             entryTuples.addAll( item.toTuples() );
168         }
169         tuples.put( normName.toString(), entryTuples );
170     }
171 
172 
173     public void subentryDeleted( Name normName, Attributes entry ) throws NamingException
174     {                                                                                      
175         if ( ! hasPrescriptiveACI( entry ) )
176         {
177             return;
178         }
179 
180         tuples.remove( normName.toString() );
181     }
182 
183 
184     public void subentryModified( Name normName, ModificationItem[] mods, Attributes entry ) throws NamingException
185     {
186         if ( ! hasPrescriptiveACI( entry ) )
187         {
188             return;
189         }
190 
191         boolean isAciModified = false;
192         for ( int ii = 0; ii < mods.length; ii++ )
193         {
194             isAciModified |= mods[ii].getAttribute().contains( ACI_ATTR );
195         }
196         if ( isAciModified )
197         {
198             subentryDeleted( normName, entry );
199             subentryAdded( normName.toString(), normName, entry );
200         }
201     }
202 
203 
204     public void subentryModified( Name normName, int modOp, Attributes mods, Attributes entry ) throws NamingException
205     {
206         if ( ! hasPrescriptiveACI( entry ) )
207         {
208             return;
209         }
210 
211         if ( mods.get( ACI_ATTR ) != null )
212         {
213             subentryDeleted( normName, entry );
214             subentryAdded( normName.toString(), normName, entry );
215         }
216     }                                                     
217 
218 
219     public List getACITuples( String subentryDn )
220     {
221         List aciTuples = ( List ) tuples.get( subentryDn );
222         if ( aciTuples == null )
223         {
224             return Collections.EMPTY_LIST;
225         }
226         return Collections.unmodifiableList( aciTuples );
227     }
228 
229 
230     public void subentryRenamed( Name oldName, Name newName )
231     {
232         tuples.put( newName.toString(), tuples.remove( oldName.toString() ) );
233     }
234 }