001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 *
019 */
020 package org.apache.directory.server.core.authz.support;
021
022
023 import java.util.ArrayList;
024 import java.util.Collection;
025 import java.util.Collections;
026 import java.util.HashSet;
027
028 import javax.naming.NamingException;
029
030 import org.apache.directory.server.core.authn.AuthenticationInterceptor;
031 import org.apache.directory.server.core.authz.AciAuthorizationInterceptor;
032 import org.apache.directory.server.core.authz.DefaultAuthorizationInterceptor;
033 import org.apache.directory.server.core.event.Evaluator;
034 import org.apache.directory.server.core.event.EventInterceptor;
035 import org.apache.directory.server.core.event.ExpressionEvaluator;
036 import org.apache.directory.server.core.interceptor.context.OperationContext;
037 import org.apache.directory.server.core.normalization.NormalizationInterceptor;
038 import org.apache.directory.server.core.operational.OperationalAttributeInterceptor;
039 import org.apache.directory.server.core.schema.SchemaInterceptor;
040 import org.apache.directory.server.core.subtree.RefinementEvaluator;
041 import org.apache.directory.server.core.subtree.RefinementLeafEvaluator;
042 import org.apache.directory.server.core.subtree.SubentryInterceptor;
043 import org.apache.directory.server.core.subtree.SubtreeEvaluator;
044 import org.apache.directory.server.core.trigger.TriggerInterceptor;
045 import org.apache.directory.shared.ldap.aci.ACITuple;
046 import org.apache.directory.shared.ldap.aci.MicroOperation;
047 import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
048 import org.apache.directory.shared.ldap.entry.ServerEntry;
049 import org.apache.directory.shared.ldap.entry.Value;
050 import org.apache.directory.shared.ldap.exception.LdapNoPermissionException;
051 import org.apache.directory.shared.ldap.name.DN;
052 import org.apache.directory.shared.ldap.schema.SchemaManager;
053 import org.apache.directory.shared.ldap.schema.registries.OidRegistry;
054
055
056 /**
057 * An implementation of Access Control Decision Function (18.8, X.501).
058 * <p>
059 * This engine simply filters the collection of tuples using the following
060 * {@link ACITupleFilter}s sequentially:
061 * <ol>
062 * <li>{@link RelatedUserClassFilter}</li>
063 * <li>{@link RelatedProtectedItemFilter}</li>
064 * <li>{@link MaxValueCountFilter}</li>
065 * <li>{@link MaxImmSubFilter}</li>
066 * <li>{@link RestrictedByFilter}</li>
067 * <li>{@link MicroOperationFilter}</li>
068 * <li>{@link HighestPrecedenceFilter}</li>
069 * <li>{@link MostSpecificUserClassFilter}</li>
070 * <li>{@link MostSpecificProtectedItemFilter}</li>
071 * </ol>
072 * <p>
073 * Operation is determined to be permitted if and only if there is at least one
074 * tuple left and all of them grants the access. (18.8.4. X.501)
075 *
076 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
077 * @version $Rev: 927146 $, $Date: 2010-03-24 20:39:54 +0200 (Wed, 24 Mar 2010) $
078 */
079 public class ACDFEngine
080 {
081 private final ACITupleFilter[] filters;
082
083
084 /**
085 * Creates a new instance.
086 *
087 * @param oidRegistry an OID registry to be used by internal components
088 * @param attrTypeRegistry an attribute type registry to be used by internal components
089 *
090 * @throws NamingException if failed to initialize internal components
091 */
092 public ACDFEngine( OidRegistry oidRegistry, SchemaManager schemaManager ) throws NamingException
093 {
094 Evaluator entryEvaluator = new ExpressionEvaluator( oidRegistry, schemaManager );
095 SubtreeEvaluator subtreeEvaluator = new SubtreeEvaluator( oidRegistry, schemaManager );
096 RefinementEvaluator refinementEvaluator = new RefinementEvaluator( new RefinementLeafEvaluator( oidRegistry ) );
097
098 filters = new ACITupleFilter[] {
099 new RelatedUserClassFilter( subtreeEvaluator ),
100 new RelatedProtectedItemFilter( refinementEvaluator, entryEvaluator, oidRegistry, schemaManager ),
101 new MaxValueCountFilter(),
102 new MaxImmSubFilter(),
103 new RestrictedByFilter(),
104 new MicroOperationFilter(),
105 new HighestPrecedenceFilter(),
106 new MostSpecificUserClassFilter(),
107 new MostSpecificProtectedItemFilter() };
108 }
109
110
111 /**
112 * Checks the user with the specified name can access the specified resource
113 * (entry, attribute type, or attribute value) and throws {@link LdapNoPermissionException}
114 * if the user doesn't have any permission to perform the specified grants.
115 *
116 * @param proxy the proxy to the partition nexus
117 * @param userGroupNames the collection of the group DNs the user who is trying to access the resource belongs
118 * @param username the DN of the user who is trying to access the resource
119 * @param entryName the DN of the entry the user is trying to access
120 * @param attrId the attribute type of the attribute the user is trying to access.
121 * <tt>null</tt> if the user is not accessing a specific attribute type.
122 * @param attrValue the attribute value of the attribute the user is trying to access.
123 * <tt>null</tt> if the user is not accessing a specific attribute value.
124 * @param microOperations the {@link org.apache.directory.shared.ldap.aci.MicroOperation}s to perform
125 * @param aciTuples {@link org.apache.directory.shared.ldap.aci.ACITuple}s translated from {@link org.apache.directory.shared.ldap.aci.ACIItem}s in the subtree entries
126 * @param entryView in case of a Modify operation, view of the entry being modified as if the modification permitted and completed
127 * @throws NamingException if failed to evaluate ACI items
128 */
129 public void checkPermission(
130 SchemaManager schemaManager,
131 OperationContext opContext,
132 Collection<DN> userGroupNames,
133 DN username,
134 AuthenticationLevel authenticationLevel,
135 DN entryName,
136 String attrId,
137 Value<?> attrValue,
138 Collection<MicroOperation> microOperations,
139 Collection<ACITuple> aciTuples,
140 ServerEntry entry,
141 ServerEntry entryView ) throws Exception
142 {
143 if ( !hasPermission( schemaManager, opContext, userGroupNames, username, authenticationLevel, entryName,
144 attrId, attrValue, microOperations, aciTuples, entry, entryView ) )
145 {
146 throw new LdapNoPermissionException();
147 }
148 }
149
150 public static final Collection<String> USER_LOOKUP_BYPASS;
151 static
152 {
153 Collection<String> c = new HashSet<String>();
154 c.add( NormalizationInterceptor.class.getName() );
155 c.add( AuthenticationInterceptor.class.getName() );
156 // c.add( ReferralInterceptor.class.getName() );
157 c.add( AciAuthorizationInterceptor.class.getName() );
158 c.add( DefaultAuthorizationInterceptor.class.getName() );
159 // c.add( ExceptionInterceptor.class.getName() );
160 c.add( OperationalAttributeInterceptor.class.getName() );
161 c.add( SchemaInterceptor.class.getName() );
162 c.add( SubentryInterceptor.class.getName() );
163 // c.add( CollectiveAttributeInterceptor.class.getName() );
164 c.add( EventInterceptor.class.getName() );
165 c.add( TriggerInterceptor.class.getName() );
166 USER_LOOKUP_BYPASS = Collections.unmodifiableCollection( c );
167 }
168
169
170 /**
171 * Returns <tt>true</tt> if the user with the specified name can access the specified resource
172 * (entry, attribute type, or attribute value) and throws {@link LdapNoPermissionException}
173 * if the user doesn't have any permission to perform the specified grants.
174 *
175 * @param proxy the proxy to the partition nexus
176 * @param userGroupNames the collection of the group DNs the user who is trying to access the resource belongs
177 * @param userName the DN of the user who is trying to access the resource
178 * @param entryName the DN of the entry the user is trying to access
179 * @param attrId the attribute type of the attribute the user is trying to access.
180 * <tt>null</tt> if the user is not accessing a specific attribute type.
181 * @param attrValue the attribute value of the attribute the user is trying to access.
182 * <tt>null</tt> if the user is not accessing a specific attribute value.
183 * @param microOperations the {@link org.apache.directory.shared.ldap.aci.MicroOperation}s to perform
184 * @param aciTuples {@link org.apache.directory.shared.ldap.aci.ACITuple}s translated from {@link org.apache.directory.shared.ldap.aci.ACIItem}s in the subtree entries
185 * @param entryView in case of a Modify operation, view of the entry being modified as if the modification permitted and completed
186 */
187 public boolean hasPermission(
188 SchemaManager schemaManager,
189 OperationContext opContext,
190 Collection<DN> userGroupNames,
191 DN userName,
192 AuthenticationLevel authenticationLevel,
193 DN entryName,
194 String attrId,
195 Value<?> attrValue,
196 Collection<MicroOperation> microOperations,
197 Collection<ACITuple> aciTuples,
198 ServerEntry entry,
199 ServerEntry entryView ) throws Exception
200 {
201 if ( entryName == null )
202 {
203 throw new NullPointerException( "entryName" );
204 }
205
206 ServerEntry userEntry = opContext.lookup( userName, USER_LOOKUP_BYPASS );
207
208 // Determine the scope of the requested operation.
209 OperationScope scope;
210
211 if ( attrId == null )
212 {
213 scope = OperationScope.ENTRY;
214 }
215 else if ( attrValue == null )
216 {
217 scope = OperationScope.ATTRIBUTE_TYPE;
218 }
219 else
220 {
221 scope = OperationScope.ATTRIBUTE_TYPE_AND_VALUE;
222 }
223
224 // Clone aciTuples in case it is unmodifiable.
225 aciTuples = new ArrayList<ACITuple>( aciTuples );
226
227 // Filter unrelated and invalid tuples
228 for ( ACITupleFilter filter : filters )
229 {
230 aciTuples = filter.filter(
231 schemaManager,
232 aciTuples,
233 scope,
234 opContext,
235 userGroupNames,
236 userName,
237 userEntry,
238 authenticationLevel,
239 entryName,
240 attrId,
241 attrValue,
242 entry,
243 microOperations,
244 entryView );
245 }
246
247 // Deny access if no tuples left.
248 if ( aciTuples.size() == 0 )
249 {
250 return false;
251 }
252
253 // Grant access if and only if one or more tuples remain and
254 // all grant access. Otherwise deny access.
255 for ( ACITuple tuple : aciTuples )
256 {
257 if ( !tuple.isGrant() )
258 {
259 return false;
260 }
261 }
262
263 return true;
264 }
265 }