From a01d9b90fa4d94fe81c1a2c6e6ddb1f83a308829 Mon Sep 17 00:00:00 2001 From: ryannewington Date: Tue, 6 Sep 2016 13:48:57 +1000 Subject: [PATCH] Fixes an issue where adding members during group creation can fail with message 'Resource Not Found: groupKey [404]' (#7) Fixes an issue where an Exported-change-not-reimported error occurs after deleting the last alias (#8) --- src/Lithnet.GoogleApps.MA.Setup/Product.wxs | 2 +- .../GroupTests.cs | 79 ++++++++++++++++++- .../ApiInterfaces/ApiInterfaceGroup.cs | 5 +- .../ApiInterfaces/ApiInterfaceGroupAliases.cs | 20 +++-- .../ApiInterfaceGroupMembership.cs | 14 +++- .../ApiInterfaces/ApiInterfaceUserAliases.cs | 19 ++++- .../Lithnet.GoogleApps.MA.csproj | 1 + 7 files changed, 127 insertions(+), 13 deletions(-) diff --git a/src/Lithnet.GoogleApps.MA.Setup/Product.wxs b/src/Lithnet.GoogleApps.MA.Setup/Product.wxs index 2f20eed..76fd661 100644 --- a/src/Lithnet.GoogleApps.MA.Setup/Product.wxs +++ b/src/Lithnet.GoogleApps.MA.Setup/Product.wxs @@ -3,7 +3,7 @@ diff --git a/src/Lithnet.GoogleApps.MA.UnitTests/GroupTests.cs b/src/Lithnet.GoogleApps.MA.UnitTests/GroupTests.cs index e15f31f..95bc1d4 100644 --- a/src/Lithnet.GoogleApps.MA.UnitTests/GroupTests.cs +++ b/src/Lithnet.GoogleApps.MA.UnitTests/GroupTests.cs @@ -235,7 +235,7 @@ public void Update() } } } - + [TestMethod] public void Delete() { @@ -628,6 +628,80 @@ public void ReplaceAliases() } + [TestMethod] + public void AddGroupWithMembers() + { + string dn = $"{Guid.NewGuid()}@{UnitTestControl.TestParameters.Domain}"; + + CSEntryChange cs = CSEntryChange.Create(); + cs.ObjectModificationType = ObjectModificationType.Add; + cs.DN = dn; + cs.ObjectType = SchemaConstants.Group; + cs.AttributeChanges.Add(AttributeChange.CreateAttributeAdd("name", Guid.NewGuid().ToString())); + + Group group1; + Group group2; + string member1 = this.CreateGroup(out group1); + string member2 = this.CreateGroup(out group2); + string member3 = $"{Guid.NewGuid()}@{UnitTestControl.TestParameters.Domain}"; + + ManagedObjects.User e = new ManagedObjects.User + { + PrimaryEmail = member3, + Password = Guid.NewGuid().ToString(), + Name = + { + GivenName = "test", + FamilyName = "test" + } + }; + + e = UserRequestFactory.Add(e); + + cs.AttributeChanges.Add(AttributeChange.CreateAttributeAdd("member", new List() { member1, member2 })); + cs.AttributeChanges.Add(AttributeChange.CreateAttributeAdd("owner", new List() { member3 })); + + CSEntryChangeResult result = null; + + try + { + result = ExportProcessor.PutCSEntryChange(cs, UnitTestControl.Schema.GetSchema().Types[SchemaConstants.Group]); + + if (result.ErrorCode != MAExportError.Success) + { + Assert.Fail(result.ErrorName); + } + + System.Threading.Thread.Sleep(10000); + + CollectionAssert.AreEquivalent(new string[] { member1, member2 }, GroupMemberRequestFactory.GetMembership(cs.DN).Members.ToArray()); + } + finally + { + string id = result?.AnchorAttributes.FirstOrDefault()?.GetValueAdd(); + + if (id != null) + { + GroupRequestFactory.Delete(id); + } + + if (group1?.Id != null) + { + GroupRequestFactory.Delete(group1.Id); + } + + if (group2?.Id != null) + { + GroupRequestFactory.Delete(group2.Id); + } + + if (e?.Id != null) + { + UserRequestFactory.Delete(e.Id); + } + } + } + [TestMethod] public void AddMembers() { @@ -668,6 +742,9 @@ public void AddMembers() { GroupRequestFactory.Delete(id); } + + GroupRequestFactory.Delete(member1); + GroupRequestFactory.Delete(member2); } } diff --git a/src/Lithnet.GoogleApps.MA/ApiInterfaces/ApiInterfaceGroup.cs b/src/Lithnet.GoogleApps.MA/ApiInterfaces/ApiInterfaceGroup.cs index f631fc9..233276f 100644 --- a/src/Lithnet.GoogleApps.MA/ApiInterfaces/ApiInterfaceGroup.cs +++ b/src/Lithnet.GoogleApps.MA/ApiInterfaces/ApiInterfaceGroup.cs @@ -88,6 +88,9 @@ public IList ApplyChanges(CSEntryChange csentry, SchemaType typ { result.Group = GroupRequestFactory.Add(group.Group); group.Group = result.Group; + + // Group membership operations fail on newly created groups if processed too quickly + System.Threading.Thread.Sleep(1000); } else if (csentry.ObjectModificationType == ObjectModificationType.Replace || csentry.ObjectModificationType == ObjectModificationType.Update) { @@ -111,7 +114,7 @@ public IList ApplyChanges(CSEntryChange csentry, SchemaType typ changes.AddRange(this.GetLocalChanges(csentry.DN, csentry.ObjectModificationType, type, result)); } - + foreach (IApiInterface i in ApiInterfaceGroup.internalInterfaces) { foreach (AttributeChange c in i.ApplyChanges(csentry, type, ref target, patch)) diff --git a/src/Lithnet.GoogleApps.MA/ApiInterfaces/ApiInterfaceGroupAliases.cs b/src/Lithnet.GoogleApps.MA/ApiInterfaces/ApiInterfaceGroupAliases.cs index 44dd487..c8dfdd8 100644 --- a/src/Lithnet.GoogleApps.MA/ApiInterfaces/ApiInterfaceGroupAliases.cs +++ b/src/Lithnet.GoogleApps.MA/ApiInterfaces/ApiInterfaceGroupAliases.cs @@ -51,11 +51,12 @@ public IList GetChanges(string dn, ObjectModificationType modTy return attributeChanges; } - private static void GetGroupAliasChanges(CSEntryChange csentry, Group group, out IList aliasAdds, out IList aliasDeletes) + private static void GetGroupAliasChanges(CSEntryChange csentry, out IList aliasAdds, out IList aliasDeletes, out bool deletingAll) { aliasAdds = new List(); aliasDeletes = new List(); AttributeChange change = csentry.AttributeChanges.FirstOrDefault(t => t.Name == "aliases"); + deletingAll = false; if (csentry.ObjectModificationType == ObjectModificationType.Replace) { @@ -87,6 +88,8 @@ private static void GetGroupAliasChanges(CSEntryChange csentry, Group group, out { aliasDeletes.Add(alias); } + + deletingAll = true; break; case AttributeModificationType.Replace: @@ -113,8 +116,9 @@ private static AttributeChange ApplyGroupAliasChanges(CSEntryChange csentry, Gro { IList aliasAdds; IList aliasDeletes; + bool deletingAll; - ApiInterfaceGroupAliases.GetGroupAliasChanges(csentry, group, out aliasAdds, out aliasDeletes); + ApiInterfaceGroupAliases.GetGroupAliasChanges(csentry, out aliasAdds, out aliasDeletes, out deletingAll); if (aliasAdds.Count == 0 && aliasDeletes.Count == 0) { @@ -157,7 +161,14 @@ private static AttributeChange ApplyGroupAliasChanges(CSEntryChange csentry, Gro { if (csentry.ObjectModificationType == ObjectModificationType.Update) { - change = AttributeChange.CreateAttributeUpdate("aliases", valueChanges); + if (deletingAll && valueChanges.Count == aliasDeletes?.Count) + { + change = AttributeChange.CreateAttributeDelete("aliases"); + } + else + { + change = AttributeChange.CreateAttributeUpdate("aliases", valueChanges); + } } else { @@ -169,5 +180,4 @@ private static AttributeChange ApplyGroupAliasChanges(CSEntryChange csentry, Gro return change; } } -} - +} \ No newline at end of file diff --git a/src/Lithnet.GoogleApps.MA/ApiInterfaces/ApiInterfaceGroupMembership.cs b/src/Lithnet.GoogleApps.MA/ApiInterfaces/ApiInterfaceGroupMembership.cs index caaeddd..091b811 100644 --- a/src/Lithnet.GoogleApps.MA/ApiInterfaces/ApiInterfaceGroupMembership.cs +++ b/src/Lithnet.GoogleApps.MA/ApiInterfaces/ApiInterfaceGroupMembership.cs @@ -55,6 +55,12 @@ public IList ApplyChanges(CSEntryChange csentry, SchemaType typ } catch (AggregateGroupUpdateException ex) { + Logger.WriteLine("The following member removals failed"); + foreach (Exception e in ex.Exceptions) + { + Logger.WriteException(e); + } + ApiInterfaceGroupMembership.AddAttributeChange("member", modificationType, membershipToDelete.Members.Except(ex.FailedMembers).ToValueChange(ValueModificationType.Delete), changes); ApiInterfaceGroupMembership.AddAttributeChange("externalMember", modificationType, membershipToDelete.ExternalMembers.Except(ex.FailedMembers).ToValueChange(ValueModificationType.Delete), changes); ApiInterfaceGroupMembership.AddAttributeChange("manager", modificationType, membershipToDelete.Managers.Except(ex.FailedMembers).ToValueChange(ValueModificationType.Delete), changes); @@ -94,6 +100,12 @@ public IList ApplyChanges(CSEntryChange csentry, SchemaType typ } catch (AggregateGroupUpdateException ex) { + Logger.WriteLine("The following member additions failed"); + foreach(Exception e in ex.Exceptions) + { + Logger.WriteException(e); + } + ApiInterfaceGroupMembership.AddAttributeChange("member", modificationType, membershipToAdd.Members.Except(ex.FailedMembers).ToValueChange(ValueModificationType.Add), changes); ApiInterfaceGroupMembership.AddAttributeChange("externalMember", modificationType, membershipToAdd.ExternalMembers.Except(ex.FailedMembers).ToValueChange(ValueModificationType.Add), changes); ApiInterfaceGroupMembership.AddAttributeChange("manager", modificationType, membershipToAdd.Managers.Except(ex.FailedMembers).ToValueChange(ValueModificationType.Add), changes); @@ -228,7 +240,7 @@ private static void GetMemberChangesFromCSEntryChange(CSEntryChange csentry, out { adds = new GroupMembership(); deletes = new GroupMembership(); - GroupMembership existingGroupMembership = null; + GroupMembership existingGroupMembership; if (ApiInterfaceGroupMembership.ExistingMembershipRequiredForUpdate(csentry) | replacing) { diff --git a/src/Lithnet.GoogleApps.MA/ApiInterfaces/ApiInterfaceUserAliases.cs b/src/Lithnet.GoogleApps.MA/ApiInterfaces/ApiInterfaceUserAliases.cs index e618f8a..59e6c94 100644 --- a/src/Lithnet.GoogleApps.MA/ApiInterfaces/ApiInterfaceUserAliases.cs +++ b/src/Lithnet.GoogleApps.MA/ApiInterfaces/ApiInterfaceUserAliases.cs @@ -43,11 +43,12 @@ public IList GetChanges(string dn, ObjectModificationType modTy return attributeChanges; } - private static void GetUserAliasChanges(CSEntryChange csentry, User user, out IList aliasAdds, out IList aliasDeletes) + private static void GetUserAliasChanges(CSEntryChange csentry, out IList aliasAdds, out IList aliasDeletes, out bool deletingAll) { aliasAdds = new List(); aliasDeletes = new List(); AttributeChange change = csentry.AttributeChanges.FirstOrDefault(t => t.Name == "aliases"); + deletingAll = false; if (csentry.ObjectModificationType == ObjectModificationType.Replace) { @@ -79,6 +80,8 @@ private static void GetUserAliasChanges(CSEntryChange csentry, User user, out IL { aliasDeletes.Add(alias); } + + deletingAll = true; break; case AttributeModificationType.Replace: @@ -105,8 +108,9 @@ private static AttributeChange ApplyUserAliasChanges(CSEntryChange csentry, User { IList aliasAdds; IList aliasDeletes; + bool deletingAll; - ApiInterfaceUserAliases.GetUserAliasChanges(csentry, user, out aliasAdds, out aliasDeletes); + ApiInterfaceUserAliases.GetUserAliasChanges(csentry, out aliasAdds, out aliasDeletes, out deletingAll); if (aliasAdds.Count == 0 && aliasDeletes.Count == 0) { @@ -165,7 +169,14 @@ private static AttributeChange ApplyUserAliasChanges(CSEntryChange csentry, User { if (csentry.ObjectModificationType == ObjectModificationType.Update) { - change = AttributeChange.CreateAttributeUpdate("aliases", valueChanges); + if (deletingAll && valueChanges.Count == aliasDeletes?.Count) + { + change = AttributeChange.CreateAttributeDelete("aliases"); + } + else + { + change = AttributeChange.CreateAttributeUpdate("aliases", valueChanges); + } } else { @@ -177,4 +188,4 @@ private static AttributeChange ApplyUserAliasChanges(CSEntryChange csentry, User return change; } } -} +} \ No newline at end of file diff --git a/src/Lithnet.GoogleApps.MA/Lithnet.GoogleApps.MA.csproj b/src/Lithnet.GoogleApps.MA/Lithnet.GoogleApps.MA.csproj index 48ecd11..ed811f9 100644 --- a/src/Lithnet.GoogleApps.MA/Lithnet.GoogleApps.MA.csproj +++ b/src/Lithnet.GoogleApps.MA/Lithnet.GoogleApps.MA.csproj @@ -109,6 +109,7 @@ +