1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System.Text; 6 using System.Diagnostics; 7 using System.ComponentModel; 8 using System.Runtime.InteropServices; 9 10 namespace System.DirectoryServices.ActiveDirectory 11 { 12 internal enum SearchFlags : int 13 { 14 None = 0, 15 IsIndexed = 1, 16 IsIndexedOverContainer = 2, 17 IsInAnr = 4, 18 IsOnTombstonedObject = 8, 19 IsTupleIndexed = 32 20 } 21 22 public class ActiveDirectorySchemaProperty : IDisposable 23 { 24 // private variables 25 private DirectoryEntry _schemaEntry = null; 26 private DirectoryEntry _propertyEntry = null; 27 private DirectoryEntry _abstractPropertyEntry = null; 28 private NativeComInterfaces.IAdsProperty _iadsProperty = null; 29 private DirectoryContext _context = null; 30 internal bool isBound = false; 31 private bool _disposed = false; 32 private ActiveDirectorySchema _schema = null; 33 private bool _propertiesFromSchemaContainerInitialized = false; 34 private bool _isDefunctOnServer = false; 35 private SearchResult _propertyValuesFromServer = null; 36 37 // private variables for caching properties 38 private string _ldapDisplayName = null; 39 private string _commonName = null; 40 private string _oid = null; 41 private ActiveDirectorySyntax _syntax = (ActiveDirectorySyntax)(-1); 42 private bool _syntaxInitialized = false; 43 private string _description = null; 44 private bool _descriptionInitialized = false; 45 private bool _isSingleValued = false; 46 private bool _isSingleValuedInitialized = false; 47 private bool _isInGlobalCatalog = false; 48 private bool _isInGlobalCatalogInitialized = false; 49 private Nullable<Int32> _rangeLower = null; 50 private bool _rangeLowerInitialized = false; 51 private Nullable<Int32> _rangeUpper = null; 52 private bool _rangeUpperInitialized = false; 53 private bool _isDefunct = false; 54 private SearchFlags _searchFlags = SearchFlags.None; 55 private bool _searchFlagsInitialized = false; 56 private ActiveDirectorySchemaProperty _linkedProperty = null; 57 private bool _linkedPropertyInitialized = false; 58 private Nullable<Int32> _linkId = null; 59 private bool _linkIdInitialized = false; 60 private byte[] _schemaGuidBinaryForm = null; 61 62 // OMObjectClass values for the syntax 63 //0x2B0C0287731C00854A 64 private static OMObjectClass s_dnOMObjectClass = new OMObjectClass(new byte[] { 0x2B, 0x0C, 0x02, 0x87, 0x73, 0x1C, 0x00, 0x85, 0x4A }); 65 //0x2A864886F7140101010C 66 private static OMObjectClass s_dNWithStringOMObjectClass = new OMObjectClass(new byte[] { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x14, 0x01, 0x01, 0x01, 0x0C }); 67 //0x2A864886F7140101010B 68 private static OMObjectClass s_dNWithBinaryOMObjectClass = new OMObjectClass(new byte[] { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x14, 0x01, 0x01, 0x01, 0x0B }); 69 //0x2A864886F71401010106 70 private static OMObjectClass s_replicaLinkOMObjectClass = new OMObjectClass(new byte[] { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x14, 0x01, 0x01, 0x01, 0x06 }); 71 //0x2B0C0287731C00855C 72 private static OMObjectClass s_presentationAddressOMObjectClass = new OMObjectClass(new byte[] { 0x2B, 0x0C, 0x02, 0x87, 0x73, 0x1C, 0x00, 0x85, 0x5C }); 73 //0x2B0C0287731C00853E 74 private static OMObjectClass s_accessPointDnOMObjectClass = new OMObjectClass(new byte[] { 0x2B, 0x0C, 0x02, 0x87, 0x73, 0x1C, 0x00, 0x85, 0x3E }); 75 //0x56060102050B1D 76 private static OMObjectClass s_oRNameOMObjectClass = new OMObjectClass(new byte[] { 0x56, 0x06, 0x01, 0x02, 0x05, 0x0B, 0x1D }); 77 78 // syntaxes 79 private static int s_syntaxesCount = 23; 80 private static Syntax[] s_syntaxes = {/* CaseExactString */ new Syntax("2.5.5.3", 27, null), 81 /* CaseIgnoreString */ new Syntax("2.5.5.4", 20, null), 82 /* NumericString */ new Syntax("2.5.5.6", 18, null), 83 /* DirectoryString */ new Syntax("2.5.5.12", 64, null), 84 /* OctetString */ new Syntax("2.5.5.10", 4, null), 85 /* SecurityDescriptor */ new Syntax("2.5.5.15", 66, null), 86 /* Int */ new Syntax("2.5.5.9", 2, null), 87 /* Int64 */ new Syntax("2.5.5.16", 65, null), 88 /* Bool */ new Syntax("2.5.5.8", 1, null), 89 /* Oid */ new Syntax("2.5.5.2", 6, null), 90 /* GeneralizedTime */ new Syntax("2.5.5.11", 24, null), 91 /* UtcTime */ new Syntax("2.5.5.11", 23, null), 92 /* DN */ new Syntax("2.5.5.1", 127, s_dnOMObjectClass), 93 /* DNWithBinary */ new Syntax("2.5.5.7", 127, s_dNWithBinaryOMObjectClass), 94 /* DNWithString */ new Syntax("2.5.5.14", 127, s_dNWithStringOMObjectClass), 95 /* Enumeration */ new Syntax("2.5.5.9", 10, null), 96 /* IA5String */ new Syntax("2.5.5.5", 22, null), 97 /* PrintableString */ new Syntax("2.5.5.5", 19, null), 98 /* Sid */ new Syntax("2.5.5.17", 4, null), 99 /* AccessPointDN */ new Syntax("2.5.5.14", 127, s_accessPointDnOMObjectClass), 100 /* ORName */ new Syntax("2.5.5.7", 127, s_oRNameOMObjectClass), 101 /* PresentationAddress */ new Syntax("2.5.5.13", 127, s_presentationAddressOMObjectClass), 102 /* ReplicaLink */ new Syntax("2.5.5.10", 127, s_replicaLinkOMObjectClass)}; 103 104 #region constructors ActiveDirectorySchemaProperty(DirectoryContext context, string ldapDisplayName)105 public ActiveDirectorySchemaProperty(DirectoryContext context, string ldapDisplayName) 106 { 107 if (context == null) 108 { 109 throw new ArgumentNullException("context"); 110 } 111 112 if ((context.Name == null) && (!context.isRootDomain())) 113 { 114 throw new ArgumentException(SR.ContextNotAssociatedWithDomain, "context"); 115 } 116 117 if (context.Name != null) 118 { 119 // the target should be a valid forest name or a server 120 if (!((context.isRootDomain()) || (context.isADAMConfigSet()) || (context.isServer()))) 121 { 122 throw new ArgumentException(SR.NotADOrADAM, "context"); 123 } 124 } 125 126 if (ldapDisplayName == null) 127 { 128 throw new ArgumentNullException("ldapDisplayName"); 129 } 130 131 if (ldapDisplayName.Length == 0) 132 { 133 throw new ArgumentException(SR.EmptyStringParameter, "ldapDisplayName"); 134 } 135 136 _context = new DirectoryContext(context); 137 138 // validate the context 139 _schemaEntry = DirectoryEntryManager.GetDirectoryEntry(context, WellKnownDN.SchemaNamingContext); 140 _schemaEntry.Bind(true); 141 142 _ldapDisplayName = ldapDisplayName; 143 // common name of the property defaults to the ldap display name 144 _commonName = ldapDisplayName; 145 146 // set the bind flag 147 this.isBound = false; 148 } 149 150 // internal constructor ActiveDirectorySchemaProperty(DirectoryContext context, string ldapDisplayName, DirectoryEntry propertyEntry, DirectoryEntry schemaEntry)151 internal ActiveDirectorySchemaProperty(DirectoryContext context, string ldapDisplayName, DirectoryEntry propertyEntry, DirectoryEntry schemaEntry) 152 { 153 _context = context; 154 _ldapDisplayName = ldapDisplayName; 155 156 // common name of the property defaults to the ldap display name 157 _propertyEntry = propertyEntry; 158 _isDefunctOnServer = false; 159 _isDefunct = _isDefunctOnServer; 160 161 try 162 { 163 // initialize the directory entry for the abstract schema class 164 _abstractPropertyEntry = DirectoryEntryManager.GetDirectoryEntryInternal(context, "LDAP://" + context.GetServerName() + "/schema/" + ldapDisplayName); 165 _iadsProperty = (NativeComInterfaces.IAdsProperty)_abstractPropertyEntry.NativeObject; 166 } 167 catch (COMException e) 168 { 169 if (e.ErrorCode == unchecked((int)0x80005000)) 170 { 171 throw new ActiveDirectoryObjectNotFoundException(SR.DSNotFound, typeof(ActiveDirectorySchemaProperty), ldapDisplayName); 172 } 173 else 174 { 175 throw ExceptionHelper.GetExceptionFromCOMException(context, e); 176 } 177 } 178 catch (InvalidCastException) 179 { 180 // this means that we found an object but it is not a schema class 181 throw new ActiveDirectoryObjectNotFoundException(SR.DSNotFound, typeof(ActiveDirectorySchemaProperty), ldapDisplayName); 182 } 183 catch (ActiveDirectoryObjectNotFoundException) 184 { 185 // this is the case where the context is a config set and we could not find an ADAM instance in that config set 186 throw new ActiveDirectoryOperationException(SR.Format(SR.ADAMInstanceNotFoundInConfigSet , context.Name)); 187 } 188 189 // set the bind flag 190 this.isBound = true; 191 } 192 ActiveDirectorySchemaProperty(DirectoryContext context, string commonName, SearchResult propertyValuesFromServer, DirectoryEntry schemaEntry)193 internal ActiveDirectorySchemaProperty(DirectoryContext context, string commonName, SearchResult propertyValuesFromServer, DirectoryEntry schemaEntry) 194 { 195 _context = context; 196 _schemaEntry = schemaEntry; 197 198 // all relevant properties have already been retrieved from the server 199 _propertyValuesFromServer = propertyValuesFromServer; 200 Debug.Assert(_propertyValuesFromServer != null); 201 _propertiesFromSchemaContainerInitialized = true; 202 _propertyEntry = GetSchemaPropertyDirectoryEntry(); 203 204 // names 205 _commonName = commonName; 206 _ldapDisplayName = (string)GetValueFromCache(PropertyManager.LdapDisplayName, true); 207 208 // this constructor is only called for defunct classes 209 _isDefunctOnServer = true; 210 _isDefunct = _isDefunctOnServer; 211 212 // set the bind flag 213 this.isBound = true; 214 } 215 ActiveDirectorySchemaProperty(DirectoryContext context, string commonName, string ldapDisplayName, DirectoryEntry propertyEntry, DirectoryEntry schemaEntry)216 internal ActiveDirectorySchemaProperty(DirectoryContext context, string commonName, string ldapDisplayName, DirectoryEntry propertyEntry, DirectoryEntry schemaEntry) 217 { 218 _context = context; 219 _schemaEntry = schemaEntry; 220 _propertyEntry = propertyEntry; 221 222 // names 223 _commonName = commonName; 224 _ldapDisplayName = ldapDisplayName; 225 226 // this constructor is only called for defunct properties 227 _isDefunctOnServer = true; 228 _isDefunct = _isDefunctOnServer; 229 230 // set the bind flag 231 this.isBound = true; 232 } 233 234 #endregion constructors 235 236 #region IDisposable 237 Dispose()238 public void Dispose() 239 { 240 Dispose(true); 241 } 242 243 // private Dispose method Dispose(bool disposing)244 protected virtual void Dispose(bool disposing) 245 { 246 if (!_disposed) 247 { 248 // check if this is an explicit Dispose 249 // only then clean up the directory entries 250 if (disposing) 251 { 252 // dispose schema entry 253 if (_schemaEntry != null) 254 { 255 _schemaEntry.Dispose(); 256 _schemaEntry = null; 257 } 258 // dispose property entry 259 if (_propertyEntry != null) 260 { 261 _propertyEntry.Dispose(); 262 _propertyEntry = null; 263 } 264 // dispose abstract class entry 265 if (_abstractPropertyEntry != null) 266 { 267 _abstractPropertyEntry.Dispose(); 268 _abstractPropertyEntry = null; 269 } 270 // dispose the schema object 271 if (_schema != null) 272 { 273 _schema.Dispose(); 274 } 275 } 276 277 _disposed = true; 278 } 279 } 280 #endregion IDisposable 281 282 #region public methods FindByName(DirectoryContext context, string ldapDisplayName)283 public static ActiveDirectorySchemaProperty FindByName(DirectoryContext context, string ldapDisplayName) 284 { 285 ActiveDirectorySchemaProperty schemaProperty = null; 286 287 if (context == null) 288 { 289 throw new ArgumentNullException("context"); 290 } 291 292 if ((context.Name == null) && (!context.isRootDomain())) 293 { 294 throw new ArgumentException(SR.ContextNotAssociatedWithDomain, "context"); 295 } 296 297 if (context.Name != null) 298 { 299 // the target should be a valid forest name or a server 300 if (!((context.isRootDomain()) || (context.isADAMConfigSet()) || (context.isServer()))) 301 { 302 throw new ArgumentException(SR.NotADOrADAM, "context"); 303 } 304 } 305 306 if (ldapDisplayName == null) 307 { 308 throw new ArgumentNullException("ldapDisplayName"); 309 } 310 311 if (ldapDisplayName.Length == 0) 312 { 313 throw new ArgumentException(SR.EmptyStringParameter, "ldapDisplayName"); 314 } 315 316 // work with copy of the context 317 context = new DirectoryContext(context); 318 319 // create a schema property 320 schemaProperty = new ActiveDirectorySchemaProperty(context, ldapDisplayName, (DirectoryEntry)null, null); 321 322 return schemaProperty; 323 } 324 Save()325 public void Save() 326 { 327 CheckIfDisposed(); 328 329 if (!isBound) 330 { 331 try 332 { 333 // create a new directory entry for this class 334 if (_schemaEntry == null) 335 { 336 _schemaEntry = DirectoryEntryManager.GetDirectoryEntry(_context, WellKnownDN.SchemaNamingContext); 337 } 338 339 // this will create the class and set the CN value 340 string rdn = "CN=" + _commonName; 341 rdn = Utils.GetEscapedPath(rdn); 342 _propertyEntry = _schemaEntry.Children.Add(rdn, "attributeSchema"); 343 } 344 catch (COMException e) 345 { 346 throw ExceptionHelper.GetExceptionFromCOMException(_context, e); 347 } 348 catch (ActiveDirectoryObjectNotFoundException) 349 { 350 // this is the case where the context is a config set and we could not find an ADAM instance in that config set 351 throw new ActiveDirectoryOperationException(SR.Format(SR.ADAMInstanceNotFoundInConfigSet , _context.Name)); 352 } 353 354 // set the ldap display name property 355 SetProperty(PropertyManager.LdapDisplayName, _ldapDisplayName); 356 357 // set the oid value 358 SetProperty(PropertyManager.AttributeID, _oid); 359 360 // set the syntax 361 if (_syntax != (ActiveDirectorySyntax)(-1)) 362 { 363 SetSyntax(_syntax); 364 } 365 366 // set the description 367 SetProperty(PropertyManager.Description, _description); 368 369 // set the isSingleValued attribute 370 _propertyEntry.Properties[PropertyManager.IsSingleValued].Value = _isSingleValued; 371 372 // set the isGlobalCatalogReplicated attribute 373 _propertyEntry.Properties[PropertyManager.IsMemberOfPartialAttributeSet].Value = _isInGlobalCatalog; 374 375 // set the isDefunct attribute 376 _propertyEntry.Properties[PropertyManager.IsDefunct].Value = _isDefunct; 377 378 // set the range lower attribute 379 if (_rangeLower != null) 380 { 381 _propertyEntry.Properties[PropertyManager.RangeLower].Value = (int)_rangeLower.Value; 382 } 383 384 // set the range upper attribute 385 if (_rangeUpper != null) 386 { 387 _propertyEntry.Properties[PropertyManager.RangeUpper].Value = (int)_rangeUpper.Value; 388 } 389 390 // set the searchFlags attribute 391 if (_searchFlags != SearchFlags.None) 392 { 393 _propertyEntry.Properties[PropertyManager.SearchFlags].Value = (int)_searchFlags; 394 } 395 396 // set the link id 397 if (_linkId != null) 398 { 399 _propertyEntry.Properties[PropertyManager.LinkID].Value = (int)_linkId.Value; 400 } 401 402 // set the schemaIDGuid property 403 if (_schemaGuidBinaryForm != null) 404 { 405 SetProperty(PropertyManager.SchemaIDGuid, _schemaGuidBinaryForm); 406 } 407 } 408 409 try 410 { 411 // commit the classEntry to server 412 _propertyEntry.CommitChanges(); 413 414 // Refresh the schema cache on the schema role owner 415 if (_schema == null) 416 { 417 ActiveDirectorySchema schemaObject = ActiveDirectorySchema.GetSchema(_context); 418 bool alreadyUsingSchemaRoleOwnerContext = false; 419 DirectoryServer schemaRoleOwner = null; 420 421 try 422 { 423 // 424 // if we are not already talking to the schema role owner, change the context 425 // 426 schemaRoleOwner = schemaObject.SchemaRoleOwner; 427 if (Utils.Compare(schemaRoleOwner.Name, _context.GetServerName()) != 0) 428 { 429 DirectoryContext schemaRoleOwnerContext = Utils.GetNewDirectoryContext(schemaRoleOwner.Name, DirectoryContextType.DirectoryServer, _context); 430 _schema = ActiveDirectorySchema.GetSchema(schemaRoleOwnerContext); 431 } 432 else 433 { 434 alreadyUsingSchemaRoleOwnerContext = true; 435 _schema = schemaObject; 436 } 437 } 438 finally 439 { 440 if (schemaRoleOwner != null) 441 { 442 schemaRoleOwner.Dispose(); 443 } 444 if (!alreadyUsingSchemaRoleOwnerContext) 445 { 446 schemaObject.Dispose(); 447 } 448 } 449 } 450 _schema.RefreshSchema(); 451 } 452 catch (COMException e) 453 { 454 throw ExceptionHelper.GetExceptionFromCOMException(_context, e); 455 } 456 457 // now that the changes are committed to the server 458 // update the defunct/non-defunct status of the class on the server 459 _isDefunctOnServer = _isDefunct; 460 461 // invalidate all properties 462 _commonName = null; 463 _oid = null; 464 _syntaxInitialized = false; 465 _descriptionInitialized = false; 466 _isSingleValuedInitialized = false; 467 _isInGlobalCatalogInitialized = false; 468 _rangeLowerInitialized = false; 469 _rangeUpperInitialized = false; 470 _searchFlagsInitialized = false; 471 _linkedPropertyInitialized = false; 472 _linkIdInitialized = false; 473 _schemaGuidBinaryForm = null; 474 _propertiesFromSchemaContainerInitialized = false; 475 476 // set bind flag 477 isBound = true; 478 } 479 ToString()480 public override string ToString() => Name; 481 GetDirectoryEntry()482 public DirectoryEntry GetDirectoryEntry() 483 { 484 CheckIfDisposed(); 485 486 if (!isBound) 487 { 488 throw new InvalidOperationException(SR.CannotGetObject); 489 } 490 491 GetSchemaPropertyDirectoryEntry(); 492 Debug.Assert(_propertyEntry != null); 493 494 return DirectoryEntryManager.GetDirectoryEntryInternal(_context, _propertyEntry.Path); 495 } 496 #endregion public methods 497 498 #region public properties 499 500 public string Name 501 { 502 get 503 { 504 CheckIfDisposed(); 505 return _ldapDisplayName; 506 } 507 } 508 509 public string CommonName 510 { 511 get 512 { 513 CheckIfDisposed(); 514 515 if (isBound) 516 { 517 if (_commonName == null) 518 { 519 // get the property from the server 520 _commonName = (string)GetValueFromCache(PropertyManager.Cn, true); 521 } 522 } 523 return _commonName; 524 } 525 set 526 { 527 CheckIfDisposed(); 528 529 if (value != null && value.Length == 0) 530 throw new ArgumentException(SR.EmptyStringParameter, "value"); 531 532 if (isBound) 533 { 534 // set the value on the directory entry 535 SetProperty(PropertyManager.Cn, value); 536 } 537 _commonName = value; 538 } 539 } 540 541 public string Oid 542 { 543 get 544 { 545 CheckIfDisposed(); 546 547 if (isBound) 548 { 549 if (_oid == null) 550 { 551 // get the property from the abstract schema/ schema container 552 // (for non-defunt classes this property is available in the abstract schema) 553 if (!_isDefunctOnServer) 554 { 555 try 556 { 557 _oid = _iadsProperty.OID; 558 } 559 catch (COMException e) 560 { 561 throw ExceptionHelper.GetExceptionFromCOMException(_context, e); 562 } 563 } 564 else 565 { 566 _oid = (string)GetValueFromCache(PropertyManager.AttributeID, true); 567 } 568 } 569 } 570 return _oid; 571 } 572 set 573 { 574 CheckIfDisposed(); 575 576 if (value != null && value.Length == 0) 577 throw new ArgumentException(SR.EmptyStringParameter, "value"); 578 579 if (isBound) 580 { 581 // set the value on the directory entry 582 SetProperty(PropertyManager.AttributeID, value); 583 } 584 _oid = value; 585 } 586 } 587 588 public ActiveDirectorySyntax Syntax 589 { 590 get 591 { 592 CheckIfDisposed(); 593 594 if (isBound) 595 { 596 if (!_syntaxInitialized) 597 { 598 byte[] omObjectClassBinaryForm = (byte[])GetValueFromCache(PropertyManager.OMObjectClass, false); 599 OMObjectClass omObjectClass = (omObjectClassBinaryForm != null) ? new OMObjectClass(omObjectClassBinaryForm) : null; 600 601 _syntax = MapSyntax((string)GetValueFromCache(PropertyManager.AttributeSyntax, true), 602 (int)GetValueFromCache(PropertyManager.OMSyntax, true), 603 omObjectClass); 604 _syntaxInitialized = true; 605 } 606 } 607 return _syntax; 608 } 609 set 610 { 611 CheckIfDisposed(); 612 613 if (value < ActiveDirectorySyntax.CaseExactString || value > ActiveDirectorySyntax.ReplicaLink) 614 { 615 throw new InvalidEnumArgumentException("value", (int)value, typeof(ActiveDirectorySyntax)); 616 } 617 618 if (isBound) 619 { 620 // set the value on the directory entry 621 SetSyntax(value); 622 } 623 _syntax = value; 624 } 625 } 626 627 public string Description 628 { 629 get 630 { 631 CheckIfDisposed(); 632 633 if (isBound) 634 { 635 if (!_descriptionInitialized) 636 { 637 // get the property from the server 638 _description = (string)GetValueFromCache(PropertyManager.Description, false); 639 _descriptionInitialized = true; 640 } 641 } 642 return _description; 643 } 644 set 645 { 646 CheckIfDisposed(); 647 648 if (value != null && value.Length == 0) 649 throw new ArgumentException(SR.EmptyStringParameter, "value"); 650 651 if (isBound) 652 { 653 // set the value on the directory entry 654 SetProperty(PropertyManager.Description, value); 655 } 656 _description = value; 657 } 658 } 659 660 public bool IsSingleValued 661 { 662 get 663 { 664 CheckIfDisposed(); 665 666 if (isBound) 667 { 668 if (!_isSingleValuedInitialized) 669 { 670 // get the property from the abstract schema/ schema container 671 // (for non-defunt classes this property is available in the abstract schema) 672 if (!_isDefunctOnServer) 673 { 674 try 675 { 676 _isSingleValued = !_iadsProperty.MultiValued; 677 } 678 catch (COMException e) 679 { 680 throw ExceptionHelper.GetExceptionFromCOMException(_context, e); 681 } 682 } 683 else 684 { 685 _isSingleValued = (bool)GetValueFromCache(PropertyManager.IsSingleValued, true); 686 } 687 _isSingleValuedInitialized = true; 688 } 689 } 690 return _isSingleValued; 691 } 692 set 693 { 694 CheckIfDisposed(); 695 696 if (isBound) 697 { 698 // get the distinguished name to construct the directory entry 699 GetSchemaPropertyDirectoryEntry(); 700 Debug.Assert(_propertyEntry != null); 701 702 // set the value on the directory entry 703 _propertyEntry.Properties[PropertyManager.IsSingleValued].Value = value; 704 } 705 _isSingleValued = value; 706 } 707 } 708 709 public bool IsIndexed 710 { 711 get 712 { 713 CheckIfDisposed(); 714 715 return IsSetInSearchFlags(SearchFlags.IsIndexed); 716 } 717 set 718 { 719 CheckIfDisposed(); 720 721 if (value) 722 { 723 SetBitInSearchFlags(SearchFlags.IsIndexed); 724 } 725 else 726 { 727 ResetBitInSearchFlags(SearchFlags.IsIndexed); 728 } 729 } 730 } 731 732 public bool IsIndexedOverContainer 733 { 734 get 735 { 736 CheckIfDisposed(); 737 738 return IsSetInSearchFlags(SearchFlags.IsIndexedOverContainer); 739 } 740 set 741 { 742 CheckIfDisposed(); 743 744 if (value) 745 { 746 SetBitInSearchFlags(SearchFlags.IsIndexedOverContainer); 747 } 748 else 749 { 750 ResetBitInSearchFlags(SearchFlags.IsIndexedOverContainer); 751 } 752 } 753 } 754 755 public bool IsInAnr 756 { 757 get 758 { 759 CheckIfDisposed(); 760 761 return IsSetInSearchFlags(SearchFlags.IsInAnr); 762 } 763 set 764 { 765 CheckIfDisposed(); 766 767 if (value) 768 { 769 SetBitInSearchFlags(SearchFlags.IsInAnr); 770 } 771 else 772 { 773 ResetBitInSearchFlags(SearchFlags.IsInAnr); 774 } 775 } 776 } 777 778 public bool IsOnTombstonedObject 779 { 780 get 781 { 782 CheckIfDisposed(); 783 784 return IsSetInSearchFlags(SearchFlags.IsOnTombstonedObject); 785 } 786 set 787 { 788 CheckIfDisposed(); 789 790 if (value) 791 { 792 SetBitInSearchFlags(SearchFlags.IsOnTombstonedObject); 793 } 794 else 795 { 796 ResetBitInSearchFlags(SearchFlags.IsOnTombstonedObject); 797 } 798 } 799 } 800 801 public bool IsTupleIndexed 802 { 803 get 804 { 805 CheckIfDisposed(); 806 807 return IsSetInSearchFlags(SearchFlags.IsTupleIndexed); 808 } 809 set 810 { 811 CheckIfDisposed(); 812 813 if (value) 814 { 815 SetBitInSearchFlags(SearchFlags.IsTupleIndexed); 816 } 817 else 818 { 819 ResetBitInSearchFlags(SearchFlags.IsTupleIndexed); 820 } 821 } 822 } 823 824 public bool IsInGlobalCatalog 825 { 826 get 827 { 828 CheckIfDisposed(); 829 830 if (isBound) 831 { 832 if (!_isInGlobalCatalogInitialized) 833 { 834 // get the property from the server 835 object value = GetValueFromCache(PropertyManager.IsMemberOfPartialAttributeSet, false); 836 _isInGlobalCatalog = (value != null) ? (bool)value : false; 837 _isInGlobalCatalogInitialized = true; 838 } 839 } 840 return _isInGlobalCatalog; 841 } 842 set 843 { 844 CheckIfDisposed(); 845 846 if (isBound) 847 { 848 // get the distinguished name to construct the directory entry 849 GetSchemaPropertyDirectoryEntry(); 850 Debug.Assert(_propertyEntry != null); 851 852 // set the value on the directory entry 853 _propertyEntry.Properties[PropertyManager.IsMemberOfPartialAttributeSet].Value = value; 854 } 855 _isInGlobalCatalog = value; 856 } 857 } 858 859 public Nullable<Int32> RangeLower 860 { 861 get 862 { 863 CheckIfDisposed(); 864 865 if (isBound) 866 { 867 if (!_rangeLowerInitialized) 868 { 869 // get the property from the server 870 // if the property is not set then we will return null 871 object value = GetValueFromCache(PropertyManager.RangeLower, false); 872 if (value == null) 873 { 874 _rangeLower = null; 875 } 876 else 877 { 878 _rangeLower = (int)value; 879 } 880 _rangeLowerInitialized = true; 881 } 882 } 883 return _rangeLower; 884 } 885 set 886 { 887 CheckIfDisposed(); 888 889 if (isBound) 890 { 891 // get the distinguished name to construct the directory entry 892 GetSchemaPropertyDirectoryEntry(); 893 Debug.Assert(_propertyEntry != null); 894 895 // set the value on the directory entry 896 if (value == null) 897 { 898 if (_propertyEntry.Properties.Contains(PropertyManager.RangeLower)) 899 { 900 _propertyEntry.Properties[PropertyManager.RangeLower].Clear(); 901 } 902 } 903 else 904 { 905 _propertyEntry.Properties[PropertyManager.RangeLower].Value = (int)value.Value; 906 } 907 } 908 _rangeLower = value; 909 } 910 } 911 912 public Nullable<Int32> RangeUpper 913 { 914 get 915 { 916 CheckIfDisposed(); 917 918 if (isBound) 919 { 920 if (!_rangeUpperInitialized) 921 { 922 // get the property from the server 923 // if the property is not set then we will return null 924 object value = GetValueFromCache(PropertyManager.RangeUpper, false); 925 if (value == null) 926 { 927 _rangeUpper = null; 928 } 929 else 930 { 931 _rangeUpper = (int)value; 932 } 933 _rangeUpperInitialized = true; 934 } 935 } 936 return _rangeUpper; 937 } 938 set 939 { 940 CheckIfDisposed(); 941 942 if (isBound) 943 { 944 // get the distinguished name to construct the directory entry 945 GetSchemaPropertyDirectoryEntry(); 946 Debug.Assert(_propertyEntry != null); 947 948 // set the value on the directory entry 949 if (value == null) 950 { 951 if (_propertyEntry.Properties.Contains(PropertyManager.RangeUpper)) 952 { 953 _propertyEntry.Properties[PropertyManager.RangeUpper].Clear(); 954 } 955 } 956 else 957 { 958 _propertyEntry.Properties[PropertyManager.RangeUpper].Value = (int)value.Value; 959 } 960 } 961 _rangeUpper = value; 962 } 963 } 964 965 public bool IsDefunct 966 { 967 get 968 { 969 CheckIfDisposed(); 970 // this is initialized for bound properties in the constructor 971 return _isDefunct; 972 } 973 set 974 { 975 CheckIfDisposed(); 976 977 if (isBound) 978 { 979 // set the value on the directory entry 980 SetProperty(PropertyManager.IsDefunct, value); 981 } 982 _isDefunct = value; 983 } 984 } 985 986 public ActiveDirectorySchemaProperty Link 987 { 988 get 989 { 990 CheckIfDisposed(); 991 992 if (isBound) 993 { 994 if (!_linkedPropertyInitialized) 995 { 996 object value = GetValueFromCache(PropertyManager.LinkID, false); 997 int tempLinkId = (value != null) ? (int)value : -1; 998 999 if (tempLinkId != -1) 1000 { 1001 int linkIdToSearch = tempLinkId - 2 * (tempLinkId % 2) + 1; 1002 1003 try 1004 { 1005 if (_schemaEntry == null) 1006 { 1007 _schemaEntry = DirectoryEntryManager.GetDirectoryEntry(_context, WellKnownDN.SchemaNamingContext); 1008 } 1009 1010 string filter = "(&(" + PropertyManager.ObjectCategory + "=attributeSchema)" + "(" + PropertyManager.LinkID + "=" + linkIdToSearch + "))"; 1011 ReadOnlyActiveDirectorySchemaPropertyCollection linkedProperties = ActiveDirectorySchema.GetAllProperties(_context, _schemaEntry, filter); 1012 1013 if (linkedProperties.Count != 1) 1014 { 1015 throw new ActiveDirectoryObjectNotFoundException(SR.Format(SR.LinkedPropertyNotFound , linkIdToSearch), typeof(ActiveDirectorySchemaProperty), null); 1016 } 1017 1018 _linkedProperty = linkedProperties[0]; 1019 } 1020 catch (COMException e) 1021 { 1022 throw ExceptionHelper.GetExceptionFromCOMException(_context, e); 1023 } 1024 } 1025 _linkedPropertyInitialized = true; 1026 } 1027 } 1028 return _linkedProperty; 1029 } 1030 } 1031 1032 public Nullable<Int32> LinkId 1033 { 1034 get 1035 { 1036 CheckIfDisposed(); 1037 1038 if (isBound) 1039 { 1040 if (!_linkIdInitialized) 1041 { 1042 object value = GetValueFromCache(PropertyManager.LinkID, false); 1043 // if the property was not set we will return null 1044 if (value == null) 1045 { 1046 _linkId = null; 1047 } 1048 else 1049 { 1050 _linkId = (int)value; 1051 } 1052 _linkIdInitialized = true; 1053 } 1054 } 1055 return _linkId; 1056 } 1057 set 1058 { 1059 CheckIfDisposed(); 1060 1061 if (isBound) 1062 { 1063 // get the distinguished name to construct the directory entry 1064 GetSchemaPropertyDirectoryEntry(); 1065 Debug.Assert(_propertyEntry != null); 1066 1067 // set the value on the directory entry 1068 if (value == null) 1069 { 1070 if (_propertyEntry.Properties.Contains(PropertyManager.LinkID)) 1071 { 1072 _propertyEntry.Properties[PropertyManager.LinkID].Clear(); 1073 } 1074 } 1075 else 1076 { 1077 _propertyEntry.Properties[PropertyManager.LinkID].Value = (int)value.Value; 1078 } 1079 } 1080 _linkId = value; 1081 } 1082 } 1083 1084 public Guid SchemaGuid 1085 { 1086 get 1087 { 1088 CheckIfDisposed(); 1089 1090 Guid schemaGuid = Guid.Empty; 1091 1092 if (isBound) 1093 { 1094 if (_schemaGuidBinaryForm == null) 1095 { 1096 // get the property from the server 1097 _schemaGuidBinaryForm = (byte[])GetValueFromCache(PropertyManager.SchemaIDGuid, true); 1098 } 1099 } 1100 1101 // we cache the byte array and create a new guid each time 1102 return new Guid(_schemaGuidBinaryForm); 1103 } 1104 set 1105 { 1106 CheckIfDisposed(); 1107 1108 if (isBound) 1109 { 1110 // set the value on the directory entry 1111 SetProperty(PropertyManager.SchemaIDGuid, (value.Equals(Guid.Empty)) ? null : value.ToByteArray()); 1112 } 1113 _schemaGuidBinaryForm = (value.Equals(Guid.Empty)) ? null : value.ToByteArray(); 1114 } 1115 } 1116 1117 #endregion public properties 1118 1119 #region private methods 1120 CheckIfDisposed()1121 private void CheckIfDisposed() 1122 { 1123 if (_disposed) 1124 { 1125 throw new ObjectDisposedException(GetType().Name); 1126 } 1127 } 1128 1129 // 1130 // This method retrieves the value of a property (single valued) from the values 1131 // that were retrieved from the server. The "mustExist" parameter controls whether or 1132 // not an exception should be thrown if a value does not exist. If mustExist is true, this 1133 // will throw an exception is value does not exist. 1134 // GetValueFromCache(string propertyName, bool mustExist)1135 private object GetValueFromCache(string propertyName, bool mustExist) 1136 { 1137 object value = null; 1138 1139 // retrieve the properties from the server if necessary 1140 InitializePropertiesFromSchemaContainer(); 1141 1142 Debug.Assert(_propertyValuesFromServer != null); 1143 1144 ResultPropertyValueCollection propertyValues = null; 1145 try 1146 { 1147 propertyValues = _propertyValuesFromServer.Properties[propertyName]; 1148 if ((propertyValues == null) || (propertyValues.Count < 1)) 1149 { 1150 if (mustExist) 1151 { 1152 throw new ActiveDirectoryOperationException(SR.Format(SR.PropertyNotFound , propertyName)); 1153 } 1154 } 1155 else 1156 { 1157 value = propertyValues[0]; 1158 } 1159 } 1160 catch (COMException e) 1161 { 1162 throw ExceptionHelper.GetExceptionFromCOMException(_context, e); 1163 } 1164 1165 return value; 1166 } 1167 1168 // 1169 // Just calls the static method GetPropertiesFromSchemaContainer with the correct context 1170 // InitializePropertiesFromSchemaContainer()1171 private void InitializePropertiesFromSchemaContainer() 1172 { 1173 if (!_propertiesFromSchemaContainerInitialized) 1174 { 1175 if (_schemaEntry == null) 1176 { 1177 _schemaEntry = DirectoryEntryManager.GetDirectoryEntry(_context, WellKnownDN.SchemaNamingContext); 1178 } 1179 1180 _propertyValuesFromServer = GetPropertiesFromSchemaContainer(_context, _schemaEntry, (_isDefunctOnServer) ? _commonName : _ldapDisplayName, _isDefunctOnServer); 1181 _propertiesFromSchemaContainerInitialized = true; 1182 } 1183 } 1184 1185 // 1186 // This method retrieves properties for this schema class from the schema container 1187 // on the server. For non-defunct classes only properties that are not available in the abstract 1188 // schema are retrieved. For defunct classes, all the properties are retrieved. 1189 // The retrieved values are stored in a class variable "propertyValuesFromServer" which is a 1190 // hashtable indexed on the property name. 1191 // GetPropertiesFromSchemaContainer(DirectoryContext context, DirectoryEntry schemaEntry, string name, bool isDefunctOnServer)1192 internal static SearchResult GetPropertiesFromSchemaContainer(DirectoryContext context, DirectoryEntry schemaEntry, string name, bool isDefunctOnServer) 1193 { 1194 SearchResult propertyValuesFromServer = null; 1195 1196 // 1197 // The properties that are loaded from the schemaContainer for non-defunct classes: 1198 // DistinguishedName 1199 // CommonName 1200 // Syntax - AttributeSyntax, OMSyntax, OMObjectClass 1201 // Description 1202 // IsIndexed, IsIndexedOverContainer, IsInAnr, IsOnTombstonedObject, IsTupleIndexed - SearchFlags 1203 // IsInGlobalCatalog - IsMemberOfPartialAttributeSet 1204 // LinkId (Link) 1205 // SchemaGuid - SchemaIdGuid 1206 // RangeLower 1207 // RangeUpper 1208 1209 // 1210 // For defunct class we also load teh remaining properties 1211 // LdapDisplayName 1212 // Oid 1213 // IsSingleValued 1214 // 1215 1216 // build the filter 1217 StringBuilder str = new StringBuilder(15); 1218 str.Append("(&("); 1219 str.Append(PropertyManager.ObjectCategory); 1220 str.Append("=attributeSchema)"); 1221 str.Append("("); 1222 if (!isDefunctOnServer) 1223 { 1224 str.Append(PropertyManager.LdapDisplayName); 1225 } 1226 else 1227 { 1228 str.Append(PropertyManager.Cn); 1229 } 1230 str.Append("="); 1231 str.Append(Utils.GetEscapedFilterValue(name)); 1232 str.Append(")"); 1233 if (!isDefunctOnServer) 1234 { 1235 str.Append("(!("); 1236 } 1237 else 1238 { 1239 str.Append("("); 1240 } 1241 str.Append(PropertyManager.IsDefunct); 1242 if (!isDefunctOnServer) 1243 { 1244 str.Append("=TRUE)))"); 1245 } 1246 else 1247 { 1248 str.Append("=TRUE))"); 1249 } 1250 1251 string[] propertiesToLoad = null; 1252 if (!isDefunctOnServer) 1253 { 1254 propertiesToLoad = new string[12]; 1255 1256 propertiesToLoad[0] = PropertyManager.DistinguishedName; 1257 propertiesToLoad[1] = PropertyManager.Cn; 1258 propertiesToLoad[2] = PropertyManager.AttributeSyntax; 1259 propertiesToLoad[3] = PropertyManager.OMSyntax; 1260 propertiesToLoad[4] = PropertyManager.OMObjectClass; 1261 propertiesToLoad[5] = PropertyManager.Description; 1262 propertiesToLoad[6] = PropertyManager.SearchFlags; 1263 propertiesToLoad[7] = PropertyManager.IsMemberOfPartialAttributeSet; 1264 propertiesToLoad[8] = PropertyManager.LinkID; 1265 propertiesToLoad[9] = PropertyManager.SchemaIDGuid; 1266 propertiesToLoad[10] = PropertyManager.RangeLower; 1267 propertiesToLoad[11] = PropertyManager.RangeUpper; 1268 } 1269 else 1270 { 1271 propertiesToLoad = new string[15]; 1272 1273 propertiesToLoad[0] = PropertyManager.DistinguishedName; 1274 propertiesToLoad[1] = PropertyManager.Cn; 1275 propertiesToLoad[2] = PropertyManager.AttributeSyntax; 1276 propertiesToLoad[3] = PropertyManager.OMSyntax; 1277 propertiesToLoad[4] = PropertyManager.OMObjectClass; 1278 propertiesToLoad[5] = PropertyManager.Description; 1279 propertiesToLoad[6] = PropertyManager.SearchFlags; 1280 propertiesToLoad[7] = PropertyManager.IsMemberOfPartialAttributeSet; 1281 propertiesToLoad[8] = PropertyManager.LinkID; 1282 propertiesToLoad[9] = PropertyManager.SchemaIDGuid; 1283 propertiesToLoad[10] = PropertyManager.AttributeID; 1284 propertiesToLoad[11] = PropertyManager.IsSingleValued; 1285 propertiesToLoad[12] = PropertyManager.RangeLower; 1286 propertiesToLoad[13] = PropertyManager.RangeUpper; 1287 propertiesToLoad[14] = PropertyManager.LdapDisplayName; 1288 } 1289 1290 // 1291 // Get all the values (don't need to use range retrieval as there are no multivalued attributes) 1292 // 1293 ADSearcher searcher = new ADSearcher(schemaEntry, str.ToString(), propertiesToLoad, SearchScope.OneLevel, false /* paged search */, false /* cache results */); 1294 1295 try 1296 { 1297 propertyValuesFromServer = searcher.FindOne(); 1298 } 1299 catch (COMException e) 1300 { 1301 if (e.ErrorCode == unchecked((int)0x80072030)) 1302 { 1303 // object is not found since we cannot even find the container in which to search 1304 throw new ActiveDirectoryObjectNotFoundException(SR.DSNotFound, typeof(ActiveDirectorySchemaProperty), name); 1305 } 1306 else 1307 { 1308 throw ExceptionHelper.GetExceptionFromCOMException(context, e); 1309 } 1310 } 1311 1312 if (propertyValuesFromServer == null) 1313 { 1314 throw new ActiveDirectoryObjectNotFoundException(SR.DSNotFound, typeof(ActiveDirectorySchemaProperty), name); 1315 } 1316 1317 return propertyValuesFromServer; 1318 } 1319 GetSchemaPropertyDirectoryEntry()1320 internal DirectoryEntry GetSchemaPropertyDirectoryEntry() 1321 { 1322 if (_propertyEntry == null) 1323 { 1324 InitializePropertiesFromSchemaContainer(); 1325 _propertyEntry = DirectoryEntryManager.GetDirectoryEntry(_context, (string)GetValueFromCache(PropertyManager.DistinguishedName, true)); 1326 } 1327 1328 return _propertyEntry; 1329 } 1330 1331 /// 1332 /// <summary> 1333 /// Initializes the search flags attribute value i.e. fetches it from 1334 /// the directory, if this object is bound. 1335 /// </summary> 1336 /// InitializeSearchFlags()1337 private void InitializeSearchFlags() 1338 { 1339 if (isBound) 1340 { 1341 if (!_searchFlagsInitialized) 1342 { 1343 object value = GetValueFromCache(PropertyManager.SearchFlags, false); 1344 1345 if (value != null) 1346 { 1347 _searchFlags = (SearchFlags)((int)value); 1348 } 1349 _searchFlagsInitialized = true; 1350 } 1351 } 1352 } 1353 IsSetInSearchFlags(SearchFlags searchFlagBit)1354 private bool IsSetInSearchFlags(SearchFlags searchFlagBit) 1355 { 1356 InitializeSearchFlags(); 1357 return (((int)_searchFlags & (int)searchFlagBit) != 0); 1358 } 1359 SetBitInSearchFlags(SearchFlags searchFlagBit)1360 private void SetBitInSearchFlags(SearchFlags searchFlagBit) 1361 { 1362 InitializeSearchFlags(); 1363 _searchFlags = (SearchFlags)((int)_searchFlags | (int)searchFlagBit); 1364 1365 if (isBound) 1366 { 1367 // get the distinguished name to construct the directory entry 1368 GetSchemaPropertyDirectoryEntry(); 1369 Debug.Assert(_propertyEntry != null); 1370 1371 // set the value on the directory entry 1372 _propertyEntry.Properties[PropertyManager.SearchFlags].Value = (int)_searchFlags; 1373 } 1374 } 1375 ResetBitInSearchFlags(SearchFlags searchFlagBit)1376 private void ResetBitInSearchFlags(SearchFlags searchFlagBit) 1377 { 1378 InitializeSearchFlags(); 1379 _searchFlags = (SearchFlags)((int)_searchFlags & ~((int)searchFlagBit)); 1380 1381 if (isBound) 1382 { 1383 // get the distinguished name to construct the directory entry 1384 GetSchemaPropertyDirectoryEntry(); 1385 Debug.Assert(_propertyEntry != null); 1386 1387 // set the value on the directory entry 1388 _propertyEntry.Properties[PropertyManager.SearchFlags].Value = (int)_searchFlags; 1389 } 1390 } 1391 SetProperty(string propertyName, object value)1392 private void SetProperty(string propertyName, object value) 1393 { 1394 // get the distinguished name to construct the directory entry 1395 GetSchemaPropertyDirectoryEntry(); 1396 Debug.Assert(_propertyEntry != null); 1397 1398 if (value == null) 1399 { 1400 if (_propertyEntry.Properties.Contains(propertyName)) 1401 { 1402 _propertyEntry.Properties[propertyName].Clear(); 1403 } 1404 } 1405 else 1406 { 1407 _propertyEntry.Properties[propertyName].Value = value; 1408 } 1409 } 1410 MapSyntax(string syntaxId, int oMID, OMObjectClass oMObjectClass)1411 private ActiveDirectorySyntax MapSyntax(string syntaxId, int oMID, OMObjectClass oMObjectClass) 1412 { 1413 for (int i = 0; i < s_syntaxesCount; i++) 1414 { 1415 if (s_syntaxes[i].Equals(new Syntax(syntaxId, oMID, oMObjectClass))) 1416 { 1417 return (ActiveDirectorySyntax)i; 1418 } 1419 } 1420 throw new ActiveDirectoryOperationException(SR.Format(SR.UnknownSyntax , _ldapDisplayName)); 1421 } 1422 SetSyntax(ActiveDirectorySyntax syntax)1423 private void SetSyntax(ActiveDirectorySyntax syntax) 1424 { 1425 if ((((int)syntax) < 0) || (((int)syntax) > (s_syntaxesCount - 1))) 1426 { 1427 throw new InvalidEnumArgumentException("syntax", (int)syntax, typeof(ActiveDirectorySyntax)); 1428 } 1429 1430 // get the distinguished name to construct the directory entry 1431 GetSchemaPropertyDirectoryEntry(); 1432 Debug.Assert(_propertyEntry != null); 1433 1434 _propertyEntry.Properties[PropertyManager.AttributeSyntax].Value = s_syntaxes[(int)syntax].attributeSyntax; 1435 _propertyEntry.Properties[PropertyManager.OMSyntax].Value = s_syntaxes[(int)syntax].oMSyntax; 1436 OMObjectClass oMObjectClass = s_syntaxes[(int)syntax].oMObjectClass; 1437 if (oMObjectClass != null) 1438 { 1439 _propertyEntry.Properties[PropertyManager.OMObjectClass].Value = oMObjectClass.Data; 1440 } 1441 } 1442 #endregion private methods 1443 } 1444 } 1445