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 19:39:54 +0100 (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 }