From 2163398328fd89b23818d16e3e7f5e577a238158 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francesco=20Chicchiricc=C3=B2?= Date: Fri, 6 Dec 2024 11:23:32 +0100 Subject: [PATCH] [GOOGLEAPPS-24] #resolve --- .../bundles/googleapps/GoogleAppsCreate.java | 16 +- .../bundles/googleapps/GoogleAppsDelete.java | 13 - .../bundles/googleapps/GoogleAppsSearch.java | 6 +- .../bundles/googleapps/GoogleAppsUpdate.java | 996 +++++++++++------- .../bundles/googleapps/GroupHandler.java | 49 +- .../bundles/googleapps/UserHandler.java | 30 + 6 files changed, 710 insertions(+), 400 deletions(-) diff --git a/src/main/java/net/tirasa/connid/bundles/googleapps/GoogleAppsCreate.java b/src/main/java/net/tirasa/connid/bundles/googleapps/GoogleAppsCreate.java index 75c21af..86bb105 100644 --- a/src/main/java/net/tirasa/connid/bundles/googleapps/GoogleAppsCreate.java +++ b/src/main/java/net/tirasa/connid/bundles/googleapps/GoogleAppsCreate.java @@ -236,7 +236,7 @@ public String handleResult(final Directory.Groups.Aliases.Insert request, final } } } - + List members = accessor.findList(GoogleAppsUtil.MEMBERS_ATTR); if (null != members) { final Directory.Members membersService = configuration.getDirectory().members(); @@ -309,20 +309,6 @@ public Uid handleResult(final Directory.Orgunits.Insert request, final OrgUnit v } if (GoogleAppsUtil.LICENSE_ASSIGNMENT.equals(objectClass)) { - // @formatter:off - /* AlreadyExistsException - * { - * "code" : 400, - * "errors" : [ { - * "domain" : "global", - * "message" : "Invalid Ou Id", - * "reason" : "invalid" - * } ], - * "message" : "Invalid Ou Id" - * } - */ - // @formatter:on - return GoogleApiExecutor.execute( LicenseAssignmentsHandler.create(configuration.getLicensing().licenseAssignments(), accessor), new RequestResultHandler() { diff --git a/src/main/java/net/tirasa/connid/bundles/googleapps/GoogleAppsDelete.java b/src/main/java/net/tirasa/connid/bundles/googleapps/GoogleAppsDelete.java index f09067b..782d64b 100644 --- a/src/main/java/net/tirasa/connid/bundles/googleapps/GoogleAppsDelete.java +++ b/src/main/java/net/tirasa/connid/bundles/googleapps/GoogleAppsDelete.java @@ -63,19 +63,6 @@ public void execute() { } else if (ObjectClass.GROUP.equals(objectClass)) { directoryRequest = configuration.getDirectory().groups().delete(uid.getUidValue()); } else if (GoogleAppsUtil.MEMBER.equals(objectClass)) { - // @formatter:off - /* Already deleted - * { - * "code" : 400, - * "errors" : [ { - * "domain" : "global", - * "message" : "Missing required field: memberKey", - * "reason" : "required" - * } ], - * "message" : "Missing required field: memberKey" - * } - */ - // @formatter:on String[] ids = uid.getUidValue().split("/"); if (ids.length == 2) { directoryRequest = configuration.getDirectory().members().delete(ids[0], ids[1]); diff --git a/src/main/java/net/tirasa/connid/bundles/googleapps/GoogleAppsSearch.java b/src/main/java/net/tirasa/connid/bundles/googleapps/GoogleAppsSearch.java index d6b16b7..ee1742f 100644 --- a/src/main/java/net/tirasa/connid/bundles/googleapps/GoogleAppsSearch.java +++ b/src/main/java/net/tirasa/connid/bundles/googleapps/GoogleAppsSearch.java @@ -319,8 +319,10 @@ public String handleResult(final Directory.Users.List request, final Users value if (null != value.getUsers()) { for (User user : value.getUsers()) { handler.handle(UserHandler.fromUser( - configuration, user, attributesToGet, configuration.getDirectory(). - groups())); + configuration, + user, + attributesToGet, + configuration.getDirectory().groups())); } } return value.getNextPageToken(); diff --git a/src/main/java/net/tirasa/connid/bundles/googleapps/GoogleAppsUpdate.java b/src/main/java/net/tirasa/connid/bundles/googleapps/GoogleAppsUpdate.java index 40ae78e..67b2ac7 100644 --- a/src/main/java/net/tirasa/connid/bundles/googleapps/GoogleAppsUpdate.java +++ b/src/main/java/net/tirasa/connid/bundles/googleapps/GoogleAppsUpdate.java @@ -27,6 +27,7 @@ import static net.tirasa.connid.bundles.googleapps.MembersHandler.listMembers; import com.google.api.services.directory.Directory; +import com.google.api.services.directory.model.Alias; import com.google.api.services.directory.model.Group; import com.google.api.services.directory.model.Member; import com.google.api.services.directory.model.OrgUnit; @@ -75,191 +76,349 @@ public GoogleAppsUpdate( this.uid = uid; } - public Uid update(final Set replaceAttributes) { - final AttributesAccessor accessor = new AttributesAccessor(replaceAttributes); - + private Uid updateUser(final AttributesAccessor accessor) { Uid uidAfterUpdate = uid; - if (ObjectClass.ACCOUNT.equals(objectClass)) { - Directory.Users.Patch patch = UserHandler.updateUser( - configuration.getDirectory().users(), - uid.getUidValue(), - accessor, - configuration.getCustomSchemasJSON()); - if (null != patch) { - uidAfterUpdate = execute(patch, new RequestResultHandler() { - @Override - public Uid handleResult(final Directory.Users.Patch request, final User value) { - LOG.ok("User is Updated:{0}", value.getId()); - return new Uid(value.getId(), value.getEtag()); + Directory.Users.Patch patch = UserHandler.updateUser( + configuration.getDirectory().users(), + uid.getUidValue(), + accessor, + configuration.getCustomSchemasJSON()); + if (null != patch) { + uidAfterUpdate = execute(patch, new RequestResultHandler() { + + @Override + public Uid handleResult(final Directory.Users.Patch request, final User value) { + LOG.ok("User is Updated:{0}", value.getId()); + return new Uid(value.getId(), value.getEtag()); + } + }); + } + + List aliases = accessor.findList(GoogleAppsUtil.ALIASES_ATTR); + if (null != aliases) { + Directory.Users.Aliases service = configuration.getDirectory().users().aliases(); + Set currentAliases = UserHandler.listAliases(service, uidAfterUpdate.getUidValue()); + + if (aliases.isEmpty()) { + // Remove all aliases + for (Object alias : currentAliases) { + execute(UserHandler.deleteUserAlias(service, uidAfterUpdate.getUidValue(), (String) alias), + new RequestResultHandler() { + + @Override + public Object handleResult(final Directory.Users.Aliases.Delete request, final Void value) { + return null; + } + + @Override + public Object handleNotFound(final IOException e) { + // It may be an indirect membership, not able to delete + return null; + } + }); + } + } else { + List addAliases = new ArrayList<>(); + Set keepAliases = CollectionUtil.newCaseInsensitiveSet(); + + for (Object alias : aliases) { + if (currentAliases.contains(alias.toString())) { + keepAliases.add((String) alias); + } else { + addAliases.add( + UserHandler.createUserAlias(service, uidAfterUpdate.getUidValue(), (String) alias)); } - }); - } + } - Attribute groups = accessor.find(PredefinedAttributes.GROUPS_NAME); - if (null != groups && null != groups.getValue()) { - final Directory.Members service = configuration.getDirectory().members(); - if (groups.getValue().isEmpty()) { - // Remove all membership - for (String groupKey : listGroups( - configuration.getDirectory().groups(), - uidAfterUpdate.getUidValue(), - configuration.getDomain())) { + // Add new alias + for (Directory.Users.Aliases.Insert insert : addAliases) { + execute(insert, new RequestResultHandler() { - execute(MembersHandler.delete(service, groupKey, uidAfterUpdate.getUidValue()), - new RequestResultHandler() { + @Override + public Object handleResult(final Directory.Users.Aliases.Insert insert, final Alias value) { + return null; + } + + @Override + public Object handleDuplicate(final IOException e) { + return null; + } + }); + } + + // Delete existing aliases + if (currentAliases.removeAll(keepAliases)) { + for (Object alias : currentAliases) { + execute(UserHandler.deleteUserAlias(service, (String) alias, uidAfterUpdate.getUidValue()), + new RequestResultHandler() { @Override - public Object handleResult(final Directory.Members.Delete request, final Void value) { + public Object handleResult(final Directory.Users.Aliases.Delete request, final Void value) { return null; } @Override public Object handleNotFound(final IOException e) { - // It may be an indirect membership, not able to delete return null; } }); } - } else { - final Set activeGroups = listGroups( - configuration.getDirectory().groups(), - uidAfterUpdate.getUidValue(), - configuration.getDomain()); - - final List addGroups = new ArrayList<>(); - final Set keepGroups = CollectionUtil.newCaseInsensitiveSet(); - - for (Object member : groups.getValue()) { - if (member instanceof String) { - if (activeGroups.contains((String) member)) { - keepGroups.add((String) member); - } else { - String email = accessor.getName().getNameValue(); - addGroups.add(MembersHandler.create(service, (String) member, email, null)); - } - } else if (null != member) { - // throw error/revert? - throw new InvalidAttributeValueException("Attribute '__GROUPS__' must be a String list"); + } + } + } + + Attribute groups = accessor.find(PredefinedAttributes.GROUPS_NAME); + if (null != groups && null != groups.getValue()) { + Directory.Members service = configuration.getDirectory().members(); + Set currentGroups = listGroups( + configuration.getDirectory().groups(), uidAfterUpdate.getUidValue(), configuration.getDomain()); + + if (groups.getValue().isEmpty()) { + // Remove all membership + for (String groupKey : currentGroups) { + execute(MembersHandler.delete(service, groupKey, uidAfterUpdate.getUidValue()), + new RequestResultHandler() { + + @Override + public Object handleResult(final Directory.Members.Delete request, final Void value) { + return null; + } + + @Override + public Object handleNotFound(final IOException e) { + // It may be an indirect membership, not able to delete + return null; + } + }); + } + } else { + List addGroups = new ArrayList<>(); + Set keepGroups = CollectionUtil.newCaseInsensitiveSet(); + + for (Object member : groups.getValue()) { + if (member instanceof String) { + if (currentGroups.contains((String) member)) { + keepGroups.add((String) member); + } else { + String email = accessor.getName().getNameValue(); + addGroups.add(MembersHandler.create(service, (String) member, email, null)); } + } else if (null != member) { + // throw error/revert? + throw new InvalidAttributeValueException("Attribute '__GROUPS__' must be a String list"); } + } - // Add new Member object - for (Directory.Members.Insert insert : addGroups) { - execute(insert, new RequestResultHandler() { + // Add new Member object + for (Directory.Members.Insert insert : addGroups) { + execute(insert, new RequestResultHandler() { + + @Override + public Object handleResult(final Directory.Members.Insert request, final Member value) { + return null; + } + + @Override + public Object handleDuplicate(final IOException e) { + // Do nothing + return null; + } + }); + } + + // Delete existing Member object + if (currentGroups.removeAll(keepGroups)) { + for (String groupKey : currentGroups) { + execute(MembersHandler.delete(service, groupKey, uidAfterUpdate.getUidValue()), + new RequestResultHandler() { @Override - public Object handleResult(final Directory.Members.Insert request, final Member value) { + public Object handleResult(final Directory.Members.Delete request, final Void value) { return null; } @Override - public Object handleDuplicate(final IOException e) { - // Do nothing + public Object handleNotFound(final IOException e) { + // It may be an indirect membership, not able to delete return null; } }); } + } + } + } - // Delete existing Member object - if (activeGroups.removeAll(keepGroups)) { - for (String groupKey : activeGroups) { - execute(MembersHandler.delete(service, groupKey, uidAfterUpdate.getUidValue()), - new RequestResultHandler() { + // GOOGLEAPPS-9 + // license management: if remove license param is true and __ENABLE__ is false perform delete license + // license read must be performed with the user primaryEmail, userId is not allowed + if (configuration.getRemoveLicenseOnDisable() + && accessor.hasAttribute(OperationalAttributes.ENABLE_NAME) + && !accessor.findBoolean(OperationalAttributes.ENABLE_NAME) + && StringUtil.isNotBlank(accessor.findString(GoogleAppsUtil.PRIMARY_EMAIL_ATTR))) { + + for (String skuId : configuration.getSkuIds()) { + // 1. retrieve user license + try { + // use email as key + Licensing.LicenseAssignments.Get request = + configuration.getLicensing().licenseAssignments().get( + configuration.getProductId(), + skuId, + accessor.findString(GoogleAppsUtil.PRIMARY_EMAIL_ATTR)); + execute(request, + new RequestResultHandler() { - @Override - public Object handleResult(final Directory.Members.Delete request, final Void value) { - return null; - } + @Override + public Boolean handleResult( + final Licensing.LicenseAssignments.Get request, + final LicenseAssignment value) { + + try { + // 2. remove license + new GoogleAppsDelete( + configuration, + GoogleAppsUtil.LICENSE_ASSIGNMENT, + new Uid(GoogleAppsUtil.generateLicenseId( + value.getProductId(), + value.getSkuId(), + value.getUserId()))).execute(); + } catch (Exception e) { + LOG.error(e, "Failed to delete license for user {0}", value.getUserId()); + throw ConnectorException.wrap(e); + } + return true; + } - @Override - public Object handleNotFound(final IOException e) { - // It may be an indirect membership, not able to delete - return null; - } - }); + @Override + public Boolean handleNotFound(final IOException e) { + // Do nothing if not found + return true; } - } + }); + } catch (IOException e) { + LOG.error(e, "Unable to find license for {0}-{1}-{2}", configuration.getProductId(), skuId, + accessor.findString(GoogleAppsUtil.PRIMARY_EMAIL_ATTR)); } } + } - // GOOGLEAPPS-9 - // license management: if remove license param is true and __ENABLE__ is false perform delete license - // license read must be performed with the user primaryEmail, userId is not allowed - if (configuration.getRemoveLicenseOnDisable() - && accessor.hasAttribute(OperationalAttributes.ENABLE_NAME) - && !accessor.findBoolean(OperationalAttributes.ENABLE_NAME) - && StringUtil.isNotBlank(accessor.findString(GoogleAppsUtil.PRIMARY_EMAIL_ATTR))) { - for (String skuId : configuration.getSkuIds()) { - // 1. retrieve user license - try { - // use email as key - Licensing.LicenseAssignments.Get request = - configuration.getLicensing().licenseAssignments().get( - configuration.getProductId(), - skuId, - accessor.findString(GoogleAppsUtil.PRIMARY_EMAIL_ATTR)); - execute(request, - new RequestResultHandler< - Licensing.LicenseAssignments.Get, LicenseAssignment, Boolean>() { + return uidAfterUpdate; + } - @Override - public Boolean handleResult( - final Licensing.LicenseAssignments.Get request, - final LicenseAssignment value) { - - try { - // 2. remove license - new GoogleAppsDelete( - configuration, - GoogleAppsUtil.LICENSE_ASSIGNMENT, - new Uid(GoogleAppsUtil.generateLicenseId( - value.getProductId(), - value.getSkuId(), - value.getUserId()))).execute(); - } catch (Exception e) { - LOG.error(e, "Failed to delete license for user {0}", value.getUserId()); - throw ConnectorException.wrap(e); - } - return true; - } + private Uid updateGroup(final AttributesAccessor accessor) { + Uid uidAfterUpdate = uid; - @Override - public Boolean handleNotFound(final IOException e) { - // Do nothing if not found - return true; + Directory.Groups.Patch patch = GroupHandler.update( + configuration.getDirectory().groups(), uid.getUidValue(), accessor); + if (null != patch) { + uidAfterUpdate = execute(patch, new RequestResultHandler() { + + @Override + public Uid handleResult(final Directory.Groups.Patch request, final Group value) { + LOG.ok("Group is Updated:{0}", value.getId()); + return new Uid(value.getId(), value.getEtag()); + } + }); + } + + Attribute members = accessor.find(GoogleAppsUtil.MEMBERS_ATTR); + if (null != members && null != members.getValue()) { + Directory.Members service = configuration.getDirectory().members(); + if (members.getValue().isEmpty()) { + // Remove all membership + for (Map member : listMembers(service, uidAfterUpdate.getUidValue(), null)) { + execute(MembersHandler.delete( + service, uidAfterUpdate.getUidValue(), member.get(GoogleAppsUtil.EMAIL_ATTR)), + new RequestResultHandler() { + + @Override + public Object handleResult(final Directory.Members.Delete request, final Void value) { + return null; + } + + @Override + public Object handleNotFound(final IOException e) { + // Do nothing + return null; + } + }); + } + } else { + List> activeMembership = listMembers(service, uidAfterUpdate.getUidValue(), null); + + List addMembership = new ArrayList<>(); + List patchMembership = new ArrayList<>(); + + for (Object member : members.getValue()) { + if (member instanceof Map) { + String email = (String) ((Map) member).get(GoogleAppsUtil.EMAIL_ATTR); + if (null == email) { + continue; + } + String role = (String) ((Map) member).get(GoogleAppsUtil.ROLE_ATTR); + if (null == role) { + role = "MEMBER"; + } + + boolean notMember = true; + for (Map a : activeMembership) { + // How to handle ROLE update? + // OWNER -> MANAGER -> MEMBER + if (email.equalsIgnoreCase(a.get(GoogleAppsUtil.EMAIL_ATTR))) { + a.put("keep", null); + if (!role.equalsIgnoreCase(a.get(GoogleAppsUtil.ROLE_ATTR))) { + patchMembership.add(MembersHandler.update( + service, uidAfterUpdate.getUidValue(), email, role)); + } + notMember = false; + break; } - }); - } catch (IOException e) { - LOG.error(e, "Unable to find license for {0}-{1}-{2}", configuration.getProductId(), skuId, - accessor.findString(GoogleAppsUtil.PRIMARY_EMAIL_ATTR)); + } + if (notMember) { + addMembership.add(MembersHandler.create( + service, uidAfterUpdate.getUidValue(), email, role)); + } + } else if (null != member) { + // throw error/revert? + throw new InvalidAttributeValueException( + "Attribute 'members' must be a Map list"); } } - } - } else if (ObjectClass.GROUP.equals(objectClass)) { - final Directory.Groups.Patch patch = GroupHandler.update( - configuration.getDirectory().groups(), - uid.getUidValue(), - accessor); - if (null != patch) { - uidAfterUpdate = execute(patch, new RequestResultHandler() { - @Override - public Uid handleResult(final Directory.Groups.Patch request, final Group value) { - LOG.ok("Group is Updated:{0}", value.getEmail()); - return new Uid(value.getEmail(), value.getEtag()); - } - }); - } + // Add new Member object + for (Directory.Members.Insert insert : addMembership) { + execute(insert, new RequestResultHandler() { + + @Override + public Object handleResult(final Directory.Members.Insert request, final Member value) { + return null; + } + + @Override + public Object handleDuplicate(final IOException e) { + // Do nothing + return null; + } + }); + } + + // Update existing Member object + for (Directory.Members.Patch request : patchMembership) { + execute(request, new RequestResultHandler() { - Attribute members = accessor.find(GoogleAppsUtil.MEMBERS_ATTR); - if (null != members && null != members.getValue()) { - final Directory.Members service = configuration.getDirectory().members(); - if (members.getValue().isEmpty()) { - // Remove all membership - for (Map member : listMembers(service, uidAfterUpdate.getUidValue(), null)) { + @Override + public Object handleResult(final Directory.Members.Patch request, final Member value) { + return null; + } + }); + } + + // Delete existing Member object + for (Map am : activeMembership) { + if (!am.containsKey("keep")) { execute(MembersHandler.delete( - service, uidAfterUpdate.getUidValue(), member.get(GoogleAppsUtil.EMAIL_ATTR)), + service, uidAfterUpdate.getUidValue(), am.get(GoogleAppsUtil.EMAIL_ATTR)), new RequestResultHandler() { @Override @@ -274,105 +433,100 @@ public Object handleNotFound(final IOException e) { } }); } - } else { - final List> activeMembership = - listMembers(service, uidAfterUpdate.getUidValue(), null); + } + } + } - final List addMembership = new ArrayList<>(); - final List patchMembership = new ArrayList<>(); + List aliases = accessor.findList(GoogleAppsUtil.ALIASES_ATTR); + if (null != aliases) { + Directory.Groups.Aliases service = configuration.getDirectory().groups().aliases(); + Set currentAliases = GroupHandler.listAliases(service, uidAfterUpdate.getUidValue()); - for (Object member : members.getValue()) { - if (member instanceof Map) { - String email = (String) ((Map) member).get(GoogleAppsUtil.EMAIL_ATTR); - if (null == email) { - continue; - } - String role = (String) ((Map) member).get(GoogleAppsUtil.ROLE_ATTR); - if (null == role) { - role = "MEMBER"; - } + if (aliases.isEmpty()) { + // Remove all aliases + for (Object alias : currentAliases) { + execute(GroupHandler.deleteGroupAlias(service, uidAfterUpdate.getUidValue(), (String) alias), + new RequestResultHandler() { - boolean notMember = true; - for (Map a : activeMembership) { - // How to handle ROLE update? - // OWNER -> MANAGER -> MEMBER - if (email.equalsIgnoreCase(a.get(GoogleAppsUtil.EMAIL_ATTR))) { - a.put("keep", null); - if (!role.equalsIgnoreCase(a.get(GoogleAppsUtil.ROLE_ATTR))) { - patchMembership.add(MembersHandler.update( - service, uidAfterUpdate.getUidValue(), email, role)); - } - notMember = false; - break; - } - } - if (notMember) { - addMembership.add(MembersHandler.create( - service, uidAfterUpdate.getUidValue(), email, role)); - } - } else if (null != member) { - // throw error/revert? - throw new InvalidAttributeValueException( - "Attribute 'members' must be a Map list"); + @Override + public Object handleResult(final Directory.Groups.Aliases.Delete request, final Void value) { + return null; + } + + @Override + public Object handleNotFound(final IOException e) { + // It may be an indirect membership, not able to delete + return null; } + }); + } + } else { + List addAliases = new ArrayList<>(); + Set keepAliases = CollectionUtil.newCaseInsensitiveSet(); + + for (Object alias : aliases) { + if (currentAliases.contains(alias.toString())) { + keepAliases.add((String) alias); + } else { + addAliases.add( + GroupHandler.createGroupAlias(service, uidAfterUpdate.getUidValue(), (String) alias)); } + } - // Add new Member object - for (Directory.Members.Insert insert : addMembership) { - execute(insert, new RequestResultHandler() { + // Add new alias + for (Directory.Groups.Aliases.Insert insert : addAliases) { + execute(insert, new RequestResultHandler() { - @Override - public Object handleResult(final Directory.Members.Insert request, final Member value) { - return null; - } + @Override + public Object handleResult(final Directory.Groups.Aliases.Insert insert, final Alias value) { + return null; + } + + @Override + public Object handleDuplicate(final IOException e) { + return null; + } + }); + } + + // Delete existing aliases + if (currentAliases.removeAll(keepAliases)) { + for (Object alias : currentAliases) { + execute(GroupHandler.deleteGroupAlias(service, (String) alias, uidAfterUpdate.getUidValue()), + new RequestResultHandler() { @Override - public Object handleDuplicate(final IOException e) { - // Do nothing + public Object handleResult(final Directory.Groups.Aliases.Delete request, final Void v) { return null; } - }); - } - - // Update existing Member object - for (Directory.Members.Patch request : patchMembership) { - execute(request, new RequestResultHandler() { @Override - public Object handleResult(final Directory.Members.Patch request, final Member value) { + public Object handleNotFound(final IOException e) { return null; } }); } + } + } + } - // Delete existing Member object - for (Map am : activeMembership) { - if (!am.containsKey("keep")) { - execute(MembersHandler.delete( - service, uidAfterUpdate.getUidValue(), am.get(GoogleAppsUtil.EMAIL_ATTR)), - new RequestResultHandler() { + return uidAfterUpdate; + } - @Override - public Object handleResult(final Directory.Members.Delete request, final Void value) { - return null; - } + public Uid update(final Set replaceAttributes) { + AttributesAccessor accessor = new AttributesAccessor(replaceAttributes); - @Override - public Object handleNotFound(final IOException e) { - // Do nothing - return null; - } - }); - } - } - } - } + Uid uidAfterUpdate = uid; + if (ObjectClass.ACCOUNT.equals(objectClass)) { + uidAfterUpdate = updateUser(accessor); + } else if (ObjectClass.GROUP.equals(objectClass)) { + uidAfterUpdate = updateGroup(accessor); } else if (GoogleAppsUtil.MEMBER.equals(objectClass)) { String role = accessor.findString(GoogleAppsUtil.ROLE_ATTR); if (StringUtil.isNotBlank(role)) { String[] ids = uid.getUidValue().split("/"); if (ids.length == 2) { - final Directory.Members.Patch patch = MembersHandler.update( + Directory.Members.Patch patch = MembersHandler.update( configuration.getDirectory().members(), ids[0], ids[1], role). setFields(GoogleAppsUtil.EMAIL_ETAG); uidAfterUpdate = execute(patch, new RequestResultHandler() { @@ -388,7 +542,7 @@ public Uid handleResult(final Directory.Members.Patch request, final Member valu } } } else if (GoogleAppsUtil.ORG_UNIT.equals(objectClass)) { - final Directory.Orgunits.Patch patch = OrgunitsHandler.update( + Directory.Orgunits.Patch patch = OrgunitsHandler.update( configuration.getDirectory().orgunits(), uid.getUidValue(), accessor); if (null != patch) { uidAfterUpdate = execute(patch, new RequestResultHandler() { @@ -401,10 +555,8 @@ public Uid handleResult(final Directory.Orgunits.Patch request, final OrgUnit va }); } } else if (GoogleAppsUtil.LICENSE_ASSIGNMENT.equals(objectClass)) { - final Licensing.LicenseAssignments.Patch patch = LicenseAssignmentsHandler.update( - configuration.getLicensing().licenseAssignments(), - uid.getUidValue(), - accessor); + Licensing.LicenseAssignments.Patch patch = LicenseAssignmentsHandler.update( + configuration.getLicensing().licenseAssignments(), uid.getUidValue(), accessor); if (null != patch) { uidAfterUpdate = execute(patch, new RequestResultHandler() { @@ -429,177 +581,303 @@ public Uid handleResult( return uidAfterUpdate; } - public Set updateDelta(final Set modifications) { - if (ObjectClass.ACCOUNT.equals(objectClass)) { - Directory.Users.Update update = UserHandler.updateUser( - configuration.getDirectory().users(), - uid.getUidValue(), - modifications, - configuration.getCustomSchemasJSON()); - if (null != update) { - execute(update, new RequestResultHandler() { - - @Override - public Uid handleResult(final Directory.Users.Update request, final User value) { - LOG.ok("User is Updated:{0}", value.getId()); - return uid; - } - }); - } + private void updateDeltaUser(final Set modifications) { + Directory.Users.Update update = UserHandler.updateUser( + configuration.getDirectory().users(), + uid.getUidValue(), + modifications, + configuration.getCustomSchemasJSON()); + if (null != update) { + execute(update, new RequestResultHandler() { + + @Override + public Uid handleResult(final Directory.Users.Update request, final User value) { + LOG.ok("User is Updated:{0}", value.getId()); + return uid; + } + }); + } - Set groupsToAdd = new HashSet<>(); - Set groupsToRemove = new HashSet<>(); - Optional.ofNullable(AttributeDeltaUtil.find(PredefinedAttributes.GROUPS_NAME, modifications)). - ifPresent(groups -> { - if (CollectionUtil.isEmpty(groups.getValuesToReplace())) { - if (!CollectionUtil.isEmpty(groups.getValuesToAdd())) { - for (Object group : CollectionUtil.nullAsEmpty(groups.getValuesToAdd())) { - groupsToAdd.add(group.toString()); - } + Set groupsToAdd = new HashSet<>(); + Set groupsToRemove = new HashSet<>(); + Optional.ofNullable(AttributeDeltaUtil.find(PredefinedAttributes.GROUPS_NAME, modifications)). + ifPresent(groups -> { + if (CollectionUtil.isEmpty(groups.getValuesToReplace())) { + if (!CollectionUtil.isEmpty(groups.getValuesToAdd())) { + for (Object group : CollectionUtil.nullAsEmpty(groups.getValuesToAdd())) { + groupsToAdd.add(group.toString()); } + } - if (!CollectionUtil.isEmpty(groups.getValuesToRemove())) { - for (Object group : CollectionUtil.nullAsEmpty(groups.getValuesToRemove())) { - groupsToRemove.add(group.toString()); - } + if (!CollectionUtil.isEmpty(groups.getValuesToRemove())) { + for (Object group : CollectionUtil.nullAsEmpty(groups.getValuesToRemove())) { + groupsToRemove.add(group.toString()); } - } else { - for (String groupKey : listGroups( - configuration.getDirectory().groups(), - uid.getUidValue(), - configuration.getDomain())) { + } + } else { + for (String groupKey : listGroups( + configuration.getDirectory().groups(), + uid.getUidValue(), + configuration.getDomain())) { - groupsToRemove.add(groupKey); - } + groupsToRemove.add(groupKey); + } - if (!CollectionUtil.isEmpty(groups.getValuesToAdd())) { - for (Object group : CollectionUtil.nullAsEmpty(groups.getValuesToReplace())) { - groupsToAdd.add(group.toString()); - } + if (!CollectionUtil.isEmpty(groups.getValuesToAdd())) { + for (Object group : CollectionUtil.nullAsEmpty(groups.getValuesToReplace())) { + groupsToAdd.add(group.toString()); } } - }); + } + }); + Directory.Members membersService = configuration.getDirectory().members(); + // Delete existing Member object + for (String groupKey : groupsToRemove) { + execute(MembersHandler.delete(membersService, groupKey, uid.getUidValue()), + new RequestResultHandler() { + + @Override + public Object handleResult(final Directory.Members.Delete request, final Void value) { + return null; + } - final Directory.Members service = configuration.getDirectory().members(); - // Delete existing Member object - for (String groupKey : groupsToRemove) { - execute(MembersHandler.delete(service, groupKey, uid.getUidValue()), - new RequestResultHandler() { + @Override + public Object handleNotFound(final IOException e) { + // It may be an indirect membership, not able to delete + return null; + } + }); + } + // Add new Member object + for (String groupKey : groupsToAdd) { + execute(MembersHandler.create(membersService, groupKey, uid, null), + new RequestResultHandler() { + + @Override + public Object handleResult(final Directory.Members.Insert request, final Member value) { + return null; + } - @Override - public Object handleResult(final Directory.Members.Delete request, final Void value) { - return null; - } + @Override + public Object handleDuplicate(final IOException e) { + // Do nothing + return null; + } + }); + } - @Override - public Object handleNotFound(final IOException e) { - // It may be an indirect membership, not able to delete - return null; - } - }); - } - // Add new Member object - for (String groupKey : groupsToAdd) { - execute(MembersHandler.create(service, groupKey, uid, null), - new RequestResultHandler() { + Directory.Users.Aliases aliasService = configuration.getDirectory().users().aliases(); + Set aliasesToAdd = new HashSet<>(); + Set aliasesToRemove = new HashSet<>(); + Optional.ofNullable(AttributeDeltaUtil.find(GoogleAppsUtil.ALIASES_ATTR, modifications)). + ifPresent(aliases -> { + if (CollectionUtil.isEmpty(aliases.getValuesToReplace())) { + if (!CollectionUtil.isEmpty(aliases.getValuesToAdd())) { + for (Object alias : CollectionUtil.nullAsEmpty(aliases.getValuesToAdd())) { + aliasesToAdd.add(alias.toString()); + } + } - @Override - public Object handleResult(final Directory.Members.Insert request, final Member value) { - return null; - } + if (!CollectionUtil.isEmpty(aliases.getValuesToRemove())) { + for (Object alias : CollectionUtil.nullAsEmpty(aliases.getValuesToRemove())) { + aliasesToRemove.add(alias.toString()); + } + } + } else { + aliasesToRemove.addAll(UserHandler.listAliases(aliasService, uid.getUidValue())); - @Override - public Object handleDuplicate(final IOException e) { - // Do nothing - return null; + if (!CollectionUtil.isEmpty(aliases.getValuesToAdd())) { + for (Object group : CollectionUtil.nullAsEmpty(aliases.getValuesToReplace())) { + aliasesToAdd.add(group.toString()); + } + } } }); - } - } else if (ObjectClass.GROUP.equals(objectClass)) { - final Directory.Groups.Update update = GroupHandler.update( - configuration.getDirectory().groups(), uid.getUidValue(), modifications); - if (null != update) { - execute(update, new RequestResultHandler() { + // Delete existing aliases + for (String alias : aliasesToRemove) { + execute(UserHandler.deleteUserAlias(aliasService, alias, uid.getUidValue()), + new RequestResultHandler() { + + @Override + public Object handleResult(final Directory.Users.Aliases.Delete request, final Void v) { + return null; + } - @Override - public Uid handleResult(final Directory.Groups.Update request, final Group value) { - LOG.ok("Group is Updated:{0}", value.getEmail()); - return uid; - } - }); - } + @Override + public Object handleNotFound(final IOException e) { + return null; + } + }); + } + // Add new aliases + for (String alias : aliasesToAdd) { + execute(UserHandler.createUserAlias(aliasService, uid.getUidValue(), alias), + new RequestResultHandler() { + + @Override + public Object handleResult(final Directory.Users.Aliases.Insert insert, final Alias value) { + return null; + } - final Directory.Members service = configuration.getDirectory().members(); + @Override + public Object handleDuplicate(final IOException e) { + return null; + } + }); + } + } - Set membersToAdd = new HashSet<>(); - Set membersToRemove = new HashSet<>(); - Optional.ofNullable(AttributeDeltaUtil.find(GoogleAppsUtil.MEMBERS_ATTR, modifications)). - ifPresent(members -> { - if (CollectionUtil.isEmpty(members.getValuesToReplace())) { - if (!CollectionUtil.isEmpty(members.getValuesToAdd())) { - for (Object group : CollectionUtil.nullAsEmpty(members.getValuesToAdd())) { - membersToAdd.add(group.toString()); - } - } + private void updateDeltaGroup(final Set modifications) { + Directory.Groups.Update update = GroupHandler.update( + configuration.getDirectory().groups(), uid.getUidValue(), modifications); + if (null != update) { + execute(update, new RequestResultHandler() { - if (!CollectionUtil.isEmpty(members.getValuesToRemove())) { - for (Object group : CollectionUtil.nullAsEmpty(members.getValuesToRemove())) { - membersToRemove.add(group.toString()); - } - } - } else { - for (Map member : listMembers( - service, - uid.getUidValue(), - null)) { + @Override + public Uid handleResult(final Directory.Groups.Update request, final Group value) { + LOG.ok("Group is Updated:{0}", value.getId()); + return uid; + } + }); + } - membersToRemove.add(member.get(GoogleAppsUtil.EMAIL_ATTR)); - } + Directory.Members membersService = configuration.getDirectory().members(); - if (!CollectionUtil.isEmpty(members.getValuesToAdd())) { - for (Object group : CollectionUtil.nullAsEmpty(members.getValuesToReplace())) { - membersToAdd.add(group.toString()); - } + Set membersToAdd = new HashSet<>(); + Set membersToRemove = new HashSet<>(); + Optional.ofNullable(AttributeDeltaUtil.find(GoogleAppsUtil.MEMBERS_ATTR, modifications)). + ifPresent(members -> { + if (CollectionUtil.isEmpty(members.getValuesToReplace())) { + if (!CollectionUtil.isEmpty(members.getValuesToAdd())) { + for (Object group : CollectionUtil.nullAsEmpty(members.getValuesToAdd())) { + membersToAdd.add(group.toString()); } } - }); - // Remove all membership - for (String member : membersToRemove) { - execute(MembersHandler.delete(service, uid.getUidValue(), member), - new RequestResultHandler() { + if (!CollectionUtil.isEmpty(members.getValuesToRemove())) { + for (Object group : CollectionUtil.nullAsEmpty(members.getValuesToRemove())) { + membersToRemove.add(group.toString()); + } + } + } else { + for (Map member : listMembers( + membersService, + uid.getUidValue(), + null)) { - @Override - public Object handleResult(final Directory.Members.Delete request, final Void value) { - return null; - } + membersToRemove.add(member.get(GoogleAppsUtil.EMAIL_ATTR)); + } - @Override - public Object handleNotFound(final IOException e) { - // Do nothing - return null; + if (!CollectionUtil.isEmpty(members.getValuesToAdd())) { + for (Object group : CollectionUtil.nullAsEmpty(members.getValuesToReplace())) { + membersToAdd.add(group.toString()); + } + } } }); - } - // Add new Member object - for (String member : membersToAdd) { - execute(MembersHandler.create(service, uid.getUidValue(), member, null), - new RequestResultHandler() { + // Remove all membership + for (String member : membersToRemove) { + execute(MembersHandler.delete(membersService, uid.getUidValue(), member), + new RequestResultHandler() { + + @Override + public Object handleResult(final Directory.Members.Delete request, final Void value) { + return null; + } - @Override - public Object handleResult(final Directory.Members.Insert request, final Member value) { - return null; - } + @Override + public Object handleNotFound(final IOException e) { + // Do nothing + return null; + } + }); + } + // Add new Member object + for (String member : membersToAdd) { + execute(MembersHandler.create(membersService, uid.getUidValue(), member, null), + new RequestResultHandler() { + + @Override + public Object handleResult(final Directory.Members.Insert request, final Member value) { + return null; + } - @Override - public Object handleDuplicate(final IOException e) { - // Do nothing - return null; + @Override + public Object handleDuplicate(final IOException e) { + // Do nothing + return null; + } + }); + } + + Directory.Groups.Aliases aliasService = configuration.getDirectory().groups().aliases(); + Set aliasesToAdd = new HashSet<>(); + Set aliasesToRemove = new HashSet<>(); + Optional.ofNullable(AttributeDeltaUtil.find(GoogleAppsUtil.ALIASES_ATTR, modifications)). + ifPresent(aliases -> { + if (CollectionUtil.isEmpty(aliases.getValuesToReplace())) { + if (!CollectionUtil.isEmpty(aliases.getValuesToAdd())) { + for (Object alias : CollectionUtil.nullAsEmpty(aliases.getValuesToAdd())) { + aliasesToAdd.add(alias.toString()); + } + } + + if (!CollectionUtil.isEmpty(aliases.getValuesToRemove())) { + for (Object alias : CollectionUtil.nullAsEmpty(aliases.getValuesToRemove())) { + aliasesToRemove.add(alias.toString()); + } + } + } else { + aliasesToRemove.addAll(GroupHandler.listAliases(aliasService, uid.getUidValue())); + + if (!CollectionUtil.isEmpty(aliases.getValuesToAdd())) { + for (Object group : CollectionUtil.nullAsEmpty(aliases.getValuesToReplace())) { + aliasesToAdd.add(group.toString()); + } + } } }); - } + // Delete existing aliases + for (String alias : aliasesToRemove) { + execute(GroupHandler.deleteGroupAlias(aliasService, alias, uid.getUidValue()), + new RequestResultHandler() { + + @Override + public Object handleResult(final Directory.Groups.Aliases.Delete request, final Void v) { + return null; + } + + @Override + public Object handleNotFound(final IOException e) { + return null; + } + }); + } + // Add new aliases + for (String alias : aliasesToAdd) { + execute(GroupHandler.createGroupAlias(aliasService, uid.getUidValue(), alias), + new RequestResultHandler() { + + @Override + public Object handleResult(final Directory.Groups.Aliases.Insert insert, final Alias value) { + return null; + } + + @Override + public Object handleDuplicate(final IOException e) { + return null; + } + }); + } + } + + public Set updateDelta(final Set modifications) { + if (ObjectClass.ACCOUNT.equals(objectClass)) { + updateDeltaUser(modifications); + } else if (ObjectClass.GROUP.equals(objectClass)) { + updateDeltaGroup(modifications); } else if (GoogleAppsUtil.ORG_UNIT.equals(objectClass)) { - final Directory.Orgunits.Update update = OrgunitsHandler.update( + Directory.Orgunits.Update update = OrgunitsHandler.update( configuration.getDirectory().orgunits(), uid.getUidValue(), modifications); if (null != update) { execute(update, new RequestResultHandler() { @@ -612,8 +890,6 @@ public Uid handleResult(final Directory.Orgunits.Update request, final OrgUnit v }); } } else { - LOG.warn("Update delta of type {0} is not supported", configuration.getConnectorMessages() - .format(objectClass.getDisplayNameKey(), objectClass.getObjectClassValue())); throw new UnsupportedOperationException("Update delta of type" + objectClass.getObjectClassValue() + " is not supported"); } diff --git a/src/main/java/net/tirasa/connid/bundles/googleapps/GroupHandler.java b/src/main/java/net/tirasa/connid/bundles/googleapps/GroupHandler.java index ba3cfa3..9234b4e 100644 --- a/src/main/java/net/tirasa/connid/bundles/googleapps/GroupHandler.java +++ b/src/main/java/net/tirasa/connid/bundles/googleapps/GroupHandler.java @@ -27,16 +27,20 @@ import com.google.api.services.directory.Directory; import com.google.api.services.directory.model.Alias; +import com.google.api.services.directory.model.Aliases; import com.google.api.services.directory.model.Group; import com.google.api.services.directory.model.Groups; import com.google.common.base.CharMatcher; import com.google.common.escape.Escaper; import com.google.common.escape.Escapers; import java.io.IOException; +import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; +import java.util.stream.Collectors; import org.identityconnectors.common.CollectionUtil; import org.identityconnectors.common.StringUtil; import org.identityconnectors.common.logging.Log; @@ -335,6 +339,33 @@ public static Directory.Groups.Insert create( } } + public static Set listAliases(final Directory.Groups.Aliases service, final String groupKey) { + try { + return execute( + service.list(groupKey), + new RequestResultHandler>() { + + @SuppressWarnings("unchecked") + @Override + public Set handleResult(final Directory.Groups.Aliases.List request, final Aliases value) { + return Optional.ofNullable(value.getAliases()).map(aliases -> aliases.stream(). + map(map -> ((Map) map).get(GoogleAppsUtil.ALIAS_ATTR)). + filter(Objects::nonNull).collect(Collectors.toSet())). + orElse(Set.of()); + } + + @Override + public Set handleError(final Throwable e) { + LOG.error(e, "While getting aliases for {0}", groupKey); + return Set.of(); + } + }); + } catch (IOException e) { + LOG.warn(e, "Failed to initialize Aliases#list"); + throw ConnectorException.wrap(e); + } + } + public static Directory.Groups.Aliases.Insert createGroupAlias( final Directory.Groups.Aliases service, final String groupKey, final String alias) { @@ -429,14 +460,14 @@ public static Directory.Groups.Update update( } public static Set listGroups(final Directory.Groups service, final String userKey, final String domain) { - final Set result = CollectionUtil.newCaseInsensitiveSet(); + Set result = CollectionUtil.newCaseInsensitiveSet(); try { - Directory.Groups.List request = service.list(); - request.setUserKey(userKey); - request.setFields("groups/email"); - // 400 Bad Request if the Customer(my_customer or exact value) is set, only domain-userKey combination - // allowed. request.setCustomer(MY_CUSTOMER_ID); - request.setDomain(domain); + Directory.Groups.List request = service.list(). + setUserKey(userKey). + setFields("groups/email"). + // 400 Bad Request if the Customer(my_customer or exact value) is set, only domain-userKey + // combination allowed. request.setCustomer(MY_CUSTOMER_ID); + setDomain(domain); String nextPageToken; do { @@ -445,9 +476,7 @@ public static Set listGroups(final Directory.Groups service, final Strin @Override public String handleResult(final Directory.Groups.List request, final Groups value) { if (null != value.getGroups()) { - for (Group group : value.getGroups()) { - result.add(group.getEmail()); - } + value.getGroups().stream().map(Group::getId).forEach(result::add); } return value.getNextPageToken(); } diff --git a/src/main/java/net/tirasa/connid/bundles/googleapps/UserHandler.java b/src/main/java/net/tirasa/connid/bundles/googleapps/UserHandler.java index 1dbaad7..c5277a3 100644 --- a/src/main/java/net/tirasa/connid/bundles/googleapps/UserHandler.java +++ b/src/main/java/net/tirasa/connid/bundles/googleapps/UserHandler.java @@ -23,9 +23,12 @@ */ package net.tirasa.connid.bundles.googleapps; +import static net.tirasa.connid.bundles.googleapps.GoogleApiExecutor.execute; + import com.google.api.client.util.GenericData; import com.google.api.services.directory.Directory; import com.google.api.services.directory.model.Alias; +import com.google.api.services.directory.model.Aliases; import com.google.api.services.directory.model.User; import com.google.api.services.directory.model.UserAddress; import com.google.api.services.directory.model.UserExternalId; @@ -973,6 +976,33 @@ public static Directory.Users.Photos.Update createUpdateUserPhoto( } } + public static Set listAliases(final Directory.Users.Aliases service, final String userKey) { + try { + return execute( + service.list(userKey), + new RequestResultHandler>() { + + @SuppressWarnings("unchecked") + @Override + public Set handleResult(final Directory.Users.Aliases.List request, final Aliases value) { + return Optional.ofNullable(value.getAliases()).map(aliases -> aliases.stream(). + map(map -> ((Map) map).get(GoogleAppsUtil.ALIAS_ATTR)). + filter(Objects::nonNull).collect(Collectors.toSet())). + orElse(Set.of()); + } + + @Override + public Set handleError(final Throwable e) { + LOG.error(e, "While getting aliases for {0}", userKey); + return Set.of(); + } + }); + } catch (IOException e) { + LOG.warn(e, "Failed to initialize Aliases#list"); + throw ConnectorException.wrap(e); + } + } + public static Directory.Users.Aliases.Insert createUserAlias( final Directory.Users.Aliases service, final String userKey, final String alias) {