1 /* 2 * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.jndi.ldap; 27 28 import javax.naming.*; 29 import javax.naming.directory.*; 30 import java.util.Hashtable; 31 import com.sun.jndi.toolkit.dir.HierMemDirCtx; 32 33 /** 34 * This is the class used to implement LDAP's GetSchema call. 35 * 36 * It subclasses HierMemDirContext for most of the functionality. It 37 * overrides functions that cause the schema definitions to change. 38 * In such a case, it write the schema to the LdapServer and (assuming 39 * there are no errors), calls it's superclass's equivalent function. 40 * Thus, the schema tree and the LDAP server's schema attributes are 41 * always in sync. 42 */ 43 44 final class LdapSchemaCtx extends HierMemDirCtx { 45 46 static private final boolean debug = false; 47 48 private static final int LEAF = 0; // schema object (e.g. attribute type defn) 49 private static final int SCHEMA_ROOT = 1; // schema tree root 50 static final int OBJECTCLASS_ROOT = 2; // root of object class subtree 51 static final int ATTRIBUTE_ROOT = 3; // root of attribute type subtree 52 static final int SYNTAX_ROOT = 4; // root of syntax subtree 53 static final int MATCHRULE_ROOT = 5; // root of matching rule subtree 54 static final int OBJECTCLASS = 6; // an object class definition 55 static final int ATTRIBUTE = 7; // an attribute type definition 56 static final int SYNTAX = 8; // a syntax definition 57 static final int MATCHRULE = 9; // a matching rule definition 58 59 private SchemaInfo info= null; 60 private boolean setupMode = true; 61 62 private int objectType; 63 createSchemaTree(Hashtable<String,Object> env, String subschemasubentry, LdapCtx schemaEntry, Attributes schemaAttrs, boolean netscapeBug)64 static DirContext createSchemaTree(Hashtable<String,Object> env, 65 String subschemasubentry, LdapCtx schemaEntry, 66 Attributes schemaAttrs, boolean netscapeBug) 67 throws NamingException { 68 try { 69 LdapSchemaParser parser = new LdapSchemaParser(netscapeBug); 70 71 SchemaInfo allinfo = new SchemaInfo(subschemasubentry, 72 schemaEntry, parser); 73 74 LdapSchemaCtx root = new LdapSchemaCtx(SCHEMA_ROOT, env, allinfo); 75 LdapSchemaParser.LDAP2JNDISchema(schemaAttrs, root); 76 return root; 77 } catch (NamingException e) { 78 schemaEntry.close(); // cleanup 79 throw e; 80 } 81 } 82 83 // Called by createNewCtx LdapSchemaCtx(int objectType, Hashtable<String,Object> environment, SchemaInfo info)84 private LdapSchemaCtx(int objectType, Hashtable<String,Object> environment, 85 SchemaInfo info) { 86 super(environment, LdapClient.caseIgnore); 87 88 this.objectType = objectType; 89 this.info = info; 90 } 91 92 // override HierMemDirCtx.close to prevent premature GC of shared data close()93 public void close() throws NamingException { 94 info.close(); 95 } 96 97 // override to ignore obj and use attrs 98 // treat same as createSubcontext bind(Name name, Object obj, Attributes attrs)99 final public void bind(Name name, Object obj, Attributes attrs) 100 throws NamingException { 101 if (!setupMode) { 102 if (obj != null) { 103 throw new IllegalArgumentException("obj must be null"); 104 } 105 106 // Update server 107 addServerSchema(attrs); 108 } 109 110 // Update in-memory copy 111 LdapSchemaCtx newEntry = 112 (LdapSchemaCtx)super.doCreateSubcontext(name, attrs); 113 } 114 doBind(Name name, Object obj, Attributes attrs, boolean useFactory)115 final protected void doBind(Name name, Object obj, Attributes attrs, 116 boolean useFactory) throws NamingException { 117 if (!setupMode) { 118 throw new SchemaViolationException( 119 "Cannot bind arbitrary object; use createSubcontext()"); 120 } else { 121 super.doBind(name, obj, attrs, false); // always ignore factories 122 } 123 } 124 125 // override to use bind() instead rebind(Name name, Object obj, Attributes attrs)126 final public void rebind(Name name, Object obj, Attributes attrs) 127 throws NamingException { 128 try { 129 doLookup(name, false); 130 throw new SchemaViolationException( 131 "Cannot replace existing schema object"); 132 } catch (NameNotFoundException e) { 133 bind(name, obj, attrs); 134 } 135 } 136 doRebind(Name name, Object obj, Attributes attrs, boolean useFactory)137 final protected void doRebind(Name name, Object obj, Attributes attrs, 138 boolean useFactory) throws NamingException { 139 if (!setupMode) { 140 throw new SchemaViolationException( 141 "Cannot bind arbitrary object; use createSubcontext()"); 142 } else { 143 super.doRebind(name, obj, attrs, false); // always ignore factories 144 } 145 } 146 doUnbind(Name name)147 final protected void doUnbind(Name name) throws NamingException { 148 if (!setupMode) { 149 // Update server 150 try { 151 // Lookup entry from memory 152 LdapSchemaCtx target = (LdapSchemaCtx)doLookup(name, false); 153 154 deleteServerSchema(target.attrs); 155 } catch (NameNotFoundException e) { 156 return; 157 } 158 } 159 // Update in-memory copy 160 super.doUnbind(name); 161 } 162 doRename(Name oldname, Name newname)163 final protected void doRename(Name oldname, Name newname) 164 throws NamingException { 165 if (!setupMode) { 166 throw new SchemaViolationException("Cannot rename a schema object"); 167 } else { 168 super.doRename(oldname, newname); 169 } 170 } 171 doDestroySubcontext(Name name)172 final protected void doDestroySubcontext(Name name) throws NamingException { 173 if (!setupMode) { 174 // Update server 175 try { 176 // Lookup entry from memory 177 LdapSchemaCtx target = (LdapSchemaCtx)doLookup(name, false); 178 179 deleteServerSchema(target.attrs); 180 } catch (NameNotFoundException e) { 181 return; 182 } 183 } 184 185 // Update in-memory copy 186 super.doDestroySubcontext(name); 187 } 188 189 // Called to create oc, attr, syntax or matching rule roots and leaf entries setup(int objectType, String name, Attributes attrs)190 final LdapSchemaCtx setup(int objectType, String name, Attributes attrs) 191 throws NamingException{ 192 try { 193 setupMode = true; 194 LdapSchemaCtx answer = 195 (LdapSchemaCtx) super.doCreateSubcontext( 196 new CompositeName(name), attrs); 197 198 answer.objectType = objectType; 199 answer.setupMode = false; 200 return answer; 201 } finally { 202 setupMode = false; 203 } 204 } 205 doCreateSubcontext(Name name, Attributes attrs)206 final protected DirContext doCreateSubcontext(Name name, Attributes attrs) 207 throws NamingException { 208 209 if (attrs == null || attrs.size() == 0) { 210 throw new SchemaViolationException( 211 "Must supply attributes describing schema"); 212 } 213 214 if (!setupMode) { 215 // Update server 216 addServerSchema(attrs); 217 } 218 219 // Update in-memory copy 220 LdapSchemaCtx newEntry = 221 (LdapSchemaCtx) super.doCreateSubcontext(name, attrs); 222 return newEntry; 223 } 224 deepClone(Attributes orig)225 final private static Attributes deepClone(Attributes orig) 226 throws NamingException { 227 BasicAttributes copy = new BasicAttributes(true); 228 NamingEnumeration<? extends Attribute> attrs = orig.getAll(); 229 while (attrs.hasMore()) { 230 copy.put((Attribute)attrs.next().clone()); 231 } 232 return copy; 233 } 234 doModifyAttributes(ModificationItem[] mods)235 final protected void doModifyAttributes(ModificationItem[] mods) 236 throws NamingException { 237 if (setupMode) { 238 super.doModifyAttributes(mods); 239 } else { 240 Attributes copy = deepClone(attrs); 241 242 // Apply modifications to copy 243 applyMods(mods, copy); 244 245 // Update server copy 246 modifyServerSchema(attrs, copy); 247 248 // Update in-memory copy 249 attrs = copy; 250 } 251 } 252 253 // we override this so the superclass creates the right kind of contexts 254 // Default is to create LEAF objects; caller will change after creation 255 // if necessary createNewCtx()256 final protected HierMemDirCtx createNewCtx() { 257 LdapSchemaCtx ctx = new LdapSchemaCtx(LEAF, myEnv, info); 258 return ctx; 259 } 260 261 addServerSchema(Attributes attrs)262 final private void addServerSchema(Attributes attrs) 263 throws NamingException { 264 Attribute schemaAttr; 265 266 switch (objectType) { 267 case OBJECTCLASS_ROOT: 268 schemaAttr = info.parser.stringifyObjDesc(attrs); 269 break; 270 271 case ATTRIBUTE_ROOT: 272 schemaAttr = info.parser.stringifyAttrDesc(attrs); 273 break; 274 275 case SYNTAX_ROOT: 276 schemaAttr = info.parser.stringifySyntaxDesc(attrs); 277 break; 278 279 case MATCHRULE_ROOT: 280 schemaAttr = info.parser.stringifyMatchRuleDesc(attrs); 281 break; 282 283 case SCHEMA_ROOT: 284 throw new SchemaViolationException( 285 "Cannot create new entry under schema root"); 286 287 default: 288 throw new SchemaViolationException( 289 "Cannot create child of schema object"); 290 } 291 292 Attributes holder = new BasicAttributes(true); 293 holder.put(schemaAttr); 294 //System.err.println((String)schemaAttr.get()); 295 296 info.modifyAttributes(myEnv, DirContext.ADD_ATTRIBUTE, holder); 297 298 } 299 300 /** 301 * When we delete an entry, we use the original to make sure that 302 * any formatting inconsistencies are eliminated. 303 * This is because we're just deleting a value from an attribute 304 * on the server and there might not be any checks for extra spaces 305 * or parens. 306 */ deleteServerSchema(Attributes origAttrs)307 final private void deleteServerSchema(Attributes origAttrs) 308 throws NamingException { 309 310 Attribute origAttrVal; 311 312 switch (objectType) { 313 case OBJECTCLASS_ROOT: 314 origAttrVal = info.parser.stringifyObjDesc(origAttrs); 315 break; 316 317 case ATTRIBUTE_ROOT: 318 origAttrVal = info.parser.stringifyAttrDesc(origAttrs); 319 break; 320 321 case SYNTAX_ROOT: 322 origAttrVal = info.parser.stringifySyntaxDesc(origAttrs); 323 break; 324 325 case MATCHRULE_ROOT: 326 origAttrVal = info.parser.stringifyMatchRuleDesc(origAttrs); 327 break; 328 329 case SCHEMA_ROOT: 330 throw new SchemaViolationException( 331 "Cannot delete schema root"); 332 333 default: 334 throw new SchemaViolationException( 335 "Cannot delete child of schema object"); 336 } 337 338 ModificationItem[] mods = new ModificationItem[1]; 339 mods[0] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE, origAttrVal); 340 341 info.modifyAttributes(myEnv, mods); 342 } 343 344 /** 345 * When we modify an entry, we use the original attribute value 346 * in the schema to make sure that any formatting inconsistencies 347 * are eliminated. A modification is done by deleting the original 348 * value and adding a new value with the modification. 349 */ modifyServerSchema(Attributes origAttrs, Attributes newAttrs)350 final private void modifyServerSchema(Attributes origAttrs, 351 Attributes newAttrs) throws NamingException { 352 353 Attribute newAttrVal; 354 Attribute origAttrVal; 355 356 switch (objectType) { 357 case OBJECTCLASS: 358 origAttrVal = info.parser.stringifyObjDesc(origAttrs); 359 newAttrVal = info.parser.stringifyObjDesc(newAttrs); 360 break; 361 362 case ATTRIBUTE: 363 origAttrVal = info.parser.stringifyAttrDesc(origAttrs); 364 newAttrVal = info.parser.stringifyAttrDesc(newAttrs); 365 break; 366 367 case SYNTAX: 368 origAttrVal = info.parser.stringifySyntaxDesc(origAttrs); 369 newAttrVal = info.parser.stringifySyntaxDesc(newAttrs); 370 break; 371 372 case MATCHRULE: 373 origAttrVal = info.parser.stringifyMatchRuleDesc(origAttrs); 374 newAttrVal = info.parser.stringifyMatchRuleDesc(newAttrs); 375 break; 376 377 default: 378 throw new SchemaViolationException( 379 "Cannot modify schema root"); 380 } 381 382 ModificationItem[] mods = new ModificationItem[2]; 383 mods[0] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE, origAttrVal); 384 mods[1] = new ModificationItem(DirContext.ADD_ATTRIBUTE, newAttrVal); 385 386 info.modifyAttributes(myEnv, mods); 387 } 388 389 final static private class SchemaInfo { 390 private LdapCtx schemaEntry; 391 private String schemaEntryName; 392 LdapSchemaParser parser; 393 private String host; 394 private int port; 395 private boolean hasLdapsScheme; 396 SchemaInfo(String schemaEntryName, LdapCtx schemaEntry, LdapSchemaParser parser)397 SchemaInfo(String schemaEntryName, LdapCtx schemaEntry, 398 LdapSchemaParser parser) { 399 this.schemaEntryName = schemaEntryName; 400 this.schemaEntry = schemaEntry; 401 this.parser = parser; 402 this.port = schemaEntry.port_number; 403 this.host = schemaEntry.hostname; 404 this.hasLdapsScheme = schemaEntry.hasLdapsScheme; 405 } 406 close()407 synchronized void close() throws NamingException { 408 if (schemaEntry != null) { 409 schemaEntry.close(); 410 schemaEntry = null; 411 } 412 } 413 reopenEntry(Hashtable<?,?> env)414 private LdapCtx reopenEntry(Hashtable<?,?> env) throws NamingException { 415 // Use subschemasubentry name as DN 416 return new LdapCtx(schemaEntryName, host, port, 417 env, hasLdapsScheme); 418 } 419 modifyAttributes(Hashtable<?,?> env, ModificationItem[] mods)420 synchronized void modifyAttributes(Hashtable<?,?> env, 421 ModificationItem[] mods) 422 throws NamingException { 423 if (schemaEntry == null) { 424 schemaEntry = reopenEntry(env); 425 } 426 schemaEntry.modifyAttributes("", mods); 427 } 428 modifyAttributes(Hashtable<?,?> env, int mod, Attributes attrs)429 synchronized void modifyAttributes(Hashtable<?,?> env, int mod, 430 Attributes attrs) throws NamingException { 431 if (schemaEntry == null) { 432 schemaEntry = reopenEntry(env); 433 } 434 schemaEntry.modifyAttributes("", mod, attrs); 435 } 436 } 437 } 438