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 */ 020package org.apache.directory.server.core.authz.support; 021 022 023import java.util.ArrayList; 024import java.util.Collection; 025 026import org.apache.directory.api.ldap.aci.ACITuple; 027import org.apache.directory.api.ldap.model.constants.SchemaConstants; 028import org.apache.directory.api.ldap.model.entry.Entry; 029import org.apache.directory.api.ldap.model.exception.LdapException; 030import org.apache.directory.api.ldap.model.exception.LdapNoPermissionException; 031import org.apache.directory.api.ldap.model.schema.SchemaManager; 032import org.apache.directory.server.core.api.CoreSession; 033import org.apache.directory.server.core.api.event.Evaluator; 034import org.apache.directory.server.core.api.event.ExpressionEvaluator; 035import org.apache.directory.server.core.api.interceptor.context.LookupOperationContext; 036import org.apache.directory.server.core.api.subtree.SubtreeEvaluator; 037import org.apache.directory.server.core.api.subtree.RefinementEvaluator; 038import org.apache.directory.server.core.api.subtree.RefinementLeafEvaluator; 039 040 041/** 042 * An implementation of Access Control Decision Function (18.8, X.501). 043 * <br> 044 * This engine simply filters the collection of tuples using the following 045 * {@link ACITupleFilter}s sequentially: 046 * <ol> 047 * <li>{@link RelatedUserClassFilter}</li> 048 * <li>{@link RelatedProtectedItemFilter}</li> 049 * <li>{@link MaxValueCountFilter}</li> 050 * <li>{@link MaxImmSubFilter}</li> 051 * <li>{@link RestrictedByFilter}</li> 052 * <li>{@link MicroOperationFilter}</li> 053 * <li>{@link HighestPrecedenceFilter}</li> 054 * <li>{@link MostSpecificUserClassFilter}</li> 055 * <li>{@link MostSpecificProtectedItemFilter}</li> 056 * </ol> 057 * <br> 058 * Operation is determined to be permitted if and only if there is at least one 059 * tuple left and all of them grants the access. (18.8.4. X.501) 060 * 061 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 062 */ 063public class ACDFEngine 064{ 065 private final ACITupleFilter[] filters; 066 067 068 /** 069 * Creates a new instance. 070 * 071 * @param schemaManager The server schemaManager 072 */ 073 public ACDFEngine( SchemaManager schemaManager ) 074 { 075 Evaluator entryEvaluator = new ExpressionEvaluator( schemaManager ); 076 SubtreeEvaluator subtreeEvaluator = new SubtreeEvaluator( schemaManager ); 077 RefinementEvaluator refinementEvaluator = new RefinementEvaluator( new RefinementLeafEvaluator( schemaManager ) ); 078 079 filters = new ACITupleFilter[] 080 { 081 new RelatedUserClassFilter( subtreeEvaluator ), 082 new RelatedProtectedItemFilter( refinementEvaluator, entryEvaluator, schemaManager ), 083 new MaxValueCountFilter(), 084 new MaxImmSubFilter( schemaManager ), 085 new RestrictedByFilter(), 086 new MicroOperationFilter(), 087 new HighestPrecedenceFilter(), 088 new MostSpecificUserClassFilter(), 089 new MostSpecificProtectedItemFilter() }; 090 } 091 092 093 /** 094 * Checks the user with the specified name can access the specified resource 095 * (entry, attribute type, or attribute value) and throws {@link LdapNoPermissionException} 096 * if the user doesn't have any permission to perform the specified grants. 097 * 098 * @param aciContext the container for ACI items 099 * @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 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}