1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.ldap.server.authz;
18
19
20 import java.text.ParseException;
21 import java.util.*;
22
23 import javax.naming.Name;
24 import javax.naming.NamingEnumeration;
25 import javax.naming.NamingException;
26 import javax.naming.directory.Attribute;
27 import javax.naming.directory.Attributes;
28 import javax.naming.directory.ModificationItem;
29 import javax.naming.directory.SearchControls;
30 import javax.naming.directory.SearchResult;
31
32 import org.apache.ldap.common.aci.ACIItem;
33 import org.apache.ldap.common.aci.ACIItemParser;
34 import org.apache.ldap.common.exception.LdapInvalidAttributeValueException;
35 import org.apache.ldap.common.exception.LdapSchemaViolationException;
36 import org.apache.ldap.common.filter.ExprNode;
37 import org.apache.ldap.common.filter.SimpleNode;
38 import org.apache.ldap.common.message.ResultCodeEnum;
39 import org.apache.ldap.common.name.LdapName;
40 import org.apache.ldap.server.DirectoryServiceConfiguration;
41 import org.apache.ldap.server.partition.DirectoryPartitionNexus;
42 import org.apache.ldap.server.schema.ConcreteNameComponentNormalizer;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45
46
47 /***
48 * A cache for tuple sets which responds to specific events to perform
49 * cache house keeping as access control subentries are added, deleted
50 * and modified.
51 *
52 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
53 * @version $Rev$
54 */
55 public class TupleCache
56 {
57 /*** the attribute id for prescriptive aci: prescriptiveACI */
58 private static final String ACI_ATTR = "prescriptiveACI";
59 /*** the attribute id for an object class: objectClass */
60 private static final String OC_ATTR = "objectClass";
61 /*** the object class for access control subentries: accessControlSubentry */
62 private static final String ACSUBENTRY_OC = "accessControlSubentry";
63
64 /*** the logger for this class */
65 private static final Logger log = LoggerFactory.getLogger( TupleCache.class );
66
67 /*** cloned startup environment properties we use for subentry searching */
68 private final Hashtable env;
69 /*** a map of strings to ACITuple collections */
70 private final Map tuples = new HashMap();
71 /*** a handle on the partition nexus */
72 private final DirectoryPartitionNexus nexus;
73 /*** a normalizing ACIItem parser */
74 private final ACIItemParser aciParser;
75
76
77 /***
78 * Creates a ACITuple cache.
79 *
80 * @param factoryCfg the context factory configuration for the server
81 */
82 public TupleCache( DirectoryServiceConfiguration factoryCfg ) throws NamingException
83 {
84 this.nexus = factoryCfg.getPartitionNexus();
85 aciParser = new ACIItemParser( new ConcreteNameComponentNormalizer(
86 factoryCfg.getGlobalRegistries().getAttributeTypeRegistry() ) );
87 env = ( Hashtable ) factoryCfg.getEnvironment().clone();
88 initialize();
89 }
90
91
92 private void initialize() throws NamingException
93 {
94
95
96
97 Iterator suffixes = nexus.listSuffixes( true );
98 while ( suffixes.hasNext() )
99 {
100 String suffix = ( String ) suffixes.next();
101 Name baseDn = new LdapName( suffix );
102 ExprNode filter = new SimpleNode( OC_ATTR, ACSUBENTRY_OC, SimpleNode.EQUALITY );
103 SearchControls ctls = new SearchControls();
104 ctls.setSearchScope( SearchControls.SUBTREE_SCOPE );
105 NamingEnumeration results = nexus.search( baseDn, env, filter, ctls );
106 while ( results.hasMore() )
107 {
108 SearchResult result = ( SearchResult ) results.next();
109 String subentryDn = result.getName();
110 Attribute aci = result.getAttributes().get( ACI_ATTR );
111 if ( aci == null )
112 {
113 log.warn( "Found accessControlSubentry '" + subentryDn + "' without any " + ACI_ATTR );
114 continue;
115 }
116
117 subentryAdded( subentryDn, new LdapName( subentryDn ), result.getAttributes() );
118 }
119 results.close();
120 }
121 }
122
123
124 private boolean hasPrescriptiveACI( Attributes entry ) throws NamingException
125 {
126
127 Attribute aci = entry.get( ACI_ATTR );
128 if ( aci == null && entry.get( OC_ATTR ).contains( ACSUBENTRY_OC ) )
129 {
130
131
132
133 throw new LdapSchemaViolationException( "", ResultCodeEnum.OBJECTCLASSVIOLATION );
134 }
135 else if ( aci == null )
136 {
137 return false;
138 }
139 return true;
140 }
141
142
143 public void subentryAdded( String upName, Name normName, Attributes entry ) throws NamingException
144 {
145
146 Attribute aci = entry.get( ACI_ATTR );
147 if ( ! hasPrescriptiveACI( entry ) )
148 {
149 return;
150 }
151
152 List entryTuples = new ArrayList();
153 for ( int ii = 0; ii < aci.size(); ii++ )
154 {
155 ACIItem item = null;
156
157 try
158 {
159 item = aciParser.parse( ( String ) aci.get( ii ) );
160 }
161 catch ( ParseException e )
162 {
163 String msg = "ACIItem parser failure on '"+item+"'. Cannnot add ACITuples to TupleCache.";
164 log.warn( msg, e );
165 }
166
167 entryTuples.addAll( item.toTuples() );
168 }
169 tuples.put( normName.toString(), entryTuples );
170 }
171
172
173 public void subentryDeleted( Name normName, Attributes entry ) throws NamingException
174 {
175 if ( ! hasPrescriptiveACI( entry ) )
176 {
177 return;
178 }
179
180 tuples.remove( normName.toString() );
181 }
182
183
184 public void subentryModified( Name normName, ModificationItem[] mods, Attributes entry ) throws NamingException
185 {
186 if ( ! hasPrescriptiveACI( entry ) )
187 {
188 return;
189 }
190
191 boolean isAciModified = false;
192 for ( int ii = 0; ii < mods.length; ii++ )
193 {
194 isAciModified |= mods[ii].getAttribute().contains( ACI_ATTR );
195 }
196 if ( isAciModified )
197 {
198 subentryDeleted( normName, entry );
199 subentryAdded( normName.toString(), normName, entry );
200 }
201 }
202
203
204 public void subentryModified( Name normName, int modOp, Attributes mods, Attributes entry ) throws NamingException
205 {
206 if ( ! hasPrescriptiveACI( entry ) )
207 {
208 return;
209 }
210
211 if ( mods.get( ACI_ATTR ) != null )
212 {
213 subentryDeleted( normName, entry );
214 subentryAdded( normName.toString(), normName, entry );
215 }
216 }
217
218
219 public List getACITuples( String subentryDn )
220 {
221 List aciTuples = ( List ) tuples.get( subentryDn );
222 if ( aciTuples == null )
223 {
224 return Collections.EMPTY_LIST;
225 }
226 return Collections.unmodifiableList( aciTuples );
227 }
228
229
230 public void subentryRenamed( Name oldName, Name newName )
231 {
232 tuples.put( newName.toString(), tuples.remove( oldName.toString() ) );
233 }
234 }