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; 021 022 023 import java.text.ParseException; 024 import java.util.ArrayList; 025 import java.util.Collections; 026 import java.util.HashMap; 027 import java.util.List; 028 import java.util.Map; 029 import java.util.Set; 030 031 import javax.naming.NamingException; 032 import javax.naming.directory.SearchControls; 033 034 import org.apache.directory.server.core.CoreSession; 035 import org.apache.directory.server.core.filtering.EntryFilteringCursor; 036 import org.apache.directory.server.core.interceptor.context.SearchOperationContext; 037 import org.apache.directory.server.core.partition.PartitionNexus; 038 import org.apache.directory.server.i18n.I18n; 039 import org.apache.directory.shared.ldap.aci.ACIItem; 040 import org.apache.directory.shared.ldap.aci.ACIItemParser; 041 import org.apache.directory.shared.ldap.aci.ACITuple; 042 import org.apache.directory.shared.ldap.constants.SchemaConstants; 043 import org.apache.directory.shared.ldap.entry.StringValue; 044 import org.apache.directory.shared.ldap.entry.EntryAttribute; 045 import org.apache.directory.shared.ldap.entry.Modification; 046 import org.apache.directory.shared.ldap.entry.ServerEntry; 047 import org.apache.directory.shared.ldap.entry.Value; 048 import org.apache.directory.shared.ldap.exception.LdapException; 049 import org.apache.directory.shared.ldap.exception.LdapSchemaViolationException; 050 import org.apache.directory.shared.ldap.filter.EqualityNode; 051 import org.apache.directory.shared.ldap.filter.ExprNode; 052 import org.apache.directory.shared.ldap.message.AliasDerefMode; 053 import org.apache.directory.shared.ldap.message.ResultCodeEnum; 054 import org.apache.directory.shared.ldap.name.DN; 055 import org.apache.directory.shared.ldap.name.NameComponentNormalizer; 056 import org.apache.directory.shared.ldap.schema.AttributeType; 057 import org.apache.directory.shared.ldap.schema.SchemaManager; 058 import org.apache.directory.shared.ldap.schema.normalizers.ConcreteNameComponentNormalizer; 059 import org.slf4j.Logger; 060 import org.slf4j.LoggerFactory; 061 062 063 /** 064 * A cache for tuple sets which responds to specific events to perform 065 * cache house keeping as access control subentries are added, deleted 066 * and modified. 067 * 068 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 069 * @version $Rev: 928945 $ 070 */ 071 public class TupleCache 072 { 073 /** the logger for this class */ 074 private static final Logger LOG = LoggerFactory.getLogger( TupleCache.class ); 075 076 /** a map of strings to ACITuple collections */ 077 private final Map<String, List<ACITuple>> tuples = new HashMap<String, List<ACITuple>>(); 078 079 /** a handle on the partition nexus */ 080 private final PartitionNexus nexus; 081 082 /** a normalizing ACIItem parser */ 083 private final ACIItemParser aciParser; 084 085 /** A storage for the PrescriptiveACI attributeType */ 086 private AttributeType prescriptiveAciAT; 087 088 089 /** 090 * Creates a ACITuple cache. 091 * 092 * @param directoryService the context factory configuration for the server 093 * @throws NamingException if initialization fails 094 */ 095 public TupleCache( CoreSession session ) throws Exception 096 { 097 SchemaManager schemaManager = session.getDirectoryService().getSchemaManager(); 098 this.nexus = session.getDirectoryService().getPartitionNexus(); 099 NameComponentNormalizer ncn = new ConcreteNameComponentNormalizer( schemaManager ); 100 aciParser = new ACIItemParser( ncn, schemaManager.getNormalizerMapping() ); 101 prescriptiveAciAT = schemaManager.lookupAttributeTypeRegistry( SchemaConstants.PRESCRIPTIVE_ACI_AT ); 102 initialize( session ); 103 } 104 105 106 private DN parseNormalized( SchemaManager schemaManager, String name ) throws LdapException 107 { 108 DN dn = new DN( name ); 109 dn.normalize( schemaManager.getNormalizerMapping() ); 110 return dn; 111 } 112 113 114 private void initialize( CoreSession session ) throws Exception 115 { 116 // search all naming contexts for access control subentenries 117 // generate ACITuple Arrays for each subentry 118 // add that subentry to the hash 119 Set<String> suffixes = nexus.listSuffixes( null ); 120 121 for ( String suffix:suffixes ) 122 { 123 DN baseDn = parseNormalized( session.getDirectoryService().getSchemaManager(), suffix ); 124 ExprNode filter = new EqualityNode<String>( SchemaConstants.OBJECT_CLASS_AT, 125 new StringValue( SchemaConstants.ACCESS_CONTROL_SUBENTRY_OC ) ); 126 SearchControls ctls = new SearchControls(); 127 ctls.setSearchScope( SearchControls.SUBTREE_SCOPE ); 128 129 SearchOperationContext searchOperationContext = new SearchOperationContext( session, 130 baseDn, filter, ctls ); 131 searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES ); 132 133 EntryFilteringCursor results = nexus.search( searchOperationContext ); 134 135 while ( results.next() ) 136 { 137 ServerEntry result = results.get(); 138 DN subentryDn = result.getDn().normalize( session.getDirectoryService().getSchemaManager(). 139 getNormalizerMapping() ); 140 EntryAttribute aci = result.get( prescriptiveAciAT ); 141 142 if ( aci == null ) 143 { 144 LOG.warn( "Found accessControlSubentry '" + subentryDn + "' without any " 145 + SchemaConstants.PRESCRIPTIVE_ACI_AT ); 146 continue; 147 } 148 149 subentryAdded( subentryDn, result ); 150 } 151 152 results.close(); 153 } 154 } 155 156 157 private boolean hasPrescriptiveACI( ServerEntry entry ) throws LdapException 158 { 159 // only do something if the entry contains prescriptiveACI 160 EntryAttribute aci = entry.get( prescriptiveAciAT ); 161 162 if ( aci == null ) 163 { 164 if ( entry.contains( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.ACCESS_CONTROL_SUBENTRY_OC ) 165 || entry.contains( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.ACCESS_CONTROL_SUBENTRY_OC_OID ) ) 166 { 167 // should not be necessary because of schema interceptor but schema checking 168 // can be turned off and in this case we must protect against being able to 169 // add access control information to anything other than an AC subentry 170 throw new LdapSchemaViolationException( ResultCodeEnum.OBJECT_CLASS_VIOLATION, "" ); 171 } 172 else 173 { 174 return false; 175 } 176 } 177 178 return true; 179 } 180 181 182 public void subentryAdded( DN normName, ServerEntry entry ) throws LdapException 183 { 184 // only do something if the entry contains prescriptiveACI 185 EntryAttribute aciAttr = entry.get( prescriptiveAciAT ); 186 187 if ( !hasPrescriptiveACI( entry ) ) 188 { 189 return; 190 } 191 192 List<ACITuple> entryTuples = new ArrayList<ACITuple>(); 193 194 for ( Value<?> value : aciAttr ) 195 { 196 String aci = value.getString(); 197 ACIItem item = null; 198 199 try 200 { 201 item = aciParser.parse( aci ); 202 entryTuples.addAll( item.toTuples() ); 203 } 204 catch ( ParseException e ) 205 { 206 String msg = I18n.err( I18n.ERR_28, item ); 207 LOG.error( msg, e ); 208 209 // do not process this ACI Item because it will be null 210 // continue on to process the next ACI item in the entry 211 } 212 } 213 214 tuples.put( normName.getNormName(), entryTuples ); 215 } 216 217 218 public void subentryDeleted( DN normName, ServerEntry entry ) throws LdapException 219 { 220 if ( !hasPrescriptiveACI( entry ) ) 221 { 222 return; 223 } 224 225 tuples.remove( normName.toString() ); 226 } 227 228 229 public void subentryModified( DN normName, List<Modification> mods, ServerEntry entry ) throws LdapException 230 { 231 if ( !hasPrescriptiveACI( entry ) ) 232 { 233 return; 234 } 235 236 for ( Modification mod : mods ) 237 { 238 if ( mod.getAttribute().instanceOf( SchemaConstants.PRESCRIPTIVE_ACI_AT ) ) 239 { 240 subentryDeleted( normName, entry ); 241 subentryAdded( normName, entry ); 242 } 243 } 244 } 245 246 247 public void subentryModified( DN normName, ServerEntry mods, ServerEntry entry ) throws LdapException 248 { 249 if ( !hasPrescriptiveACI( entry ) ) 250 { 251 return; 252 } 253 254 if ( mods.get( prescriptiveAciAT ) != null ) 255 { 256 subentryDeleted( normName, entry ); 257 subentryAdded( normName, entry ); 258 } 259 } 260 261 262 @SuppressWarnings("unchecked") 263 public List<ACITuple> getACITuples( String subentryDn ) 264 { 265 List aciTuples = tuples.get( subentryDn ); 266 if ( aciTuples == null ) 267 { 268 return Collections.EMPTY_LIST; 269 } 270 return Collections.unmodifiableList( aciTuples ); 271 } 272 273 274 public void subentryRenamed( DN oldName, DN newName ) 275 { 276 tuples.put( newName.getNormName(), tuples.remove( oldName.getNormName() ) ); 277 } 278 }