public void MergeAddToExistingAttribute() { UpdateImpl up = new UpdateImpl(null, null); ICollection <ConnectorAttribute> actual; ICollection <ConnectorAttribute> baseAttrs = CollectionUtil.NewSet <ConnectorAttribute>(); ICollection <ConnectorAttribute> expected = CollectionUtil.NewSet <ConnectorAttribute>(); ICollection <ConnectorAttribute> changeset = CollectionUtil.NewSet <ConnectorAttribute>(); // attempt to add a value to an attribute.. ConnectorAttribute battr = ConnectorAttributeBuilder.Build("abc", 1); ConnectorAttribute cattr = ConnectorAttributeBuilder.Build("abc", 2); baseAttrs.Add(battr); changeset.Add(cattr); expected.Add(ConnectorAttributeBuilder.Build("abc", 1, 2)); actual = up.Merge(changeset, baseAttrs, true); Assert.IsTrue(AreEqual(expected, actual)); }
public void TestSimplifyNoAndNoOrNoLeaf() { Filter a = FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); Filter b = FilterBuilder.Contains(ConnectorAttributeBuilder.Build("b", "b")); Filter c = FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("c", "c")); Filter d = FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); Filter filter = FilterBuilder.And( FilterBuilder.Or(a, b), FilterBuilder.Or(c, d)); IList <String> results = new NoAndNoOrNoEndsWithTranslator().Translate(filter); Assert.AreEqual(2, results.Count); Assert.AreEqual("( CONTAINS a a )", results[0]); Assert.AreEqual("( CONTAINS b b )", results[1]); a = FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); b = FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("b", "b")); c = FilterBuilder.Contains(ConnectorAttributeBuilder.Build("c", "c")); d = FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); filter = FilterBuilder.And( FilterBuilder.Or(a, b), FilterBuilder.Or(c, d)); results = new NoAndNoOrNoEndsWithTranslator().Translate(filter); Assert.AreEqual(2, results.Count); Assert.AreEqual("( CONTAINS c c )", results[0]); Assert.AreEqual("( CONTAINS d d )", results[1]); }
public virtual void TestSearch2() { ConnectorFacade search = GetFacade(); for (int i = 0; i < 100; i++) { ICollection <ConnectorAttribute> co = GetTestCreateConnectorObject(string.Format("TEST{0:D5}", i)); co.Add(ConnectorAttributeBuilder.Build("sortKey", i)); search.Create(ObjectClass.ACCOUNT, co, null); } OperationOptionsBuilder builder = new OperationOptionsBuilder { PageSize = 10, SortKeys = new[] { new SortKey("sortKey", false) } }; SearchResult result = null; ICollection <ConnectorObject> resultSet = new HashSet <ConnectorObject>(); int pageIndex = 0; int index = 101; while ((result = search.Search(ObjectClass.ACCOUNT, FilterBuilder.StartsWith(ConnectorAttributeBuilder.Build(Name.NAME, "TEST")), new ResultsHandler() { Handle = connectorObject => { int?idx = ConnectorAttributeUtil.GetIntegerValue(connectorObject.GetAttributeByName("sortKey")); Assert.IsTrue(idx < index); if (idx != null) { index = (int)idx; } resultSet.Add(connectorObject); return(true); } }, builder.Build())).PagedResultsCookie != null) { builder = new OperationOptionsBuilder(builder.Build()) { PagedResultsCookie = result.PagedResultsCookie }; Assert.AreEqual(10 * ++pageIndex, resultSet.Count); } Assert.AreEqual(9, pageIndex); Assert.AreEqual(100, resultSet.Count); }
public void TestGetSingleValue() { object TEST_VALUE = 1L; ConnectorAttribute attr = ConnectorAttributeBuilder.Build("long", TEST_VALUE); object value = ConnectorAttributeUtil.GetSingleValue(attr); Assert.AreEqual(TEST_VALUE, value); // test null attr = ConnectorAttributeBuilder.Build("long"); value = ConnectorAttributeUtil.GetSingleValue(attr); Assert.IsNull(value); // test empty attr = ConnectorAttributeBuilder.Build("long", new List <object>()); value = ConnectorAttributeUtil.GetSingleValue(attr); Assert.IsNull(value); // test illegal argument exception ConnectorAttributeUtil.GetSingleValue(ConnectorAttributeBuilder.Build("bob", 1, 2, 3)); }
public void TestSimplifyNoAnd() { Filter a = FilterBuilder.Contains(ConnectorAttributeBuilder.Build("a", "a")); Filter b = FilterBuilder.Contains(ConnectorAttributeBuilder.Build("b", "b")); Filter c = FilterBuilder.EndsWith(ConnectorAttributeBuilder.Build("c", "c")); Filter d = FilterBuilder.Contains(ConnectorAttributeBuilder.Build("d", "d")); Filter filter = FilterBuilder.And( FilterBuilder.Or(a, b), FilterBuilder.Or(c, d)); String expected = "( | ( CONTAINS a a ) ( CONTAINS b b ) )"; String actual = TranslateSingle(new NoAndTranslator(), filter); Assert.AreEqual(expected, actual); }
private ConnectorAttribute NormalizeSmtpAddressAttribute(ConnectorAttribute attribute) { if (attribute.Value == null) { return(attribute); } IList <object> normValues = new List <object>(); bool normalized = false; foreach (object val in attribute.Value) { string strVal = val as string; if (strVal != null) { string[] split = strVal.Split(':'); if (split.Length == 2) { // it contains delimiter, use the second part normValues.Add(split[1]); normalized = true; } else { // put the original value normValues.Add(val); } } } if (normalized) { // build the attribute again return(ConnectorAttributeBuilder.Build(attribute.Name, normValues)); } else { return(attribute); } }
/// <summary> /// Attribute normalizer /// </summary> /// <param name="oclass">Object class</param> /// <param name="attribute">Attribute to be normalized</param> /// <returns>Normalized attribute</returns> public override ConnectorAttribute NormalizeAttribute(ObjectClass oclass, ConnectorAttribute attribute) { // normalize the attribute using AD connector first attribute = base.NormalizeAttribute(oclass, attribute); // normalize external mail value if (attribute.Name == AttExternalMail && attribute.Value != null) { IList <object> normAttributes = new List <object>(); bool normalized = false; foreach (object val in attribute.Value) { string strVal = val as string; if (strVal != null) { string[] split = strVal.Split(':'); if (split.Length == 2) { // it contains delimiter, use the second part normAttributes.Add(split[1]); normalized = true; } else { // put the original value normAttributes.Add(val); } } } if (normalized) { // build the attribute again return(ConnectorAttributeBuilder.Build(attribute.Name, normAttributes)); } } // return the original attribute return(attribute); }
/// <summary> /// Renames the connector attribute to new name; transforms value by keeping Common Name only /// </summary> /// <param name="cattribute">ConnectorAttribute to be renamed</param> /// <param name="newName">New attribute name</param> /// <returns>Renamed and transformed ConnectorAttribute</returns> /// <exception cref="ArgumentNullException">If some of the params is null</exception> internal static ConnectorAttribute ExtractCommonName(ConnectorAttribute cattribute, string newName) { Assertions.NullCheck(cattribute, "cattribute"); Assertions.NullCheck(newName, "newName"); var attBuilder = new ConnectorAttributeBuilder(); if (cattribute.Value != null) { ICollection <object> convertedValues = new List <object>(); foreach (object oldValue in cattribute.Value) { if (oldValue != null) // should be always the case { convertedValues.Add(ExtractCommonName(oldValue.ToString())); } } attBuilder.AddValue(convertedValues); } attBuilder.Name = newName; return(attBuilder.Build()); }
public virtual void Sync(ObjectClass objectClass, SyncToken token, SyncResultsHandler handler, OperationOptions options) { if (ObjectClass.ALL.Equals(objectClass)) { // } else if (ObjectClass.ACCOUNT.Equals(objectClass)) { var builder = new ConnectorObjectBuilder(); builder.SetUid("3f50eca0-f5e9-11e3-a3ac-0800200c9a66"); builder.SetName("Foo"); builder.AddAttribute(ConnectorAttributeBuilder.BuildEnabled(true)); var deltaBuilder = new SyncDeltaBuilder { Object = builder.Build(), DeltaType = SyncDeltaType.CREATE, Token = new SyncToken(10) }; foreach (SyncDelta connectorObject in CollectionUtil.NewSet(deltaBuilder.Build())) { if (!handler.Handle(connectorObject)) { // Stop iterating because the handler stopped processing break; } } } else { Trace.TraceWarning("Sync of type {0} is not supported", _configuration.ConnectorMessages.Format(objectClass.GetDisplayNameKey(), objectClass.GetObjectClassValue())); throw new NotSupportedException("Sync of type" + objectClass.GetObjectClassValue() + " is not supported"); } ((SyncTokenResultsHandler)handler).HandleResult(new SyncToken(10)); }
public void TestDictionaryAttribute() { Dictionary <object, object> map1 = new Dictionary <object, object>(); map1["string"] = "OK"; Dictionary <object, object> map2 = new Dictionary <object, object>(); map2["map1"] = map1; map2["list"] = new List <int> { 1, 2, 3 }; Dictionary <object, object> map3 = new Dictionary <object, object>(); map3["map2"] = map2; Dictionary <object, object> map4 = new Dictionary <object, object>(); map4["map3"] = map3; ConnectorAttributeBuilder.Build("map", map4); }
static TstAbstractConnector() { bool enabled = true; for (int i = 0; i < 100; i++) { ConnectorObjectBuilder builder = new ConnectorObjectBuilder(); builder.SetUid(Convert.ToString(i)); builder.SetName(string.Format("user{0:D3}", i)); builder.AddAttribute(ConnectorAttributeBuilder.BuildEnabled(enabled)); IDictionary <string, object> mapAttribute = new Dictionary <string, object>(); mapAttribute["email"] = "*****@*****.**"; mapAttribute["primary"] = true; mapAttribute["usage"] = new List <String>() { "home", "work" }; builder.AddAttribute("emails", mapAttribute); ConnectorObject co = builder.Build(); collection[co.Name.GetNameValue()] = co; enabled = !enabled; } }
public virtual void ExecuteQuery(ObjectClass objectClass, string query, ResultsHandler handler, OperationOptions options) { var builder = new ConnectorObjectBuilder(); builder.SetUid("3f50eca0-f5e9-11e3-a3ac-0800200c9a66"); builder.SetName("Foo"); builder.AddAttribute(ConnectorAttributeBuilder.BuildEnabled(true)); foreach (ConnectorObject connectorObject in CollectionUtil.NewSet(builder.Build())) { if (!handler.Handle(connectorObject)) { // Stop iterating because the handler stopped processing break; } } if (options.PageSize != null && 0 < options.PageSize) { Trace.TraceInformation("Paged Search was requested"); ((SearchResultsHandler)handler).HandleResult(new SearchResult("0", 0)); } }
public void TestAttributeTypeMap() { ConnectorPoolManager.Dispose(); ConnectorInfoManager manager = GetConnectorInfoManager(); ConnectorInfo info1 = FindConnectorInfo(manager, "1.0.0.0", "org.identityconnectors.testconnector.TstStatefulConnector"); Assert.IsNotNull(info1); APIConfiguration config = info1.CreateDefaultAPIConfiguration(); config.ConnectorPoolConfiguration.MinIdle = 0; config.ConnectorPoolConfiguration.MaxIdle = 0; ConnectorFacade facade = ConnectorFacadeFactory.GetInstance().NewInstance(config); HashSet <ConnectorAttribute> createAttributes = new HashSet <ConnectorAttribute>(); IDictionary <string, object> mapAttribute = new Dictionary <string, object>(); mapAttribute["email"] = "*****@*****.**"; mapAttribute["primary"] = true; mapAttribute["usage"] = new List <String>() { "home", "work" }; createAttributes.Add(ConnectorAttributeBuilder.Build("emails", mapAttribute)); Uid uid = facade.Create(ObjectClass.ACCOUNT, createAttributes, null); Assert.AreEqual(uid.GetUidValue(), "*****@*****.**"); ConnectorObject co = facade.GetObject(ObjectClass.ACCOUNT, new Uid("0"), null); object value = ConnectorAttributeUtil.GetSingleValue(co.GetAttributeByName("emails")); Assert.IsTrue(value is IDictionary); Assert.IsTrue(((IDictionary)value)["usage"] is IList); }
public void TestPagedSearch() { ConnectorPoolManager.Dispose(); ConnectorInfoManager manager = GetConnectorInfoManager(); ConnectorInfo info1 = FindConnectorInfo(manager, "1.0.0.0", "org.identityconnectors.testconnector.TstStatefulPoolableConnector"); Assert.IsNotNull(info1); APIConfiguration config = info1.CreateDefaultAPIConfiguration(); config.ProducerBufferSize = 0; config.ConnectorPoolConfiguration.MinIdle = 1; config.ConnectorPoolConfiguration.MaxIdle = 2; config.ResultsHandlerConfiguration.FilteredResultsHandlerInValidationMode = true; // for paged searches, the filtered results handler should be either disabled or put into validation mode ConnectorFacade facade1 = ConnectorFacadeFactory.GetInstance().NewInstance(config); OperationOptionsBuilder builder = new OperationOptionsBuilder(); builder.PageSize = 10; builder.SetSortKeys(new ICF.SortKey(Name.NAME, true)); SearchResult searchResult = null; ISet <Uid> UIDs = new HashSet <Uid>(); int iteration = 0; do { if (null != searchResult) { builder.PagedResultsCookie = searchResult.PagedResultsCookie; } int size = 0; searchResult = facade1.Search(ObjectClass.ACCOUNT, null, new ResultsHandler() { Handle = obj => { if (size >= 10) { Assert.Fail("More then 10 objects was handled!"); } size++; if (UIDs.Contains(obj.Uid)) { Assert.Fail("Duplicate Entry in results"); } return(UIDs.Add(obj.Uid)); } }, builder.Build()); iteration++; Assert.IsNotNull(searchResult); Assert.AreEqual(searchResult.RemainingPagedResults, 100 - (iteration * 10)); } while (searchResult.PagedResultsCookie != null); // Search with paged results offset builder = new OperationOptionsBuilder(); builder.PageSize = 10; builder.PagedResultsOffset = 5; builder.SetSortKeys(new ICF.SortKey(Name.NAME, true)); searchResult = null; UIDs.Clear(); Filter filter = FilterBuilder.EqualTo(ConnectorAttributeBuilder.BuildEnabled(true)); iteration = 0; do { if (null != searchResult) { builder.PagedResultsCookie = searchResult.PagedResultsCookie; } int size = 0; searchResult = facade1.Search(ObjectClass.ACCOUNT, filter, new ResultsHandler() { Handle = obj => { if (size >= 10) { Assert.Fail("More then 10 objects was handled!"); } size++; if (UIDs.Contains(obj.Uid)) { Assert.Fail("Duplicate Entry in results"); } return(UIDs.Add(obj.Uid)); } }, builder.Build()); iteration++; Assert.IsNotNull(searchResult); Assert.AreEqual(searchResult.RemainingPagedResults, Math.Max(50 - (iteration * 15), 0)); } while (searchResult.PagedResultsCookie != null); }
public void TestBasics() { ConnectorAttribute attribute = ConnectorAttributeBuilder.Build("att-name", "att-value"); ConnectorAttribute attribute2 = ConnectorAttributeBuilder.Build("att-name2", "att-value2"); AllFiltersTranslator translator = new AllFiltersTranslator(); { Filter filter = FilterBuilder.Contains(attribute); String expected = "( CONTAINS att-name att-value )"; String actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); filter = FilterBuilder.Not(filter); expected = "( ! " + expected + " )"; actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); } { Filter filter = FilterBuilder.EndsWith(attribute); String expected = "( ENDS-WITH att-name att-value )"; String actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); filter = FilterBuilder.Not(filter); expected = "( ! " + expected + " )"; actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); } { Filter filter = FilterBuilder.EqualTo(attribute); String expected = "( = att-name [att-value] )"; String actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); filter = FilterBuilder.Not(filter); expected = "( ! " + expected + " )"; actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); } { Filter filter = FilterBuilder.GreaterThan(attribute); String expected = "( > att-name att-value )"; String actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); filter = FilterBuilder.Not(filter); expected = "( ! " + expected + " )"; actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); } { Filter filter = FilterBuilder.GreaterThanOrEqualTo(attribute); String expected = "( >= att-name att-value )"; String actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); filter = FilterBuilder.Not(filter); expected = "( ! " + expected + " )"; actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); } { Filter filter = FilterBuilder.LessThan(attribute); String expected = "( < att-name att-value )"; String actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); filter = FilterBuilder.Not(filter); expected = "( ! " + expected + " )"; actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); } { Filter filter = FilterBuilder.LessThanOrEqualTo(attribute); String expected = "( <= att-name att-value )"; String actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); filter = FilterBuilder.Not(filter); expected = "( ! " + expected + " )"; actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); } { Filter filter = FilterBuilder.StartsWith(attribute); String expected = "( STARTS-WITH att-name att-value )"; String actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); filter = FilterBuilder.Not(filter); expected = "( ! " + expected + " )"; actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); } { Filter filter = FilterBuilder.ContainsAllValues(attribute); String expected = "( CONTAINS-ALL-VALUES " + attribute + " )"; String actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); filter = FilterBuilder.Not(filter); expected = "( ! " + expected + " )"; actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); } //and { Filter left = FilterBuilder.Contains(attribute); Filter right = FilterBuilder.Contains(attribute2); String expectedLeft = "( CONTAINS att-name att-value )"; String expectedRight = "( CONTAINS att-name2 att-value2 )"; Filter filter = FilterBuilder.And(left, right); String expected = "( & " + expectedLeft + " " + expectedRight + " )"; String actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); filter = FilterBuilder.Not(filter); expectedLeft = "( ! " + expectedLeft + " )"; expectedRight = "( ! " + expectedRight + " )"; expected = "( | " + expectedLeft + " " + expectedRight + " )"; actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); } //or { Filter left = FilterBuilder.Contains(attribute); Filter right = FilterBuilder.Contains(attribute2); String expectedLeft = "( CONTAINS att-name att-value )"; String expectedRight = "( CONTAINS att-name2 att-value2 )"; Filter filter = FilterBuilder.Or(left, right); String expected = "( | " + expectedLeft + " " + expectedRight + " )"; String actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); filter = FilterBuilder.Not(filter); expectedLeft = "( ! " + expectedLeft + " )"; expectedRight = "( ! " + expectedRight + " )"; expected = "( & " + expectedLeft + " " + expectedRight + " )"; actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); } //double-negative { Filter filter = FilterBuilder.Contains(attribute); filter = FilterBuilder.Not(filter); filter = FilterBuilder.Not(filter); String expected = "( CONTAINS att-name att-value )"; String actual = TranslateSingle(translator, filter); Assert.AreEqual(expected, actual); } }
public void TestUpdateTimeOut() { GetFacade().Update(Test, new Uid("TIMEOUT"), CollectionUtil.NewSet(ConnectorAttributeBuilder.Build("null")), null); Assert.Fail(); }
/// <summary> /// Process the Hashtable result and convert it to a SyncDelta object /// ready to be processed by the sync handler /// </summary> /// <remarks> /// The result Hashtable must follow a specific format and contain the following key/value: /// /// "Token": (Object) token object (could be Integer, Date, String), [!! could be null] /// "DeltaType": (String) ("CREATE|UPDATE|CREATE_OR_UPDATE"|"DELETE"), /// "Uid": (String) uid (uid of the entry), /// "PreviousUid": (String) previous uid (This is for rename ops), /// "Object": Hashtable(String,List) of attributes name/values describing the object /// "ObjectClass": (String) must be set if Operation = DELETE and Object = null /// </remarks> /// <param name="result"></param> /// <returns></returns> public Object Process(Hashtable result) { var syncbld = new SyncDeltaBuilder(); var cobld = new ConnectorObjectBuilder(); Uid uid; // SyncToken // Mandatory here if (result.ContainsKey(SyncTokenKeyName)) { syncbld.Token = result[SyncTokenKeyName] == null ? new SyncToken(0L) : new SyncToken(result[SyncTokenKeyName]); } else { throw new ArgumentException("SyncToken is missing in Sync result"); } // SyncDelta // Mandatory here if (isValidKeyAndValue(result, DeltaTypeKeyName)) { var op = result[DeltaTypeKeyName]; if (SyncDeltaType.CREATE.ToString().Equals(op as String, StringComparison.OrdinalIgnoreCase)) { syncbld.DeltaType = SyncDeltaType.CREATE; } else if (SyncDeltaType.UPDATE.ToString().Equals(op as String, StringComparison.OrdinalIgnoreCase)) { syncbld.DeltaType = SyncDeltaType.UPDATE; } else if (SyncDeltaType.DELETE.ToString().Equals(op as String, StringComparison.OrdinalIgnoreCase)) { syncbld.DeltaType = SyncDeltaType.DELETE; } else if (SyncDeltaType.CREATE_OR_UPDATE.ToString().Equals(op as String, StringComparison.OrdinalIgnoreCase)) { syncbld.DeltaType = SyncDeltaType.CREATE_OR_UPDATE; } else { throw new ArgumentException("Unrecognized DeltaType in Sync result"); } } else { throw new ArgumentException("DeltaType is missing in Sync result"); } // Uid // Mandatory if (isValidKeyAndValue(result, UidKeyName)) { var value = result[UidKeyName]; if (value is String) { uid = new Uid(value as String); } else if (value is Uid) { uid = value as Uid; } else { throw new ArgumentException("Unrecognized Uid in Sync result"); } syncbld.Uid = uid; cobld.SetUid(uid); } else { throw new ArgumentException("Uid is missing in Sync result"); } // PreviousUid // Not valid if DELETE if (isValidKeyAndValue(result, PreviousUidKeyName)) { var value = result[PreviousUidKeyName]; Uid previousUid; if (value is String) { previousUid = new Uid(value as String); } else if (value is Uid) { previousUid = value as Uid; } else { throw new ArgumentException("Unrecognized PreviousUid in Sync result"); } syncbld.PreviousUid = previousUid; } if (syncbld.PreviousUid != null && syncbld.DeltaType == SyncDeltaType.DELETE) { throw new ArgumentException("PreviousUid can only be specified for Create or Update."); } // Connector object // Mandatory unless DELETE if (result.ContainsKey(ConnectorObjectKeyName) && result[ConnectorObjectKeyName] is Hashtable) { var attrs = result[ConnectorObjectKeyName] as Hashtable; if (!attrs.ContainsKey(Name.NAME)) { throw new ArgumentException("The Object must contain a Name"); } foreach (DictionaryEntry attr in attrs) { var attrName = attr.Key as String; var attrValue = attr.Value; if (Name.NAME.Equals(attrName)) { cobld.SetName(attrValue as String); } else if (Uid.NAME.Equals((attrName))) { if (!uid.GetUidValue().Equals(attrValue)) { throw new ArgumentException("Uid from Object is different than Uid from Sync result"); } } else if (OperationalAttributes.ENABLE_NAME.Equals((attrName))) { cobld.AddAttribute(ConnectorAttributeBuilder.BuildEnabled(attr.Value is bool && (bool)attr.Value)); } else { if (attrValue == null) { cobld.AddAttribute(ConnectorAttributeBuilder.Build(attrName)); } else if (attrValue.GetType() == typeof(Object[]) || attrValue.GetType() == typeof(System.Collections.ICollection)) { var list = new Collection <object>(); foreach (var val in (ICollection)attrValue) { list.Add(FrameworkUtil.IsSupportedAttributeType(val.GetType()) ? val : val.ToString()); } cobld.AddAttribute(ConnectorAttributeBuilder.Build(attrName, list)); } else { cobld.AddAttribute(ConnectorAttributeBuilder.Build(attrName, attrValue)); } } } cobld.ObjectClass = _objectClass; syncbld.Object = cobld.Build(); } // If operation is DELETE and the ConnectorObject is null, // we need to set the ObjectClass at the SyncDelta level else if ((SyncDeltaType.DELETE == syncbld.DeltaType) && isValidKeyAndValue(result, ObjectClassKeyName)) { var objclass = result[ObjectClassKeyName]; if (objclass is ObjectClass) { syncbld.ObjectClass = objclass as ObjectClass; } else if (objclass is String) { syncbld.ObjectClass = new ObjectClass(objclass as String); } else { throw new ArgumentException("Unrecognized ObjectClass in Sync result"); } } else { throw new ArgumentException("Object is missing in Sync result"); } return(_handler.Handle(syncbld.Build())); }
// creates a collection of attributes that correspond to the original ones, but resolves ADD/DELETE using existing values of psuser public ICollection <ConnectorAttribute> DetermineNewAttributeValues(UpdateOpContext context, ConnectorObject originalObject) { if (context.UpdateType == UpdateType.REPLACE) { // TODO check multivaluedness and updateability (as below) return(new List <ConnectorAttribute>(context.Attributes)); } else { Boolean add; if (context.UpdateType == UpdateType.ADD) { add = true; } else if (context.UpdateType == UpdateType.DELETE) { add = false; } else { throw new ArgumentException("Unsupported update type: " + context.UpdateType); } Schema schema = null; ICollection <ConnectorAttribute> rv = new List <ConnectorAttribute>(context.Attributes.Count); foreach (ConnectorAttribute attribute in context.Attributes) { ConnectorAttribute originalAttribute = originalObject.GetAttributeByName(attribute.Name); IList <object> newValues = originalAttribute != null && originalAttribute.Value != null ? new List <object>(originalAttribute.Value) : new List <object>(); Boolean changed = false; if (attribute.Value != null) { foreach (object item in attribute.Value) { if (add) { if (newValues.Contains(item)) { LOG.Warn("Trying to add value from " + attribute.Name + " that is already there: " + item); } else { newValues.Add(item); changed = true; } } else { if (!newValues.Contains(item)) { LOG.Warn("Trying to remove value from " + attribute.Name + " that is not there: " + item); } else { newValues.Remove(item); changed = true; } } } } if (changed) { ConnectorAttributeBuilder b = new ConnectorAttributeBuilder(); b.Name = attribute.Name; b.AddValue(newValues); ConnectorAttribute modified = b.Build(); if (schema == null) { ExchangeConnector connector = (ExchangeConnector)context.Connector; schema = connector.Schema(); } ObjectClassInfo oci = schema.FindObjectClassInfo(context.ObjectClass.Type); if (oci == null) { throw new InvalidOperationException("No object class info for " + context.ObjectClass.Type + " in the schema"); } var cai = ConnectorAttributeInfoUtil.Find(attribute.Name, oci.ConnectorAttributeInfos); if (cai == null) { throw new InvalidOperationException("No connector attribute info for " + context.ObjectClass.Type + " in the schema"); } if (!cai.IsUpdateable) { throw new ConnectorSecurityException("Attempt to update a non-updateable attribute (" + attribute.Name + "): " + CollectionUtil.Dump(newValues)); } if (newValues.Count > 1 && !cai.IsMultiValued) { throw new InvalidAttributeValueException("More than one value in a single-valued attribute (" + attribute.Name + "): " + CollectionUtil.Dump(newValues)); } rv.Add(modified); } } return(rv); } }
/// <summary> /// DeduplicatesEmailAddresses. /// - on REPLACE (i.e. update/replace or create) the situation is easy: we just remove duplicate entries (SMTP:x & smtp:x result in SMTP:x) /// - on DELETE we currently do nothing /// - on ADD we have one additional rule: /// "If we are adding SMTP:x, we first convert all SMTP:y in existing records to smtp:y because we see the intent of having x to be a new primary" /// /// </summary> /// <param name="context"></param> /// <param name="attributes">these are attributes to be set (already resolved if we have update of type ADD or DELETE)</param> /// <returns></returns> private ICollection <ConnectorAttribute> DeduplicateEmailAddresses(CreateUpdateOpContext context, ICollection <ConnectorAttribute> attributes) { // trivial cases if (context is UpdateOpContext && ((UpdateOpContext)context).UpdateType == UpdateType.DELETE) { return(attributes); } ConnectorAttribute attribute = ConnectorAttributeUtil.Find(ExchangeConnectorAttributes.AttEmailAddresses, attributes); if (attribute == null || attribute.Value == null) { return(attributes); // missing or empty EmailAddresses - nothing to deduplicate } ConnectorAttribute attributeDelta = ConnectorAttributeUtil.Find(ExchangeConnectorAttributes.AttEmailAddresses, context.Attributes); if (attributeDelta == null || attributeDelta.Value == null) { return(attributes); // missing or empty changed EmailAddresses - nothing to deduplicate } // now the main part IList <string> valuesToDeduplicate = new List <string>(); foreach (object o in attribute.Value) { if (o != null) { valuesToDeduplicate.Add(o.ToString()); } } bool changed = false; // special rule: if ADD with SMTP:, let us change all other "SMTP:x" to "smtp:x" Boolean isUpdateAdd = context is UpdateOpContext && ((UpdateOpContext)context).UpdateType == UpdateType.ADD; if (isUpdateAdd) { string newPrimary = null; foreach (object o in attributeDelta.Value) { if (((string)o).StartsWith("SMTP:")) { newPrimary = (string)o; break; } } if (newPrimary != null) { foreach (string address in new List <string>(valuesToDeduplicate)) // to eliminate concurrent access { if (address.StartsWith("SMTP:") && !address.Equals(newPrimary)) { string replacement = "smtp:" + address.Substring(5); LOGGER.TraceEvent(TraceEventType.Information, CAT_DEFAULT, "Changing duplicate primary-candidate address {0} to {1}", address, replacement); valuesToDeduplicate.Remove(address); valuesToDeduplicate.Add(replacement); changed = true; } } } } IDictionary <string, string> values = new Dictionary <string, string>(); // normalized->most-recent-original e.g. SMTP:[email protected] -> SMTP:[email protected] (if primary is present) foreach (object v in valuesToDeduplicate) { string address = (string)v; string normalized = address.ToUpper(); if (values.ContainsKey(normalized)) { changed = true; string existing = values[normalized]; if (address.StartsWith("SMTP:") && existing.StartsWith("smtp:")) { LOGGER.TraceEvent(TraceEventType.Information, CAT_DEFAULT, "Removing redundant address {0}, keeping {1}", existing, address); values[normalized] = address; } else { LOGGER.TraceEvent(TraceEventType.Information, CAT_DEFAULT, "Removing redundant address {0}, keeping {1}", address, existing); } } else { values.Add(normalized, address); } } if (changed) { ConnectorAttributeBuilder cab = new ConnectorAttributeBuilder(); cab.Name = ExchangeConnectorAttributes.AttEmailAddresses; foreach (string value in values.Values) { cab.AddValue(value); } ICollection <ConnectorAttribute> rv = new List <ConnectorAttribute>(attributes); // the original is (sometimes) a read-only collection rv.Remove(attribute); rv.Add(cab.Build()); return(rv); } else { return(attributes); } }
public void UpdateMergeTests() { ConnectorAttribute expected, actual; Configuration config = new MockConfiguration(false); ConnectorFacadeFactory factory = ConnectorFacadeFactory.GetInstance(); SafeType <Connector> clazz = SafeType <Connector> .Get <MockUpdateConnector>(); // **test only** APIConfiguration impl = TestHelpers.CreateTestConfiguration(clazz, config); impl.SetTimeout(SafeType <APIOperation> .Get <GetApiOp>(), APIConstants.NO_TIMEOUT); impl.SetTimeout(SafeType <APIOperation> .Get <UpdateApiOp>(), APIConstants.NO_TIMEOUT); impl.SetTimeout(SafeType <APIOperation> .Get <SearchApiOp>(), APIConstants.NO_TIMEOUT); ConnectorFacade facade = factory.NewInstance(impl); // sniff test to make sure we can get an object.. ConnectorObject obj = facade.GetObject(ObjectClass.ACCOUNT, NewUid(1), null); Assert.AreEqual(NewUid(1), obj.Uid); // ok lets add an attribute that doesn't exist.. String ADDED = "somthing to add to the object"; String ATTR_NAME = "added"; ICollection <ConnectorAttribute> addAttrSet; addAttrSet = CollectionUtil.NewSet((IEnumerable <ConnectorAttribute>)obj.GetAttributes()); addAttrSet.Add(ConnectorAttributeBuilder.Build(ATTR_NAME, ADDED)); Name name = obj.Name; addAttrSet.Remove(name); Uid uid = facade.AddAttributeValues(ObjectClass.ACCOUNT, obj.Uid, ConnectorAttributeUtil.FilterUid(addAttrSet), null); // get back the object and see if there are the same.. addAttrSet.Add(name); ConnectorObject addO = new ConnectorObject(ObjectClass.ACCOUNT, addAttrSet); obj = facade.GetObject(ObjectClass.ACCOUNT, NewUid(1), null); Assert.AreEqual(addO, obj); // attempt to add on to an existing attribute.. addAttrSet.Remove(name); uid = facade.AddAttributeValues(ObjectClass.ACCOUNT, obj.Uid, ConnectorAttributeUtil.FilterUid(addAttrSet), null); // get the object back out and check on it.. obj = facade.GetObject(ObjectClass.ACCOUNT, uid, null); expected = ConnectorAttributeBuilder.Build(ATTR_NAME, ADDED, ADDED); actual = obj.GetAttributeByName(ATTR_NAME); Assert.AreEqual(expected, actual); // attempt to delete a value from an attribute.. ICollection <ConnectorAttribute> deleteAttrs = CollectionUtil.NewSet((IEnumerable <ConnectorAttribute>)addO.GetAttributes()); deleteAttrs.Remove(name); uid = facade.RemoveAttributeValues(ObjectClass.ACCOUNT, addO.Uid, ConnectorAttributeUtil.FilterUid(deleteAttrs), null); obj = facade.GetObject(ObjectClass.ACCOUNT, uid, null); expected = ConnectorAttributeBuilder.Build(ATTR_NAME, ADDED); actual = obj.GetAttributeByName(ATTR_NAME); Assert.AreEqual(expected, actual); // attempt to delete an attribute that doesn't exist.. ICollection <ConnectorAttribute> nonExist = new HashSet <ConnectorAttribute>(); nonExist.Add(NewUid(1)); nonExist.Add(ConnectorAttributeBuilder.Build("does not exist", "asdfe")); uid = facade.RemoveAttributeValues(ObjectClass.ACCOUNT, addO.Uid, ConnectorAttributeUtil.FilterUid(nonExist), null); obj = facade.GetObject(ObjectClass.ACCOUNT, NewUid(1), null); Assert.IsTrue(obj.GetAttributeByName("does not exist") == null); }
private ConnectorAttribute CreateNormalizedTestAttribute() { return(ConnectorAttributeBuilder.Build("foo", "bar")); }
/// <summary> /// Finds the attributes in connector object and rename it according to input array of names, but only /// if the atribute name is in attributes to get /// </summary> /// <param name="cobject">ConnectorObject which attributes should be replaced</param> /// <param name="map">Replace mapping</param> /// <returns>ConnectorObject with replaced attributes</returns> /// <exception cref="ArgumentNullException">If some of the params is null</exception> internal static ConnectorObject ConvertAdAttributesToExchange(ConnectorObject cobject) { Assertions.NullCheck(cobject, "cobject"); var attributes = cobject.GetAttributes(); var builder = new ConnectorObjectBuilder(); bool emailAddressPolicyEnabled = true; foreach (ConnectorAttribute attribute in attributes) { string newName; if (attribute.Is(ExchangeConnectorAttributes.AttMsExchPoliciesExcludedADName)) { if (attribute.Value != null && attribute.Value.Contains("{26491cfc-9e50-4857-861b-0cb8df22b5d7}")) { emailAddressPolicyEnabled = false; } } else if (attribute.Is(ExchangeConnectorAttributes.AttAddressBookPolicyADName)) { var newAttribute = ExtractCommonName(attribute, ExchangeConnectorAttributes.AttAddressBookPolicy); builder.AddAttribute(newAttribute); builder.AddAttribute(attribute); // keep the original one as well } else if (attribute.Is(ExchangeConnectorAttributes.AttOfflineAddressBookADName)) { var newAttribute = ExtractCommonName(attribute, ExchangeConnectorAttributes.AttOfflineAddressBook); builder.AddAttribute(newAttribute); builder.AddAttribute(attribute); // keep the original one as well } else if (attribute.Is(ExchangeConnectorAttributes.AttHiddenFromAddressListsEnabledADName)) { builder.AddAttribute(attribute); // keep the original one as well } else if (ExchangeConnectorAttributes.AttMapFromAD.TryGetValue(attribute.Name, out newName)) { var newAttribute = RenameAttribute(attribute, newName); builder.AddAttribute(newAttribute); } else { builder.AddAttribute(attribute); } } builder.AddAttribute(ConnectorAttributeBuilder.Build(ExchangeConnectorAttributes.AttEmailAddressPolicyEnabled, emailAddressPolicyEnabled)); copyAttribute(builder, cobject, ExchangeConnectorAttributes.AttPrimarySmtpAddressADName, ExchangeConnectorAttributes.AttPrimarySmtpAddress); // derive recipient type string recipientType = GetRecipientType(cobject); if (recipientType != null) { builder.AddAttribute(ConnectorAttributeBuilder.Build(ExchangeConnectorAttributes.AttRecipientType, new string[] { recipientType })); } builder.ObjectClass = cobject.ObjectClass; builder.SetName(cobject.Name); builder.SetUid(cobject.Uid); return(builder.Build()); }
public void Create(CreateOpContext context) { context.Attributes = DeduplicateEmailAddresses(context, context.Attributes); // get recipient type string rcptType = ExchangeUtility.GetAttValue(ExchangeConnectorAttributes.AttRecipientType, context.Attributes) as string; if (rcptType == null || rcptType.Equals("")) { rcptType = ExchangeConnectorAttributes.RcptTypeUser; } ExchangeConnector exconn = (ExchangeConnector)context.Connector; ActiveDirectoryConnector adconn = exconn.ActiveDirectoryConnector; PSExchangeConnector.CommandInfo cmdInfoEnable = null; PSExchangeConnector.CommandInfo cmdInfoSet = null; switch (rcptType) { case ExchangeConnectorAttributes.RcptTypeMailBox: cmdInfoEnable = PSExchangeConnector.CommandInfo.EnableMailbox; cmdInfoSet = PSExchangeConnector.CommandInfo.SetMailbox; break; case ExchangeConnectorAttributes.RcptTypeMailUser: cmdInfoEnable = PSExchangeConnector.CommandInfo.EnableMailUser; cmdInfoSet = PSExchangeConnector.CommandInfo.SetMailUser; break; case ExchangeConnectorAttributes.RcptTypeUser: break; default: throw new ArgumentException( context.ConnectorConfiguration.ConnectorMessages.Format( "ex_bad_rcpt", "Recipient type [{0}] is not supported", rcptType)); } // first create the object in AD ICollection <ConnectorAttribute> adAttributes = ExchangeUtility.FilterOut(context.Attributes, PSExchangeConnector.CommandInfo.EnableMailbox, PSExchangeConnector.CommandInfo.SetMailbox, PSExchangeConnector.CommandInfo.EnableMailUser, PSExchangeConnector.CommandInfo.SetMailUser); Uid uid = adconn.Create(context.ObjectClass, adAttributes, context.Options); if (rcptType == ExchangeConnectorAttributes.RcptTypeUser) { // AD account only, we do nothing context.Uid = uid; return; } // add a empty "EmailAddresses" attribute if needed (address policy is disabled and no addresses are provided) ICollection <ConnectorAttribute> enhancedAttributes; ConnectorAttribute policyEnabledAttribute = ConnectorAttributeUtil.Find(ExchangeConnectorAttributes.AttEmailAddressPolicyEnabled, context.Attributes); if (policyEnabledAttribute != null && ConnectorAttributeUtil.GetBooleanValue(policyEnabledAttribute).HasValue&& ConnectorAttributeUtil.GetBooleanValue(policyEnabledAttribute).Value == false && ConnectorAttributeUtil.Find(ExchangeConnectorAttributes.AttPrimarySmtpAddress, context.Attributes) == null && ConnectorAttributeUtil.Find(ExchangeConnectorAttributes.AttEmailAddresses, context.Attributes) == null) { enhancedAttributes = new HashSet <ConnectorAttribute>(context.Attributes); enhancedAttributes.Add(ConnectorAttributeBuilder.Build(ExchangeConnectorAttributes.AttEmailAddresses)); LOGGER.TraceEvent(TraceEventType.Verbose, CAT_DEFAULT, "Added empty EmailAddresses attribute because address policy use is disabled and no addresses were provided"); } else { enhancedAttributes = context.Attributes; // no change } // prepare the command Command cmdEnable = ExchangeUtility.GetCommand(cmdInfoEnable, enhancedAttributes, uid, (ExchangeConfiguration)context.ConnectorConfiguration); Command cmdSet = ExchangeUtility.GetCommand(cmdInfoSet, enhancedAttributes, uid, (ExchangeConfiguration)context.ConnectorConfiguration); try { _helper.InvokePipeline(exconn, cmdEnable); _helper.InvokePipeline(exconn, cmdSet); } catch { LOGGER.TraceEvent(TraceEventType.Information, CAT_DEFAULT, "Rolling back AD create for UID: " + uid.GetUidValue()); // rollback AD create try { adconn.Delete(context.ObjectClass, uid, context.Options); } catch { LOGGER.TraceEvent(TraceEventType.Warning, CAT_DEFAULT, "Not able to rollback AD create for UID: " + uid.GetUidValue()); // note: this is not perfect, we hide the original exception throw; } // rethrow original exception throw; } context.Uid = uid; }