View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *  
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *  
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License. 
18   *  
19   */
20  package org.apache.directory.server.core.authz;
21  
22  
23  import org.apache.directory.server.core.CoreSession;
24  import org.apache.directory.server.core.entry.ServerAttribute;
25  import org.apache.directory.server.core.entry.ServerEntry;
26  import org.apache.directory.server.core.filtering.EntryFilteringCursor;
27  import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
28  import org.apache.directory.server.core.partition.PartitionNexus;
29  import org.apache.directory.server.schema.ConcreteNameComponentNormalizer;
30  import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
31  import org.apache.directory.server.schema.registries.OidRegistry;
32  import org.apache.directory.shared.ldap.aci.ACIItem;
33  import org.apache.directory.shared.ldap.aci.ACIItemParser;
34  import org.apache.directory.shared.ldap.aci.ACITuple;
35  import org.apache.directory.shared.ldap.constants.SchemaConstants;
36  import org.apache.directory.shared.ldap.entry.EntryAttribute;
37  import org.apache.directory.shared.ldap.entry.Modification;
38  import org.apache.directory.shared.ldap.entry.Value;
39  import org.apache.directory.shared.ldap.entry.client.ClientStringValue;
40  import org.apache.directory.shared.ldap.exception.LdapSchemaViolationException;
41  import org.apache.directory.shared.ldap.filter.EqualityNode;
42  import org.apache.directory.shared.ldap.filter.ExprNode;
43  import org.apache.directory.shared.ldap.message.AliasDerefMode;
44  import org.apache.directory.shared.ldap.message.ResultCodeEnum;
45  import org.apache.directory.shared.ldap.name.LdapDN;
46  import org.apache.directory.shared.ldap.name.NameComponentNormalizer;
47  import org.apache.directory.shared.ldap.schema.AttributeType;
48  import org.apache.directory.shared.ldap.schema.OidNormalizer;
49  import org.slf4j.Logger;
50  import org.slf4j.LoggerFactory;
51  
52  import javax.naming.NamingException;
53  import javax.naming.directory.SearchControls;
54  import java.text.ParseException;
55  import java.util.ArrayList;
56  import java.util.Collections;
57  import java.util.HashMap;
58  import java.util.Iterator;
59  import java.util.List;
60  import java.util.Map;
61  
62  
63  /**
64   * A cache for tuple sets which responds to specific events to perform
65   * cache house keeping as access control subentries are added, deleted
66   * and modified.
67   *
68   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
69   * @version $Rev: 662440 $
70   */
71  public class TupleCache
72  {
73      /** the logger for this class */
74      private static final Logger LOG = LoggerFactory.getLogger( TupleCache.class );
75  
76      /** a map of strings to ACITuple collections */
77      private final Map<String, List<ACITuple>> tuples = new HashMap<String, List<ACITuple>>();
78  
79      /** a handle on the partition nexus */
80      private final PartitionNexus nexus;
81  
82      /** a normalizing ACIItem parser */
83      private final ACIItemParser aciParser;
84  
85      /** A starage for the PrescriptiveACI attributeType */
86      private AttributeType prescriptiveAciAT;
87  
88      /**
89       * The OIDs normalizer map
90       */
91      private Map<String, OidNormalizer> normalizerMap;
92  
93  
94      /**
95       * Creates a ACITuple cache.
96       *
97       * @param directoryService the context factory configuration for the server
98       * @throws NamingException if initialization fails
99       */
100     public TupleCache( CoreSession session ) throws Exception
101     {
102         normalizerMap = session.getDirectoryService().getRegistries()
103             .getAttributeTypeRegistry().getNormalizerMapping();
104         this.nexus = session.getDirectoryService().getPartitionNexus();
105         AttributeTypeRegistry attributeTypeRegistry = session.getDirectoryService()
106             .getRegistries().getAttributeTypeRegistry();
107         OidRegistry oidRegistry = session.getDirectoryService().getRegistries().getOidRegistry();
108         NameComponentNormalizer ncn = new ConcreteNameComponentNormalizer( attributeTypeRegistry, oidRegistry );
109         aciParser = new ACIItemParser( ncn, normalizerMap );
110         prescriptiveAciAT = attributeTypeRegistry.lookup( SchemaConstants.PRESCRIPTIVE_ACI_AT );
111         initialize( session );
112     }
113 
114 
115     private LdapDN parseNormalized( String name ) throws NamingException
116     {
117         LdapDN dn = new LdapDN( name );
118         dn.normalize( normalizerMap );
119         return dn;
120     }
121 
122 
123     private void initialize( CoreSession session ) throws Exception
124     {
125         // search all naming contexts for access control subentenries
126         // generate ACITuple Arrays for each subentry
127         // add that subentry to the hash
128         Iterator<String> suffixes = nexus.listSuffixes( null );
129 
130         while ( suffixes.hasNext() )
131         {
132             String suffix = suffixes.next();
133             LdapDN baseDn = parseNormalized( suffix );
134             ExprNode filter = new EqualityNode<String>( SchemaConstants.OBJECT_CLASS_AT, 
135                 new ClientStringValue( SchemaConstants.ACCESS_CONTROL_SUBENTRY_OC ) );
136             SearchControls ctls = new SearchControls();
137             ctls.setSearchScope( SearchControls.SUBTREE_SCOPE );
138             EntryFilteringCursor results = nexus.search( new SearchOperationContext( session,
139                 baseDn, AliasDerefMode.NEVER_DEREF_ALIASES, filter, ctls ) );
140 
141             while ( results.next() )
142             {
143                 ServerEntry result = results.get();
144                 LdapDN subentryDn = result.getDn().normalize( normalizerMap );
145                 EntryAttribute aci = result.get( prescriptiveAciAT );
146 
147                 if ( aci == null )
148                 {
149                     LOG.warn( "Found accessControlSubentry '" + subentryDn + "' without any "
150                         + SchemaConstants.PRESCRIPTIVE_ACI_AT );
151                     continue;
152                 }
153 
154                 subentryAdded( subentryDn, result );
155             }
156 
157             results.close();
158         }
159     }
160 
161 
162     private boolean hasPrescriptiveACI( ServerEntry entry ) throws NamingException
163     {
164         // only do something if the entry contains prescriptiveACI
165         EntryAttribute aci = entry.get( prescriptiveAciAT );
166 
167         if ( aci == null )
168         {
169             if ( entry.contains( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.ACCESS_CONTROL_SUBENTRY_OC )
170                 || entry.contains( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.ACCESS_CONTROL_SUBENTRY_OC_OID ) )
171             {
172                 // should not be necessary because of schema interceptor but schema checking
173                 // can be turned off and in this case we must protect against being able to
174                 // add access control information to anything other than an AC subentry
175                 throw new LdapSchemaViolationException( "", ResultCodeEnum.OBJECT_CLASS_VIOLATION );
176             }
177             else
178             {
179                 return false;
180             }
181         }
182 
183         return true;
184     }
185 
186 
187     public void subentryAdded( LdapDN normName, ServerEntry entry ) throws NamingException
188     {
189         // only do something if the entry contains prescriptiveACI
190         EntryAttribute aciAttr = entry.get( prescriptiveAciAT );
191 
192         if ( !hasPrescriptiveACI( entry ) )
193         {
194             return;
195         }
196 
197         List<ACITuple> entryTuples = new ArrayList<ACITuple>();
198 
199         for ( Value<?> value : aciAttr )
200         {
201             String aci = ( String ) value.get();
202             ACIItem item = null;
203 
204             try
205             {
206                 item = aciParser.parse( aci );
207                 entryTuples.addAll( item.toTuples() );
208             }
209             catch ( ParseException e )
210             {
211                 String msg = "ACIItem parser failure on \n'" + item + "'\ndue to syntax error. "
212                     + "Cannnot add ACITuples to TupleCache.\n"
213                     + "Check that the syntax of the ACI item is correct. \nUntil this error "
214                     + "is fixed your security settings will not be as expected.";
215                 LOG.error( msg, e );
216 
217                 // do not process this ACI Item because it will be null
218                 // continue on to process the next ACI item in the entry
219             }
220         }
221 
222         tuples.put( normName.toNormName(), entryTuples );
223     }
224 
225 
226     public void subentryDeleted( LdapDN normName, ServerEntry entry ) throws NamingException
227     {
228         if ( !hasPrescriptiveACI( entry ) )
229         {
230             return;
231         }
232 
233         tuples.remove( normName.toString() );
234     }
235 
236 
237     public void subentryModified( LdapDN normName, List<Modification> mods, ServerEntry entry ) throws NamingException
238     {
239         if ( !hasPrescriptiveACI( entry ) )
240         {
241             return;
242         }
243 
244         for ( Modification mod : mods )
245         {
246             if ( ( ( ServerAttribute ) mod.getAttribute() ).instanceOf( SchemaConstants.PRESCRIPTIVE_ACI_AT ) )
247             {
248                 subentryDeleted( normName, entry );
249                 subentryAdded( normName, entry );
250             }
251         }
252     }
253 
254 
255     public void subentryModified( LdapDN normName, ServerEntry mods, ServerEntry entry ) throws NamingException
256     {
257         if ( !hasPrescriptiveACI( entry ) )
258         {
259             return;
260         }
261 
262         if ( mods.get( prescriptiveAciAT ) != null )
263         {
264             subentryDeleted( normName, entry );
265             subentryAdded( normName, entry );
266         }
267     }
268 
269 
270     @SuppressWarnings("unchecked")
271     public List<ACITuple> getACITuples( String subentryDn )
272     {
273         List aciTuples = tuples.get( subentryDn );
274         if ( aciTuples == null )
275         {
276             return Collections.EMPTY_LIST;
277         }
278         return Collections.unmodifiableList( aciTuples );
279     }
280 
281 
282     public void subentryRenamed( LdapDN oldName, LdapDN newName )
283     {
284         tuples.put( newName.toString(), tuples.remove( oldName.toString() ) );
285     }
286 }