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.support;
21  
22  
23  import java.util.ArrayList;
24  import java.util.Collection;
25  
26  import org.apache.directory.api.ldap.aci.ACITuple;
27  import org.apache.directory.api.ldap.model.constants.SchemaConstants;
28  import org.apache.directory.api.ldap.model.entry.Entry;
29  import org.apache.directory.api.ldap.model.exception.LdapException;
30  import org.apache.directory.api.ldap.model.exception.LdapNoPermissionException;
31  import org.apache.directory.api.ldap.model.schema.SchemaManager;
32  import org.apache.directory.server.core.api.CoreSession;
33  import org.apache.directory.server.core.api.event.Evaluator;
34  import org.apache.directory.server.core.api.event.ExpressionEvaluator;
35  import org.apache.directory.server.core.api.interceptor.context.LookupOperationContext;
36  import org.apache.directory.server.core.api.subtree.SubtreeEvaluator;
37  import org.apache.directory.server.core.api.subtree.RefinementEvaluator;
38  import org.apache.directory.server.core.api.subtree.RefinementLeafEvaluator;
39  
40  
41  /**
42   * An implementation of Access Control Decision Function (18.8, X.501).
43   * <br>
44   * This engine simply filters the collection of tuples using the following
45   * {@link ACITupleFilter}s sequentially:
46   * <ol>
47   * <li>{@link RelatedUserClassFilter}</li>
48   * <li>{@link RelatedProtectedItemFilter}</li>
49   * <li>{@link MaxValueCountFilter}</li>
50   * <li>{@link MaxImmSubFilter}</li>
51   * <li>{@link RestrictedByFilter}</li>
52   * <li>{@link MicroOperationFilter}</li>
53   * <li>{@link HighestPrecedenceFilter}</li>
54   * <li>{@link MostSpecificUserClassFilter}</li>
55   * <li>{@link MostSpecificProtectedItemFilter}</li>
56   * </ol>
57   * <br>
58   * Operation is determined to be permitted if and only if there is at least one
59   * tuple left and all of them grants the access. (18.8.4. X.501)
60   *
61   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
62   */
63  public class ACDFEngine
64  {
65      private final ACITupleFilter[] filters;
66  
67  
68      /**
69       * Creates a new instance.
70       *
71       * @param schemaManager The server schemaManager
72       */
73      public ACDFEngine( SchemaManager schemaManager )
74      {
75          Evaluator entryEvaluator = new ExpressionEvaluator( schemaManager );
76          SubtreeEvaluatorbtree/SubtreeEvaluator.html#SubtreeEvaluator">SubtreeEvaluator subtreeEvaluator = new SubtreeEvaluator( schemaManager );
77          RefinementEvaluatorRefinementEvaluator.html#RefinementEvaluator">RefinementEvaluator refinementEvaluator = new RefinementEvaluator( new RefinementLeafEvaluator( schemaManager ) );
78  
79          filters = new ACITupleFilter[]
80              {
81                  new RelatedUserClassFilter( subtreeEvaluator ),
82                  new RelatedProtectedItemFilter( refinementEvaluator, entryEvaluator, schemaManager ),
83                  new MaxValueCountFilter(),
84                  new MaxImmSubFilter( schemaManager ),
85                  new RestrictedByFilter(),
86                  new MicroOperationFilter(),
87                  new HighestPrecedenceFilter(),
88                  new MostSpecificUserClassFilter(),
89                  new MostSpecificProtectedItemFilter() };
90      }
91  
92  
93      /**
94       * Checks the user with the specified name can access the specified resource
95       * (entry, attribute type, or attribute value) and throws {@link LdapNoPermissionException}
96       * if the user doesn't have any permission to perform the specified grants.
97       *
98       * @param aciContext the container for ACI items
99       * @throws LdapException if failed to evaluate ACI items
100      */
101     public void checkPermission( AciContext aciContext ) throws LdapException
102     {
103         if ( !hasPermission( aciContext ) )
104         {
105             throw new LdapNoPermissionException();
106         }
107     }
108 
109 
110     /**
111      * Returns <tt>true</tt> if the user with the specified name can access the specified resource
112      * (entry, attribute type, or attribute value) and throws {@link org.apache.directory.api.ldap.model.exception.LdapNoPermissionException}
113      * if the user doesn't have any permission to perform the specified grants.
114      *
115      * @param aciContext the container for ACI items
116      * @return <tt>true</tt> if the user has permission to access the resource
117      * @throws LdapException if failed to evaluate ACI items
118      */
119     public boolean hasPermission( AciContext aciContext ) throws LdapException
120     {
121         if ( aciContext.getEntryDn() == null )
122         {
123             throw new IllegalArgumentException( "entryName" );
124         }
125 
126         CoreSession session = aciContext.getOperationContext().getSession();
127         LookupOperationContextceptor/context/LookupOperationContext.html#LookupOperationContext">LookupOperationContext lookupContext = new LookupOperationContext( session, aciContext.getUserDn(),
128             SchemaConstants.ALL_ATTRIBUTES_ARRAY );
129         lookupContext.setPartition( aciContext.getOperationContext().getPartition() );
130         lookupContext.setTransaction( aciContext.getOperationContext().getTransaction() );
131         
132         Entry userEntry = session.getDirectoryService().getPartitionNexus().lookup( lookupContext );
133 
134         // Determine the scope of the requested operation.
135         OperationScope scope;
136 
137         if ( aciContext.getAttributeType() == null )
138         {
139             scope = OperationScope.ENTRY;
140         }
141         else if ( aciContext.getAttrValue() == null )
142         {
143             scope = OperationScope.ATTRIBUTE_TYPE;
144         }
145         else
146         {
147             scope = OperationScope.ATTRIBUTE_TYPE_AND_VALUE;
148         }
149 
150         // Clone aciTuples in case it is unmodifiable.
151         aciContext.setAciTuples( new ArrayList<ACITuple>( aciContext.getAciTuples() ) );
152 
153         // Filter unrelated and invalid tuples
154         for ( ACITupleFilter filter : filters )
155         {
156             if ( aciContext.getAciTuples().isEmpty() )
157             {
158                 // No need to continue filtering
159                 return false;
160             }
161 
162             Collection<ACITuple> aciTuples = filter.filter( aciContext, scope, userEntry );
163             aciContext.setAciTuples( aciTuples );
164         }
165 
166         // Deny access if no tuples left.
167         if ( aciContext.getAciTuples().isEmpty() )
168         {
169             return false;
170         }
171 
172         // Grant access if and only if one or more tuples remain and
173         // all grant access. Otherwise deny access.
174         for ( ACITuple tuple : aciContext.getAciTuples() )
175         {
176             if ( !tuple.isGrant() )
177             {
178                 return false;
179             }
180         }
181 
182         return true;
183     }
184 }