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 import java.util.Collections;
26 import java.util.HashSet;
27
28 import javax.naming.NamingException;
29
30 import org.apache.directory.server.core.authn.AuthenticationInterceptor;
31 import org.apache.directory.server.core.authz.AciAuthorizationInterceptor;
32 import org.apache.directory.server.core.authz.DefaultAuthorizationInterceptor;
33 import org.apache.directory.server.core.entry.ServerEntry;
34 import org.apache.directory.server.core.event.Evaluator;
35 import org.apache.directory.server.core.event.EventInterceptor;
36 import org.apache.directory.server.core.event.ExpressionEvaluator;
37 import org.apache.directory.server.core.interceptor.context.OperationContext;
38 import org.apache.directory.server.core.normalization.NormalizationInterceptor;
39 import org.apache.directory.server.core.operational.OperationalAttributeInterceptor;
40 import org.apache.directory.server.core.schema.SchemaInterceptor;
41 import org.apache.directory.server.core.subtree.RefinementEvaluator;
42 import org.apache.directory.server.core.subtree.RefinementLeafEvaluator;
43 import org.apache.directory.server.core.subtree.SubentryInterceptor;
44 import org.apache.directory.server.core.subtree.SubtreeEvaluator;
45 import org.apache.directory.server.core.trigger.TriggerInterceptor;
46 import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
47 import org.apache.directory.server.schema.registries.OidRegistry;
48 import org.apache.directory.server.schema.registries.Registries;
49 import org.apache.directory.shared.ldap.aci.ACITuple;
50 import org.apache.directory.shared.ldap.aci.MicroOperation;
51 import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
52 import org.apache.directory.shared.ldap.entry.Value;
53 import org.apache.directory.shared.ldap.exception.LdapNoPermissionException;
54 import org.apache.directory.shared.ldap.name.LdapDN;
55
56
57 /**
58 * An implementation of Access Control Decision Function (18.8, X.501).
59 * <p>
60 * This engine simply filters the collection of tuples using the following
61 * {@link ACITupleFilter}s sequentially:
62 * <ol>
63 * <li>{@link RelatedUserClassFilter}</li>
64 * <li>{@link RelatedProtectedItemFilter}</li>
65 * <li>{@link MaxValueCountFilter}</li>
66 * <li>{@link MaxImmSubFilter}</li>
67 * <li>{@link RestrictedByFilter}</li>
68 * <li>{@link MicroOperationFilter}</li>
69 * <li>{@link HighestPrecedenceFilter}</li>
70 * <li>{@link MostSpecificUserClassFilter}</li>
71 * <li>{@link MostSpecificProtectedItemFilter}</li>
72 * </ol>
73 * <p>
74 * Operation is determined to be permitted if and only if there is at least one
75 * tuple left and all of them grants the access. (18.8.4. X.501)
76 *
77 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
78 * @version $Rev: 662440 $, $Date: 2008-06-02 16:00:23 +0200 (Mo, 02 Jun 2008) $
79 */
80 public class ACDFEngine
81 {
82 private final ACITupleFilter[] filters;
83
84
85 /**
86 * Creates a new instance.
87 *
88 * @param oidRegistry an OID registry to be used by internal components
89 * @param attrTypeRegistry an attribute type registry to be used by internal components
90 *
91 * @throws NamingException if failed to initialize internal components
92 */
93 public ACDFEngine( OidRegistry oidRegistry, AttributeTypeRegistry attrTypeRegistry ) throws NamingException
94 {
95 Evaluator entryEvaluator = new ExpressionEvaluator( oidRegistry, attrTypeRegistry );
96 SubtreeEvaluator subtreeEvaluator = new SubtreeEvaluator( oidRegistry, attrTypeRegistry );
97 RefinementEvaluator refinementEvaluator = new RefinementEvaluator( new RefinementLeafEvaluator( oidRegistry ) );
98
99 filters = new ACITupleFilter[] {
100 new RelatedUserClassFilter( subtreeEvaluator ),
101 new RelatedProtectedItemFilter( refinementEvaluator, entryEvaluator, oidRegistry, attrTypeRegistry ),
102 new MaxValueCountFilter(),
103 new MaxImmSubFilter(),
104 new RestrictedByFilter(),
105 new MicroOperationFilter(),
106 new HighestPrecedenceFilter(),
107 new MostSpecificUserClassFilter(),
108 new MostSpecificProtectedItemFilter() };
109 }
110
111
112 /**
113 * Checks the user with the specified name can access the specified resource
114 * (entry, attribute type, or attribute value) and throws {@link LdapNoPermissionException}
115 * if the user doesn't have any permission to perform the specified grants.
116 *
117 * @param proxy the proxy to the partition nexus
118 * @param userGroupNames the collection of the group DNs the user who is trying to access the resource belongs
119 * @param username the DN of the user who is trying to access the resource
120 * @param entryName the DN of the entry the user is trying to access
121 * @param attrId the attribute type of the attribute the user is trying to access.
122 * <tt>null</tt> if the user is not accessing a specific attribute type.
123 * @param attrValue the attribute value of the attribute the user is trying to access.
124 * <tt>null</tt> if the user is not accessing a specific attribute value.
125 * @param microOperations the {@link org.apache.directory.shared.ldap.aci.MicroOperation}s to perform
126 * @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
127 * @param entryView in case of a Modify operation, view of the entry being modified as if the modification permitted and completed
128 * @throws NamingException if failed to evaluate ACI items
129 */
130 public void checkPermission(
131 Registries registries,
132 OperationContext opContext,
133 Collection<LdapDN> userGroupNames,
134 LdapDN username,
135 AuthenticationLevel authenticationLevel,
136 LdapDN entryName,
137 String attrId,
138 Value<?> attrValue,
139 Collection<MicroOperation> microOperations,
140 Collection<ACITuple> aciTuples,
141 ServerEntry entry,
142 ServerEntry entryView ) throws Exception
143 {
144 if ( !hasPermission( registries, opContext, userGroupNames, username, authenticationLevel, entryName,
145 attrId, attrValue, microOperations, aciTuples, entry, entryView ) )
146 {
147 throw new LdapNoPermissionException();
148 }
149 }
150
151 public static final Collection<String> USER_LOOKUP_BYPASS;
152 static
153 {
154 Collection<String> c = new HashSet<String>();
155 c.add( NormalizationInterceptor.class.getName() );
156 c.add( AuthenticationInterceptor.class.getName() );
157 // c.add( ReferralInterceptor.class.getName() );
158 c.add( AciAuthorizationInterceptor.class.getName() );
159 c.add( DefaultAuthorizationInterceptor.class.getName() );
160 // c.add( ExceptionInterceptor.class.getName() );
161 c.add( OperationalAttributeInterceptor.class.getName() );
162 c.add( SchemaInterceptor.class.getName() );
163 c.add( SubentryInterceptor.class.getName() );
164 // c.add( CollectiveAttributeInterceptor.class.getName() );
165 c.add( EventInterceptor.class.getName() );
166 c.add( TriggerInterceptor.class.getName() );
167 USER_LOOKUP_BYPASS = Collections.unmodifiableCollection( c );
168 }
169
170
171 /**
172 * Returns <tt>true</tt> if the user with the specified name can access the specified resource
173 * (entry, attribute type, or attribute value) and throws {@link LdapNoPermissionException}
174 * if the user doesn't have any permission to perform the specified grants.
175 *
176 * @param proxy the proxy to the partition nexus
177 * @param userGroupNames the collection of the group DNs the user who is trying to access the resource belongs
178 * @param userName the DN of the user who is trying to access the resource
179 * @param entryName the DN of the entry the user is trying to access
180 * @param attrId the attribute type of the attribute the user is trying to access.
181 * <tt>null</tt> if the user is not accessing a specific attribute type.
182 * @param attrValue the attribute value of the attribute the user is trying to access.
183 * <tt>null</tt> if the user is not accessing a specific attribute value.
184 * @param microOperations the {@link org.apache.directory.shared.ldap.aci.MicroOperation}s to perform
185 * @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
186 * @param entryView in case of a Modify operation, view of the entry being modified as if the modification permitted and completed
187 */
188 public boolean hasPermission(
189 Registries registries,
190 OperationContext opContext,
191 Collection<LdapDN> userGroupNames,
192 LdapDN userName,
193 AuthenticationLevel authenticationLevel,
194 LdapDN entryName,
195 String attrId,
196 Value<?> attrValue,
197 Collection<MicroOperation> microOperations,
198 Collection<ACITuple> aciTuples,
199 ServerEntry entry,
200 ServerEntry entryView ) throws Exception
201 {
202 if ( entryName == null )
203 {
204 throw new NullPointerException( "entryName" );
205 }
206
207 ServerEntry userEntry = opContext.lookup( userName, USER_LOOKUP_BYPASS );
208
209 // Determine the scope of the requested operation.
210 OperationScope scope;
211
212 if ( attrId == null )
213 {
214 scope = OperationScope.ENTRY;
215 }
216 else if ( attrValue == null )
217 {
218 scope = OperationScope.ATTRIBUTE_TYPE;
219 }
220 else
221 {
222 scope = OperationScope.ATTRIBUTE_TYPE_AND_VALUE;
223 }
224
225 // Clone aciTuples in case it is unmodifiable.
226 aciTuples = new ArrayList<ACITuple>( aciTuples );
227
228 // Filter unrelated and invalid tuples
229 for ( ACITupleFilter filter : filters )
230 {
231 aciTuples = filter.filter(
232 registries,
233 aciTuples,
234 scope,
235 opContext,
236 userGroupNames,
237 userName,
238 userEntry,
239 authenticationLevel,
240 entryName,
241 attrId,
242 attrValue,
243 entry,
244 microOperations,
245 entryView );
246 }
247
248 // Deny access if no tuples left.
249 if ( aciTuples.size() == 0 )
250 {
251 return false;
252 }
253
254 // Grant access if and only if one or more tuples remain and
255 // all grant access. Otherwise deny access.
256 for ( ACITuple tuple : aciTuples )
257 {
258 if ( !tuple.isGrant() )
259 {
260 return false;
261 }
262 }
263
264 return true;
265 }
266 }