From be3d668fdeb38f7d2358a9dd09c38efaf629cf0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francesco=20Chicchiricc=C3=B2?= Date: Wed, 22 May 2024 14:25:27 +0200 Subject: [PATCH] [GOOGLEAPPS-20] #resolve --- pom.xml | 3 +- .../googleapps/GoogleAppsConfiguration.java | 58 +++-- .../googleapps/GoogleAppsConnector.java | 209 ++++++++-------- .../googleapps/GoogleAppsCustomSchema.java | 29 ++- .../bundles/googleapps/GoogleAppsUtil.java | 13 +- .../bundles/googleapps/GroupHandler.java | 200 +++------------ .../googleapps/LicenseAssignmentsHandler.java | 57 +++-- .../bundles/googleapps/MembersHandler.java | 173 +++++++++++++ .../bundles/googleapps/OrgunitsHandler.java | 141 +++++------ .../bundles/googleapps/UserHandler.java | 234 +++++++----------- .../CodeProcessorServlet.java | 29 ++- .../CredentialsGeneratorApplication.java | 33 ++- src/main/resources/application.properties | 30 ++- .../bundles/googleapps/Messages.properties | 2 +- .../GoogleAppsConnectorUnitTests.java | 55 ++-- 15 files changed, 640 insertions(+), 626 deletions(-) create mode 100644 src/main/java/net/tirasa/connid/bundles/googleapps/MembersHandler.java diff --git a/pom.xml b/pom.xml index a4da149..59c29e3 100644 --- a/pom.xml +++ b/pom.xml @@ -90,7 +90,7 @@ 1.23.0 2.17.1 - 2.6.15 + 2.7.18 1.8 @@ -159,7 +159,6 @@ ${connid.version} test - org.junit.jupiter junit-jupiter-engine diff --git a/src/main/java/net/tirasa/connid/bundles/googleapps/GoogleAppsConfiguration.java b/src/main/java/net/tirasa/connid/bundles/googleapps/GoogleAppsConfiguration.java index 622ef70..a2154a5 100644 --- a/src/main/java/net/tirasa/connid/bundles/googleapps/GoogleAppsConfiguration.java +++ b/src/main/java/net/tirasa/connid/bundles/googleapps/GoogleAppsConfiguration.java @@ -43,7 +43,6 @@ import org.identityconnectors.common.security.GuardedString; import org.identityconnectors.common.security.SecurityUtil; import org.identityconnectors.framework.common.exceptions.ConfigurationException; -import org.identityconnectors.framework.common.exceptions.ConnectorException; import org.identityconnectors.framework.spi.AbstractConfiguration; import org.identityconnectors.framework.spi.ConfigurationProperty; import org.identityconnectors.framework.spi.StatefulConfiguration; @@ -66,6 +65,8 @@ public class GoogleAppsConfiguration extends AbstractConfiguration implements St */ private static final JsonFactory JSON_FACTORY = new GsonFactory(); + private static final String APPLICATION_NAME = "ConnId"; + private String domain = null; /** @@ -80,7 +81,7 @@ public class GoogleAppsConfiguration extends AbstractConfiguration implements St private GuardedString refreshToken = null; - private GoogleCredentials credentials = null; + private GoogleCredentials googleCredentials = null; private Directory directory; @@ -222,52 +223,55 @@ public void validate() { } } - public void getGoogleCredential() { + private void initGoogleCredentials() { synchronized (this) { - if (null == credentials) { - final UserCredentials.Builder credentialsBuilder = - UserCredentials.newBuilder() - .setClientId(getClientId()) - .setClientSecret(SecurityUtil.decrypt(getClientSecret())); + if (null == googleCredentials) { + UserCredentials.Builder credentialsBuilder = UserCredentials.newBuilder() + .setClientId(getClientId()) + .setClientSecret(SecurityUtil.decrypt(getClientSecret())); getRefreshToken().access(chars -> credentialsBuilder.setRefreshToken(new String(chars))); - final UserCredentials userCredentials = credentialsBuilder.build(); - credentials = userCredentials.createScoped( - Arrays.asList(DirectoryScopes.ADMIN_DIRECTORY_USER, - DirectoryScopes.ADMIN_DIRECTORY_USER_ALIAS, - DirectoryScopes.ADMIN_DIRECTORY_USERSCHEMA, - DirectoryScopes.ADMIN_DIRECTORY_ORGUNIT, - DirectoryScopes.ADMIN_DIRECTORY_DOMAIN, - DirectoryScopes.ADMIN_DIRECTORY_NOTIFICATIONS, - DirectoryScopes.ADMIN_DIRECTORY_GROUP, - DirectoryScopes.ADMIN_DIRECTORY_GROUP_MEMBER)); - - HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(credentials); + UserCredentials userCredentials = credentialsBuilder.build(); + + googleCredentials = userCredentials.createScoped(Arrays.asList( + DirectoryScopes.ADMIN_DIRECTORY_USER, + DirectoryScopes.ADMIN_DIRECTORY_USER_ALIAS, + DirectoryScopes.ADMIN_DIRECTORY_USERSCHEMA, + DirectoryScopes.ADMIN_DIRECTORY_ORGUNIT, + DirectoryScopes.ADMIN_DIRECTORY_DOMAIN, + DirectoryScopes.ADMIN_DIRECTORY_NOTIFICATIONS, + DirectoryScopes.ADMIN_DIRECTORY_GROUP, + DirectoryScopes.ADMIN_DIRECTORY_GROUP_MEMBER)); + + HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(googleCredentials); directory = new Directory.Builder(HTTP_TRANSPORT, JSON_FACTORY, requestInitializer). - setApplicationName("ConnId"). + setApplicationName(APPLICATION_NAME). build(); licensing = new Licensing.Builder(HTTP_TRANSPORT, JSON_FACTORY, requestInitializer). - setApplicationName("ConnId"). + setApplicationName(APPLICATION_NAME). build(); } } } + public void test() throws IOException { + initGoogleCredentials(); + googleCredentials.refreshIfExpired(); + } + @Override public void release() { + googleCredentials = null; } public Directory getDirectory() { - getGoogleCredential(); + initGoogleCredentials(); return directory; } public Licensing getLicensing() { - getGoogleCredential(); - if (null == licensing) { - throw new ConnectorException("Licensing is not enabled"); - } + initGoogleCredentials(); return licensing; } } diff --git a/src/main/java/net/tirasa/connid/bundles/googleapps/GoogleAppsConnector.java b/src/main/java/net/tirasa/connid/bundles/googleapps/GoogleAppsConnector.java index 5bbefff..90cc835 100644 --- a/src/main/java/net/tirasa/connid/bundles/googleapps/GoogleAppsConnector.java +++ b/src/main/java/net/tirasa/connid/bundles/googleapps/GoogleAppsConnector.java @@ -88,8 +88,8 @@ import org.identityconnectors.framework.common.objects.filter.FilterTranslator; import org.identityconnectors.framework.common.objects.filter.StartsWithFilter; import org.identityconnectors.framework.spi.Configuration; -import org.identityconnectors.framework.spi.Connector; import org.identityconnectors.framework.spi.ConnectorClass; +import org.identityconnectors.framework.spi.PoolableConnector; import org.identityconnectors.framework.spi.SearchResultsHandler; import org.identityconnectors.framework.spi.operations.CreateOp; import org.identityconnectors.framework.spi.operations.DeleteOp; @@ -102,8 +102,10 @@ * Main implementation of the GoogleApps Connector. */ @ConnectorClass(displayNameKey = "GoogleApps.connector.display", configurationClass = GoogleAppsConfiguration.class) -public class GoogleAppsConnector implements Connector, CreateOp, DeleteOp, SchemaOp, - SearchOp, TestOp, UpdateOp { +public class GoogleAppsConnector + implements PoolableConnector, + TestOp, SchemaOp, SearchOp, + CreateOp, UpdateOp, DeleteOp { /** * Setup logging for the {@link GoogleAppsConnector}. @@ -171,13 +173,12 @@ public Uid create( final AttributesAccessor accessor = new AttributesAccessor(createAttributes); if (ObjectClass.ACCOUNT.equals(objectClass)) { - Uid uid = execute(UserHandler.createUser(configuration.getDirectory().users(), accessor, configuration. - getCustomSchemasJSON()), + Uid uid = execute(UserHandler.createUser( + configuration.getDirectory().users(), accessor, configuration.getCustomSchemasJSON()), new RequestResultHandler() { @Override - public Uid handleResult(final Directory.Users.Insert request, - final User value) { + public Uid handleResult(final Directory.Users.Insert request, final User value) { LOG.ok("New User is created: {0}", value.getId()); return new Uid(value.getId(), value.getEtag()); } @@ -230,13 +231,11 @@ public String handleResult(final Directory.Users.Photos.Update request, final Us if (null == id) { // TODO make warn about failed update } - } else if (null != photoObject) { // Delete group and Error or - RetryableException e = RetryableException.wrap("Invalid attribute value: " - + String.valueOf(photoObject), uid); - e.initCause(new InvalidAttributeValueException( - "Attribute 'photo' must be a single Map value")); + RetryableException e = RetryableException.wrap( + "Invalid attribute value: " + String.valueOf(photoObject), uid); + e.initCause(new InvalidAttributeValueException("Attribute 'photo' must be a single byte[] value")); throw e; } } @@ -279,7 +278,7 @@ public Void handleResult(final Directory.Users.MakeAdmin request, final Void val * } */ // @formatter:on - Uid uid = execute(GroupHandler.createGroup(configuration.getDirectory().groups(), accessor), + Uid uid = execute(GroupHandler.create(configuration.getDirectory().groups(), accessor), new RequestResultHandler() { @Override @@ -297,7 +296,7 @@ public Uid handleResult(final Directory.Groups.Insert request, String email = (String) ((Map) member).get(GoogleAppsUtil.EMAIL_ATTR); String role = (String) ((Map) member).get(GoogleAppsUtil.ROLE_ATTR); - String id = execute(GroupHandler.createMember(membersService, uid.getUidValue(), email, role), + String id = execute(MembersHandler.create(membersService, uid.getUidValue(), email, role), new RequestResultHandler() { @Override @@ -322,24 +321,24 @@ public String handleResult(final Directory.Members.Insert request, final Member return uid; } else if (GoogleAppsUtil.MEMBER.equals(objectClass)) { - return execute(GroupHandler.createMember(configuration.getDirectory().members(), accessor), + return execute(MembersHandler.create(configuration.getDirectory().members(), accessor), new RequestResultHandler() { @Override public Uid handleResult(final Directory.Members.Insert request, final Member value) { LOG.ok("New Member is created:{0}/{1}", request.getGroupKey(), value.getEmail()); - return GroupHandler.generateMemberId(request.getGroupKey(), value); + return MembersHandler.generateUid(request.getGroupKey(), value); } }); } else if (GoogleAppsUtil.ORG_UNIT.equals(objectClass)) { - return execute(OrgunitsHandler.createOrgunit(configuration.getDirectory().orgunits(), accessor), + return execute(OrgunitsHandler.create(configuration.getDirectory().orgunits(), accessor), new RequestResultHandler() { @Override public Uid handleResult(final Directory.Orgunits.Insert request, final OrgUnit value) { LOG.ok("New OrgUnit is created:{0}", value.getName()); - return OrgunitsHandler.generateOrgUnitId(value); + return OrgunitsHandler.generateUid(value); } }); } else if (GoogleAppsUtil.LICENSE_ASSIGNMENT.equals(objectClass)) { @@ -358,7 +357,7 @@ public Uid handleResult(final Directory.Orgunits.Insert request, final OrgUnit v // @formatter:on return execute( - LicenseAssignmentsHandler.createLicenseAssignment( + LicenseAssignmentsHandler.create( configuration.getLicensing().licenseAssignments(), accessor), new RequestResultHandler() { @@ -369,7 +368,7 @@ public Uid handleResult( LOG.ok("LicenseAssignment is Created:{0}/{1}/{2}", value.getProductId(), value.getSkuId(), value.getUserId()); - return LicenseAssignmentsHandler.generateLicenseAssignmentId(value); + return LicenseAssignmentsHandler.generateUid(value); } }); } else { @@ -414,7 +413,7 @@ public void delete(final ObjectClass objectClass, final Uid uid, final Operation request = configuration.getDirectory().orgunits(). delete(GoogleAppsUtil.MY_CUSTOMER_ID, CollectionUtil.newList(uid.getUidValue())); } else if (GoogleAppsUtil.LICENSE_ASSIGNMENT.equals(objectClass)) { - request = LicenseAssignmentsHandler.deleteLicenseAssignment( + request = LicenseAssignmentsHandler.delete( configuration.getLicensing().licenseAssignments(), uid.getUidValue()); } } catch (IOException e) { @@ -448,19 +447,19 @@ public Schema schema() { if (null == schema) { final SchemaBuilder builder = new SchemaBuilder(GoogleAppsConnector.class); - ObjectClassInfo user = UserHandler.getUserClassInfo(configuration.getCustomSchemasJSON()); + ObjectClassInfo user = UserHandler.getObjectClassInfo(configuration.getCustomSchemasJSON()); builder.defineObjectClass(user); - ObjectClassInfo group = GroupHandler.getGroupClassInfo(); + ObjectClassInfo group = GroupHandler.getObjectClassInfo(); builder.defineObjectClass(group); - ObjectClassInfo member = GroupHandler.getMemberClassInfo(); + ObjectClassInfo member = MembersHandler.getObjectClassInfo(); builder.defineObjectClass(member); - ObjectClassInfo orgUnit = OrgunitsHandler.getOrgunitClassInfo(); + ObjectClassInfo orgUnit = OrgunitsHandler.getObjectClassInfo(); builder.defineObjectClass(orgUnit); - ObjectClassInfo licenseAssignment = LicenseAssignmentsHandler.getLicenseAssignmentClassInfo(); + ObjectClassInfo licenseAssignment = LicenseAssignmentsHandler.getObjectClassInfo(); builder.defineObjectClass(licenseAssignment); builder.defineOperationOption(OperationOptionInfoBuilder.buildAttributesToGet(), @@ -757,7 +756,7 @@ public String handleResult( final Members value) { if (null != value.getMembers()) { for (Member group : value.getMembers()) { - handler.handle(GroupHandler.fromMember(request.getGroupKey(), group)); + handler.handle(MembersHandler.from(request.getGroupKey(), group)); } } return value.getNextPageToken(); @@ -790,7 +789,7 @@ public String handleResult( @Override public Boolean handleResult(final Directory.Members.Get request, final Member value) { - return handler.handle(GroupHandler.fromMember(request.getGroupKey(), value)); + return handler.handle(MembersHandler.from(request.getGroupKey(), value)); } @Override @@ -848,7 +847,7 @@ public Void handleResult(final Directory.Orgunits.List request, final OrgUnits value) { if (null != value.getOrganizationUnits()) { for (OrgUnit group : value.getOrganizationUnits()) { - handler.handle(OrgunitsHandler.fromOrgunit(group, attributesToGet)); + handler.handle(OrgunitsHandler.from(group, attributesToGet)); } } return null; @@ -871,7 +870,7 @@ public Void handleResult(final Directory.Orgunits.List request, @Override public Boolean handleResult(final Directory.Orgunits.Get request, final OrgUnit value) { - return handler.handle(OrgunitsHandler.fromOrgunit(value, attributesToGet)); + return handler.handle(OrgunitsHandler.from(value, attributesToGet)); } @Override @@ -937,7 +936,7 @@ public String handleResult( if (null != value.getItems()) { for (LicenseAssignment resource : value.getItems()) { - handler.handle(LicenseAssignmentsHandler.fromLicenseAssignment(resource)); + handler.handle(LicenseAssignmentsHandler.from(resource)); } } return value.getNextPageToken(); @@ -982,7 +981,7 @@ public Boolean handleResult( final Licensing.LicenseAssignments.Get request, final LicenseAssignment value) { - return handler.handle(LicenseAssignmentsHandler.fromLicenseAssignment(value)); + return handler.handle(LicenseAssignmentsHandler.from(value)); } @Override @@ -1194,7 +1193,16 @@ protected String getFields(final OperationOptions options, final String... nameA @Override public void test() { - LOG.ok("Test works well"); + try { + configuration.test(); + } catch (Exception e) { + throw new ConnectorException(e); + } + } + + @Override + public void checkAlive() { + test(); } @Override @@ -1204,13 +1212,15 @@ public Uid update( final Set replaceAttributes, final OperationOptions options) { - final AttributesAccessor attributesAccessor = new AttributesAccessor(replaceAttributes); + final AttributesAccessor accessor = new AttributesAccessor(replaceAttributes); Uid uidAfterUpdate = uid; if (ObjectClass.ACCOUNT.equals(objectClass)) { - final Directory.Users.Patch patch = - UserHandler.updateUser(configuration.getDirectory().users(), uid.getUidValue(), attributesAccessor, - configuration.getCustomSchemasJSON()); + Directory.Users.Patch patch = UserHandler.updateUser( + configuration.getDirectory().users(), + uid.getUidValue(), + accessor, + configuration.getCustomSchemasJSON()); if (null != patch) { uidAfterUpdate = execute(patch, new RequestResultHandler() { @@ -1221,15 +1231,17 @@ public Uid handleResult(final Directory.Users.Patch request, final User value) { } }); } - Attribute groups = attributesAccessor.find(PredefinedAttributes.GROUPS_NAME); + 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())) { + for (String groupKey : listGroups( + configuration.getDirectory().groups(), + uidAfterUpdate.getUidValue(), + configuration.getDomain())) { - execute(GroupHandler.deleteMembers(service, groupKey, uidAfterUpdate.getUidValue()), + execute(MembersHandler.delete(service, groupKey, uidAfterUpdate.getUidValue()), new RequestResultHandler() { @Override @@ -1246,8 +1258,10 @@ public Object handleNotFound(final IOException e) { }); } } else { - final Set activeGroups = - listGroups(configuration.getDirectory().groups(), uidAfterUpdate.getUidValue()); + final Set activeGroups = listGroups( + configuration.getDirectory().groups(), + uidAfterUpdate.getUidValue(), + configuration.getDomain()); final List addGroups = new ArrayList<>(); final Set keepGroups = CollectionUtil.newCaseInsensitiveSet(); @@ -1257,13 +1271,12 @@ public Object handleNotFound(final IOException e) { if (activeGroups.contains((String) member)) { keepGroups.add((String) member); } else { - String email = attributesAccessor.getName().getNameValue(); - addGroups.add(GroupHandler.createMember(service, (String) member, email, null)); + 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"); + throw new InvalidAttributeValueException("Attribute '__GROUPS__' must be a String list"); } } @@ -1287,7 +1300,7 @@ public Object handleDuplicate(final IOException e) { // Delete existing Member object if (activeGroups.removeAll(keepGroups)) { for (String groupKey : activeGroups) { - execute(GroupHandler.deleteMembers(service, groupKey, uidAfterUpdate.getUidValue()), + execute(MembersHandler.delete(service, groupKey, uidAfterUpdate.getUidValue()), new RequestResultHandler() { @Override @@ -1297,8 +1310,7 @@ public Object handleResult(final Directory.Members.Delete request, final Void va @Override public Object handleNotFound(final IOException e) { - // It may be an indirect membership, - // not able to delete + // It may be an indirect membership, not able to delete return null; } }); @@ -1310,9 +1322,9 @@ public Object handleNotFound(final IOException e) { // 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() - && attributesAccessor.hasAttribute(OperationalAttributes.ENABLE_NAME) - && !attributesAccessor.findBoolean(OperationalAttributes.ENABLE_NAME) - && StringUtil.isNotBlank(attributesAccessor.findString(GoogleAppsUtil.PRIMARY_EMAIL_ATTR))) { + && 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 { @@ -1321,7 +1333,7 @@ public Object handleNotFound(final IOException e) { configuration.getLicensing().licenseAssignments().get( configuration.getProductId(), skuId, - attributesAccessor.findString(GoogleAppsUtil.PRIMARY_EMAIL_ATTR)); + accessor.findString(GoogleAppsUtil.PRIMARY_EMAIL_ATTR)); execute(request, new RequestResultHandler< Licensing.LicenseAssignments.Get, LicenseAssignment, Boolean>() { @@ -1353,13 +1365,15 @@ public Boolean handleNotFound(final IOException e) { }); } catch (IOException e) { LOG.error(e, "Unable to find license for {0}-{1}-{2}", configuration.getProductId(), skuId, - attributesAccessor.findString(GoogleAppsUtil.PRIMARY_EMAIL_ATTR)); + accessor.findString(GoogleAppsUtil.PRIMARY_EMAIL_ATTR)); } } } } else if (ObjectClass.GROUP.equals(objectClass)) { - final Directory.Groups.Patch patch = GroupHandler.updateGroup( - configuration.getDirectory().groups(), uid.getUidValue(), attributesAccessor); + final Directory.Groups.Patch patch = GroupHandler.update( + configuration.getDirectory().groups(), + uid.getUidValue(), + accessor); if (null != patch) { uidAfterUpdate = execute(patch, new RequestResultHandler() { @@ -1370,13 +1384,13 @@ public Uid handleResult(final Directory.Groups.Patch request, final Group value) } }); } - Attribute members = attributesAccessor.find(GoogleAppsUtil.MEMBERS_ATTR); + 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)) { - execute(GroupHandler.deleteMembers( + execute(MembersHandler.delete( service, uidAfterUpdate.getUidValue(), member.get(GoogleAppsUtil.EMAIL_ATTR)), new RequestResultHandler() { @@ -1417,7 +1431,7 @@ public Object handleNotFound(final IOException e) { if (email.equalsIgnoreCase(a.get(GoogleAppsUtil.EMAIL_ATTR))) { a.put("keep", null); if (!role.equalsIgnoreCase(a.get(GoogleAppsUtil.ROLE_ATTR))) { - patchMembership.add(GroupHandler.updateMembers( + patchMembership.add(MembersHandler.update( service, uidAfterUpdate.getUidValue(), email, role)); } notMember = false; @@ -1425,7 +1439,7 @@ public Object handleNotFound(final IOException e) { } } if (notMember) { - addMembership.add(GroupHandler.createMember( + addMembership.add(MembersHandler.create( service, uidAfterUpdate.getUidValue(), email, role)); } } else if (null != member) { @@ -1466,7 +1480,7 @@ public Object handleResult(final Directory.Members.Patch request, final Member v // Delete existing Member object for (Map am : activeMembership) { if (!am.containsKey("keep")) { - execute(GroupHandler.deleteMembers( + execute(MembersHandler.delete( service, uidAfterUpdate.getUidValue(), am.get(GoogleAppsUtil.EMAIL_ATTR)), new RequestResultHandler() { @@ -1486,11 +1500,11 @@ public Object handleNotFound(final IOException e) { } } } else if (GoogleAppsUtil.MEMBER.equals(objectClass)) { - String role = attributesAccessor.findString(GoogleAppsUtil.ROLE_ATTR); + 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 = GroupHandler.updateMembers( + final Directory.Members.Patch patch = MembersHandler.update( configuration.getDirectory().members(), ids[0], ids[1], role). setFields(GoogleAppsUtil.EMAIL_ETAG); uidAfterUpdate = execute(patch, new RequestResultHandler() { @@ -1498,7 +1512,7 @@ public Object handleNotFound(final IOException e) { @Override public Uid handleResult(final Directory.Members.Patch request, final Member value) { LOG.ok("Member is updated:{0}/{1}", request.getGroupKey(), value.getEmail()); - return GroupHandler.generateMemberId(request.getGroupKey(), value); + return MembersHandler.generateUid(request.getGroupKey(), value); } }); } else { @@ -1506,22 +1520,23 @@ public Uid handleResult(final Directory.Members.Patch request, final Member valu } } } else if (GoogleAppsUtil.ORG_UNIT.equals(objectClass)) { - final Directory.Orgunits.Patch patch = OrgunitsHandler.updateOrgunit( - configuration.getDirectory().orgunits(), uid.getUidValue(), attributesAccessor); + final Directory.Orgunits.Patch patch = OrgunitsHandler.update( + configuration.getDirectory().orgunits(), uid.getUidValue(), accessor); if (null != patch) { uidAfterUpdate = execute(patch, new RequestResultHandler() { @Override public Uid handleResult(final Directory.Orgunits.Patch request, final OrgUnit value) { LOG.ok("OrgUnit is updated:{0}", value.getName()); - return OrgunitsHandler.generateOrgUnitId(value); + return OrgunitsHandler.generateUid(value); } }); } } else if (GoogleAppsUtil.LICENSE_ASSIGNMENT.equals(objectClass)) { - final Licensing.LicenseAssignments.Patch patch = - LicenseAssignmentsHandler.updateLicenseAssignment( - configuration.getLicensing().licenseAssignments(), uid.getUidValue(), attributesAccessor); + final Licensing.LicenseAssignments.Patch patch = LicenseAssignmentsHandler.update( + configuration.getLicensing().licenseAssignments(), + uid.getUidValue(), + accessor); if (null != patch) { uidAfterUpdate = execute(patch, new RequestResultHandler() { @@ -1533,7 +1548,7 @@ public Uid handleResult( LOG.ok("LicenseAssignment is Updated:{0}/{1}/{2}", value.getProductId(), value.getSkuId(), value.getUserId()); - return LicenseAssignmentsHandler.generateLicenseAssignmentId(value); + return LicenseAssignmentsHandler.generateUid(value); } }); } @@ -1546,6 +1561,16 @@ public Uid handleResult( return uidAfterUpdate; } + private static Object getValueFromKey( + final String customSchema, + final Map> customSchemas) { + + String[] names = customSchema.split("\\."); + return names.length > 1 + ? customSchemas.get(names[0]) != null ? customSchemas.get(names[0]).get(names[1]) : null + : null; + } + protected ConnectorObject fromUser( final User user, final Set attributesToGet, @@ -1674,15 +1699,12 @@ protected ConnectorObject fromUser( } if (null == attributesToGet || ("full".equals(configuration.getProjection()) && StringUtil.isNotBlank(configuration.getCustomSchemasJSON()))) { - List customSchemas = GoogleAppsUtil.extractCustomSchemas(configuration. - getCustomSchemasJSON()); - for (GoogleAppsCustomSchema customSchema : customSchemas) { + + GoogleAppsUtil.extractCustomSchemas(configuration.getCustomSchemasJSON()).forEach(customSchema -> { if (customSchema.getType().equals("object")) { - // parse inner schemas - String basicName = customSchema.getName(); // manage only first level inner schemas for (GoogleAppsCustomSchema innerSchema : customSchema.getInnerSchemas()) { - final String innerSchemaName = basicName + "." + innerSchema.getName(); + String innerSchemaName = customSchema.getName() + "." + innerSchema.getName(); builder.addAttribute(AttributeBuilder.build( innerSchemaName, null != user.getCustomSchemas() @@ -1692,18 +1714,18 @@ protected ConnectorObject fromUser( } else { LOG.warn("CustomSchema type {0} not allowed at this level", customSchema.getType()); } - } + }); } // Expensive to get if (null != attributesToGet && attributesToGet.contains(PredefinedAttributes.GROUPS_NAME)) { - builder.addAttribute( - AttributeBuilder.build(PredefinedAttributes.GROUPS_NAME, listGroups(service, user.getId()))); + builder.addAttribute(AttributeBuilder.build(PredefinedAttributes.GROUPS_NAME, + listGroups(service, user.getId(), configuration.getDomain()))); } return builder.build(); } - protected ConnectorObject fromGroup( + protected static ConnectorObject fromGroup( final Group group, final Set attributesToGet, final Directory.Members service) { @@ -1753,7 +1775,7 @@ protected ConnectorObject fromGroup( return builder.build(); } - protected List> listMembers( + protected static List> listMembers( final Directory.Members service, final String groupKey, final String roles) { final List> result = new ArrayList<>(); @@ -1786,7 +1808,7 @@ public String handleResult(final Directory.Members.List request, final Members v return result; } - protected Set listGroups(final Directory.Groups service, final String userKey) { + protected static Set listGroups(final Directory.Groups service, final String userKey, final String domain) { final Set result = CollectionUtil.newCaseInsensitiveSet(); try { Directory.Groups.List request = service.list(); @@ -1794,7 +1816,7 @@ protected Set listGroups(final Directory.Groups service, final String us 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(configuration.getDomain()); + request.setDomain(domain); String nextPageToken; do { @@ -1818,7 +1840,7 @@ public String handleResult(final Directory.Groups.List request, final Groups val return result; } - protected , T, R> R execute( + protected static , T, R> R execute( final G request, final RequestResultHandler handler) { return execute( @@ -1826,7 +1848,7 @@ protected , T, R> R execute( Assertions.nullChecked(handler, "handler"), -1); } - protected , T, R> R execute( + protected static , T, R> R execute( final G request, final RequestResultHandler handler, final int retry) { try { @@ -1898,10 +1920,6 @@ protected , T, R> R execute( } } - protected RuntimeException get(final GoogleJsonError.ErrorInfo errorInfo) { - return null; - } - private static long nextLong(final long n) { long bits, val; do { @@ -1911,14 +1929,7 @@ private static long nextLong(final long n) { return val; } - private Object getValueFromKey(final String customSchema, final Map> customSchemas) { - String[] names = customSchema.split("\\."); - return names.length > 1 - ? customSchemas.get(names[0]) != null ? customSchemas.get(names[0]).get(names[1]) : null - : null; - } - - private List customSchemaNames(final String customSchemasJSON) { + private static List customSchemaNames(final String customSchemasJSON) { List customSchemas = GoogleAppsUtil.extractCustomSchemas(customSchemasJSON); List customSchemaNames = new ArrayList<>(); for (GoogleAppsCustomSchema customSchema : customSchemas) { diff --git a/src/main/java/net/tirasa/connid/bundles/googleapps/GoogleAppsCustomSchema.java b/src/main/java/net/tirasa/connid/bundles/googleapps/GoogleAppsCustomSchema.java index 3b29f6b..378346d 100644 --- a/src/main/java/net/tirasa/connid/bundles/googleapps/GoogleAppsCustomSchema.java +++ b/src/main/java/net/tirasa/connid/bundles/googleapps/GoogleAppsCustomSchema.java @@ -1,17 +1,24 @@ -/** - * Copyright © 2018 ConnId (connid-dev@googlegroups.com) +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Copyright (c) 2016 ConnId All Rights Reserved * - * http://www.apache.org/licenses/LICENSE-2.0 + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== */ package net.tirasa.connid.bundles.googleapps; diff --git a/src/main/java/net/tirasa/connid/bundles/googleapps/GoogleAppsUtil.java b/src/main/java/net/tirasa/connid/bundles/googleapps/GoogleAppsUtil.java index 7743f43..29bb839 100644 --- a/src/main/java/net/tirasa/connid/bundles/googleapps/GoogleAppsUtil.java +++ b/src/main/java/net/tirasa/connid/bundles/googleapps/GoogleAppsUtil.java @@ -30,6 +30,7 @@ import com.google.api.client.util.GenericData; import java.io.IOException; import java.util.List; +import java.util.Optional; import org.identityconnectors.common.StringUtil; import org.identityconnectors.common.logging.Log; import org.identityconnectors.framework.common.exceptions.InvalidAttributeValueException; @@ -204,28 +205,28 @@ public static String getName(final Name name) { throw new InvalidAttributeValueException("Required attribute __NAME__ is missing"); } - public static String getStringValueWithDefault(final Attribute source, final String defaultTo) { + public static Optional getStringValue(final Attribute source) { Object value = AttributeUtil.getSingleValue(source); if (value instanceof String) { - return (String) value; + return Optional.of((String) value); } else if (null != value) { throw new InvalidAttributeValueException("The " + source.getName() + " attribute is not String value attribute. It has value with type " + value.getClass().getSimpleName()); } - return defaultTo; + return Optional.empty(); } - public static Boolean getBooleanValueWithDefault(final Attribute source, final Boolean defaultTo) { + public static Optional getBooleanValue(final Attribute source) { Object value = AttributeUtil.getSingleValue(source); if (value instanceof Boolean) { - return (Boolean) value; + return Optional.of((Boolean) value); } else if (null != value) { throw new InvalidAttributeValueException("The " + source.getName() + " attribute is not Boolean value attribute. It has value with type " + value.getClass().getSimpleName()); } - return defaultTo; + return Optional.empty(); } public static String generateLicenseId(final String productId, final String skuId, final String userId) { 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 c0342d2..b99c728 100644 --- a/src/main/java/net/tirasa/connid/bundles/googleapps/GroupHandler.java +++ b/src/main/java/net/tirasa/connid/bundles/googleapps/GroupHandler.java @@ -25,27 +25,25 @@ import com.google.api.services.admin.directory.Directory; import com.google.api.services.admin.directory.model.Group; -import com.google.api.services.admin.directory.model.Member; 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.Optional; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; import org.identityconnectors.common.StringUtil; import org.identityconnectors.common.logging.Log; import org.identityconnectors.framework.common.exceptions.ConnectorException; import org.identityconnectors.framework.common.exceptions.InvalidAttributeValueException; import org.identityconnectors.framework.common.objects.Attribute; -import org.identityconnectors.framework.common.objects.AttributeBuilder; import org.identityconnectors.framework.common.objects.AttributeInfoBuilder; import org.identityconnectors.framework.common.objects.AttributeUtil; import org.identityconnectors.framework.common.objects.AttributesAccessor; -import org.identityconnectors.framework.common.objects.ConnectorObject; -import org.identityconnectors.framework.common.objects.ConnectorObjectBuilder; import org.identityconnectors.framework.common.objects.Name; import org.identityconnectors.framework.common.objects.ObjectClass; import org.identityconnectors.framework.common.objects.ObjectClassInfo; import org.identityconnectors.framework.common.objects.ObjectClassInfoBuilder; -import org.identityconnectors.framework.common.objects.Uid; import org.identityconnectors.framework.common.objects.filter.AndFilter; import org.identityconnectors.framework.common.objects.filter.ContainsAllValuesFilter; import org.identityconnectors.framework.common.objects.filter.ContainsFilter; @@ -261,7 +259,7 @@ public StringBuilder visitEndsWithFilter(final Directory.Groups.List list, final // GROUP // // ///////////// - public static ObjectClassInfo getGroupClassInfo() { + public static ObjectClassInfo getObjectClassInfo() { // @formatter:off /* GROUP from https://devsite.googleplex.com/admin-sdk/directory/v1/reference/groups#resource * { @@ -306,34 +304,9 @@ public static ObjectClassInfo getGroupClassInfo() { return builder.build(); } - public static ObjectClassInfo getMemberClassInfo() { - // @formatter:off - /* - * { - * } - */ - // @formatter:on - ObjectClassInfoBuilder builder = new ObjectClassInfoBuilder(); - builder.setType(GoogleAppsUtil.MEMBER.getObjectClassValue()); - builder.addAttributeInfo(AttributeInfoBuilder.define(Name.NAME).setUpdateable(false). - setCreateable(false)/* .setRequired(true) */.build()); - - // optional - builder.addAttributeInfo(AttributeInfoBuilder.define(GoogleAppsUtil.GROUP_KEY_ATTR).setUpdateable(false). - setRequired(true).build()); - builder.addAttributeInfo(AttributeInfoBuilder.define(GoogleAppsUtil.EMAIL_ATTR).setUpdateable(false). - setRequired(true).build()); - - builder.addAttributeInfo(AttributeInfoBuilder.build(GoogleAppsUtil.ROLE_ATTR)); - builder.addAttributeInfo(AttributeInfoBuilder.define(GoogleAppsUtil.TYPE_ATTR).setUpdateable(false). - setCreateable(false).build()); - - return builder.build(); - } - // https://support.google.com/a/answer/33386 - public static Directory.Groups.Insert createGroup( - final Directory.Groups groups, final AttributesAccessor attributes) { + public static Directory.Groups.Insert create( + final Directory.Groups service, final AttributesAccessor attributes) { Group group = new Group(); group.setEmail(GoogleAppsUtil.getName(attributes.getName())); @@ -342,7 +315,7 @@ public static Directory.Groups.Insert createGroup( group.setName(attributes.findString(GoogleAppsUtil.NAME_ATTR)); try { - return groups.insert(group).setFields(GoogleAppsUtil.ID_EMAIL_ETAG); + return service.insert(group).setFields(GoogleAppsUtil.ID_EMAIL_ETAG); // } catch (HttpResponseException e){ } catch (IOException e) { LOG.warn(e, "Failed to initialize Groups#Insert"); @@ -350,154 +323,41 @@ public static Directory.Groups.Insert createGroup( } } - public static Directory.Groups.Patch updateGroup( - final Directory.Groups groups, final String groupKey, final AttributesAccessor attributes) { - - Group group = null; - - Name email = attributes.getName(); - if (email != null) { - String stringValue = GoogleAppsUtil.getStringValueWithDefault(email, null); - if (null != stringValue) { - if (StringUtil.isBlank(stringValue)) { - throw new InvalidAttributeValueException( - "Invalid attribute '__NAME__'. The group's email address. " - + "Can not be blank when updating a group."); - } - group = new Group(); - group.setEmail(stringValue); - } - } - - Attribute description = attributes.find(GoogleAppsUtil.DESCRIPTION_ATTR); - if (null != description) { - String stringValue = GoogleAppsUtil.getStringValueWithDefault(description, null); - if (null != stringValue) { - if (null == group) { - group = new Group(); - } - group.setDescription(stringValue); - } - } - Attribute name = attributes.find(GoogleAppsUtil.NAME_ATTR); - if (null != name) { - String stringValue = GoogleAppsUtil.getStringValueWithDefault(name, null); - if (null != stringValue) { - if (null == group) { - group = new Group(); - } - group.setName(stringValue); - } - } - - if (null == group) { - return null; - } - try { - return groups.patch(groupKey, group).setFields(GoogleAppsUtil.ID_EMAIL_ETAG); - // } catch (HttpResponseException e){ - } catch (IOException e) { - LOG.warn(e, "Failed to initialize Groups#Patch"); - throw ConnectorException.wrap(e); + private static void set(final AtomicReference content, final Consumer consumer) { + if (content.get() == null) { + content.set(new Group()); } + consumer.accept(content.get()); } - public static Directory.Members.Insert createMember( - final Directory.Members service, final AttributesAccessor attributes) { + public static Directory.Groups.Patch update( + final Directory.Groups service, + final String groupKey, + final AttributesAccessor attributes) { - String groupKey = attributes.findString(GoogleAppsUtil.GROUP_KEY_ATTR); - if (StringUtil.isBlank(groupKey)) { - throw new InvalidAttributeValueException( - "Missing required attribute 'groupKey'. " - + "Identifies the group in the API request. Required when creating a Member."); - } + AtomicReference content = new AtomicReference<>(); - String memberKey = attributes.findString(GoogleAppsUtil.EMAIL_ATTR); - if (StringUtil.isBlank(memberKey)) { - throw new InvalidAttributeValueException( - "Missing required attribute 'memberKey'. " - + "Identifies the group member in the API request. Required when creating a Member."); - } - String role = attributes.findString(GoogleAppsUtil.ROLE_ATTR); + Optional.ofNullable(attributes.getName()) + .filter(email -> !StringUtil.isBlank(email.getNameValue())) + .ifPresent(email -> set(content, g -> g.setEmail(email.getNameValue()))); - return createMember(service, groupKey, memberKey, role); - } - - public static Directory.Members.Insert createMember( - final Directory.Members service, final String groupKey, final String memberKey, final String role) { + Optional.ofNullable(attributes.find(GoogleAppsUtil.NAME_ATTR)) + .flatMap(name -> GoogleAppsUtil.getStringValue(name)) + .ifPresent(stringValue -> set(content, g -> g.setName(stringValue))); - Member content = new Member(); - content.setEmail(memberKey); - if (StringUtil.isBlank(role)) { - content.setRole("MEMBER"); - } else { - // OWNER. MANAGER. MEMBER. - content.setRole(role); - } - try { - return service.insert(groupKey, content).setFields(GoogleAppsUtil.EMAIL_ETAG); - } catch (IOException e) { - LOG.warn(e, "Failed to initialize Members#Insert"); - throw ConnectorException.wrap(e); - } - } + Optional.ofNullable(attributes.find(GoogleAppsUtil.DESCRIPTION_ATTR)) + .flatMap(description -> GoogleAppsUtil.getStringValue(description)) + .ifPresent(stringValue -> set(content, g -> g.setDescription(stringValue))); - public static Directory.Members.Patch updateMembers( - final Directory.Members service, final String groupKey, final String memberKey, final String role) { - - Member content = new Member(); - content.setEmail(memberKey); - - if (StringUtil.isBlank(role)) { - content.setRole("MEMBER"); - } else { - // OWNER. MANAGER. MEMBER. - content.setRole(role); + if (null == content.get()) { + return null; } try { - return service.patch(groupKey, memberKey, content).setFields(GoogleAppsUtil.EMAIL_ETAG); - } catch (IOException e) { - LOG.warn(e, "Failed to initialize Members#Update"); - throw ConnectorException.wrap(e); - } - } - - public static Directory.Members.Delete deleteMembers( - final Directory.Members service, final String groupKey, final String memberKey) { - - try { - return service.delete(groupKey, memberKey); + return service.patch(groupKey, content.get()).setFields(GoogleAppsUtil.ID_EMAIL_ETAG); + // } catch (HttpResponseException e){ } catch (IOException e) { - LOG.warn(e, "Failed to initialize Members#Delete"); + LOG.warn(e, "Failed to initialize Groups#Patch"); throw ConnectorException.wrap(e); } } - - public static ConnectorObject fromMember(final String groupKey, final Member content) { - ConnectorObjectBuilder builder = new ConnectorObjectBuilder(); - builder.setObjectClass(GoogleAppsUtil.MEMBER); - - Uid uid = generateMemberId(groupKey, content); - builder.setUid(uid); - builder.setName(uid.getUidValue()); - - builder.addAttribute(AttributeBuilder.build(GoogleAppsUtil.GROUP_KEY_ATTR, content.getId())); - builder.addAttribute(AttributeBuilder.build(GoogleAppsUtil.EMAIL_ATTR, content.getEmail())); - builder.addAttribute(AttributeBuilder.build(GoogleAppsUtil.ROLE_ATTR, content.getRole())); - builder.addAttribute(AttributeBuilder.build(GoogleAppsUtil.TYPE_ATTR, content.getType())); - - return builder.build(); - } - - public static Uid generateMemberId(final String groupKey, final Member content) { - String memberName = groupKey + '/' + content.getEmail(); - - Uid uid; - if (null != content.getEtag()) { - uid = new Uid(memberName, content.getEtag()); - } else { - uid = new Uid(memberName); - } - return uid; - } } diff --git a/src/main/java/net/tirasa/connid/bundles/googleapps/LicenseAssignmentsHandler.java b/src/main/java/net/tirasa/connid/bundles/googleapps/LicenseAssignmentsHandler.java index cd47439..162ae37 100644 --- a/src/main/java/net/tirasa/connid/bundles/googleapps/LicenseAssignmentsHandler.java +++ b/src/main/java/net/tirasa/connid/bundles/googleapps/LicenseAssignmentsHandler.java @@ -27,6 +27,9 @@ import com.google.api.services.licensing.model.LicenseAssignment; import com.google.api.services.licensing.model.LicenseAssignmentInsert; import java.io.IOException; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.identityconnectors.common.StringUtil; @@ -34,10 +37,8 @@ import org.identityconnectors.framework.common.exceptions.ConnectorException; import org.identityconnectors.framework.common.exceptions.InvalidAttributeValueException; import org.identityconnectors.framework.common.exceptions.UnknownUidException; -import org.identityconnectors.framework.common.objects.Attribute; import org.identityconnectors.framework.common.objects.AttributeBuilder; import org.identityconnectors.framework.common.objects.AttributeInfoBuilder; -import org.identityconnectors.framework.common.objects.AttributeUtil; import org.identityconnectors.framework.common.objects.AttributesAccessor; import org.identityconnectors.framework.common.objects.ConnectorObject; import org.identityconnectors.framework.common.objects.ConnectorObjectBuilder; @@ -69,7 +70,7 @@ public final class LicenseAssignmentsHandler { // https://developers.google.com/admin-sdk/licensing/v1/reference/licenseAssignments // // ///////////// - public static ObjectClassInfo getLicenseAssignmentClassInfo() { + public static ObjectClassInfo getObjectClassInfo() { // @formatter:off /* * { @@ -101,7 +102,7 @@ public static ObjectClassInfo getLicenseAssignmentClassInfo() { return builder.build(); } - public static Licensing.LicenseAssignments.Insert createLicenseAssignment( + public static Licensing.LicenseAssignments.Insert create( final Licensing.LicenseAssignments service, final AttributesAccessor attributes) { String productId = attributes.findString(GoogleAppsUtil.PRODUCT_ID_ATTR); @@ -125,10 +126,10 @@ public static Licensing.LicenseAssignments.Insert createLicenseAssignment( + "The user's current primary email address. Required when creating a LicenseAssignment."); } - return createLicenseAssignment(service, productId, skuId, userId); + return create(service, productId, skuId, userId); } - public static Licensing.LicenseAssignments.Insert createLicenseAssignment( + public static Licensing.LicenseAssignments.Insert create( final Licensing.LicenseAssignments service, final String productId, final String skuId, @@ -144,12 +145,22 @@ public static Licensing.LicenseAssignments.Insert createLicenseAssignment( } } - public static Licensing.LicenseAssignments.Patch updateLicenseAssignment( + private static void set( + final AtomicReference content, + final Consumer consumer) { + + if (content.get() == null) { + content.set(new LicenseAssignment()); + } + consumer.accept(content.get()); + } + + public static Licensing.LicenseAssignments.Patch update( final Licensing.LicenseAssignments service, final String groupKey, final AttributesAccessor attributes) { - LicenseAssignment content = null; + AtomicReference content = new AtomicReference<>(); Matcher name = LICENSE_NAME_PATTERN.matcher(groupKey); if (!name.matches()) { @@ -160,30 +171,22 @@ public static Licensing.LicenseAssignments.Patch updateLicenseAssignment( String oldSkuId = name.group(1); String userId = name.group(2); - Attribute skuId = attributes.find(GoogleAppsUtil.SKU_ID_ATTR); - if (null != skuId) { - content = new LicenseAssignment(); - content.setSkuId(AttributeUtil.getStringValue(skuId)); - } + Optional.ofNullable(attributes.find(GoogleAppsUtil.SKU_ID_ATTR)) + .flatMap(GoogleAppsUtil::getStringValue) + .ifPresent(stringValue -> set(content, l -> l.setSkuId(stringValue))); - if (null == content) { + if (null == content.get() || oldSkuId.equalsIgnoreCase(content.get().getSkuId())) { return null; } try { - if (oldSkuId.equalsIgnoreCase(content.getSkuId())) { - // There is nothing to change - return null; - } else { - return service.patch(productId, oldSkuId, userId, content); - } - // } catch (HttpResponseException e){ + return service.patch(productId, oldSkuId, userId, content.get()); } catch (IOException e) { LOG.warn(e, "Failed to initialize LicenseAssignments#Patch"); throw ConnectorException.wrap(e); } } - public static Licensing.LicenseAssignments.Delete deleteLicenseAssignment( + public static Licensing.LicenseAssignments.Delete delete( final Licensing.LicenseAssignments service, final String groupKey) { Matcher name = LICENSE_NAME_PATTERN.matcher(groupKey); @@ -204,10 +207,10 @@ public static Licensing.LicenseAssignments.Delete deleteLicenseAssignment( } } - public static ConnectorObject fromLicenseAssignment(final LicenseAssignment content) { + public static ConnectorObject from(final LicenseAssignment content) { ConnectorObjectBuilder builder = new ConnectorObjectBuilder(); builder.setObjectClass(GoogleAppsUtil.LICENSE_ASSIGNMENT); - Uid uid = generateLicenseAssignmentId(content); + Uid uid = generateUid(content); builder.setUid(uid); builder.setName(uid.getUidValue()); @@ -219,14 +222,14 @@ public static ConnectorObject fromLicenseAssignment(final LicenseAssignment cont return builder.build(); } - public static Uid generateLicenseAssignmentId(final LicenseAssignment content) { + public static Uid generateUid(final LicenseAssignment content) { String id = content.getProductId() + "/sku/" + content.getSkuId() + "/user/" + content.getUserId(); if (null != content.getEtags()) { return new Uid(id, content.getEtags()); - } else { - return new Uid(id); } + + return new Uid(id); } private LicenseAssignmentsHandler() { diff --git a/src/main/java/net/tirasa/connid/bundles/googleapps/MembersHandler.java b/src/main/java/net/tirasa/connid/bundles/googleapps/MembersHandler.java new file mode 100644 index 0000000..8292292 --- /dev/null +++ b/src/main/java/net/tirasa/connid/bundles/googleapps/MembersHandler.java @@ -0,0 +1,173 @@ +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2024 ConnId. All Rights Reserved + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== + */ +package net.tirasa.connid.bundles.googleapps; + +import com.google.api.services.admin.directory.Directory; +import com.google.api.services.admin.directory.model.Member; +import java.io.IOException; +import org.identityconnectors.common.StringUtil; +import org.identityconnectors.common.logging.Log; +import org.identityconnectors.framework.common.exceptions.ConnectorException; +import org.identityconnectors.framework.common.exceptions.InvalidAttributeValueException; +import org.identityconnectors.framework.common.objects.AttributeBuilder; +import org.identityconnectors.framework.common.objects.AttributeInfoBuilder; +import org.identityconnectors.framework.common.objects.AttributesAccessor; +import org.identityconnectors.framework.common.objects.ConnectorObject; +import org.identityconnectors.framework.common.objects.ConnectorObjectBuilder; +import org.identityconnectors.framework.common.objects.Name; +import org.identityconnectors.framework.common.objects.ObjectClassInfo; +import org.identityconnectors.framework.common.objects.ObjectClassInfoBuilder; +import org.identityconnectors.framework.common.objects.Uid; + +public final class MembersHandler { + + private static final Log LOG = Log.getLog(MembersHandler.class); + + public static ObjectClassInfo getObjectClassInfo() { + // @formatter:off + /* + * { + * } + */ + // @formatter:on + ObjectClassInfoBuilder builder = new ObjectClassInfoBuilder(); + builder.setType(GoogleAppsUtil.MEMBER.getObjectClassValue()); + builder.addAttributeInfo(AttributeInfoBuilder.define(Name.NAME).setUpdateable(false). + setCreateable(false)/* .setRequired(true) */.build()); + + // optional + builder.addAttributeInfo(AttributeInfoBuilder.define(GoogleAppsUtil.GROUP_KEY_ATTR).setUpdateable(false). + setRequired(true).build()); + builder.addAttributeInfo(AttributeInfoBuilder.define(GoogleAppsUtil.EMAIL_ATTR).setUpdateable(false). + setRequired(true).build()); + + builder.addAttributeInfo(AttributeInfoBuilder.build(GoogleAppsUtil.ROLE_ATTR)); + builder.addAttributeInfo(AttributeInfoBuilder.define(GoogleAppsUtil.TYPE_ATTR).setUpdateable(false). + setCreateable(false).build()); + + return builder.build(); + } + + public static Directory.Members.Insert create( + final Directory.Members service, final AttributesAccessor attributes) { + + String groupKey = attributes.findString(GoogleAppsUtil.GROUP_KEY_ATTR); + if (StringUtil.isBlank(groupKey)) { + throw new InvalidAttributeValueException( + "Missing required attribute 'groupKey'. " + + "Identifies the group in the API request. Required when creating a Member."); + } + + String memberKey = attributes.findString(GoogleAppsUtil.EMAIL_ATTR); + if (StringUtil.isBlank(memberKey)) { + throw new InvalidAttributeValueException( + "Missing required attribute 'memberKey'. " + + "Identifies the group member in the API request. Required when creating a Member."); + } + String role = attributes.findString(GoogleAppsUtil.ROLE_ATTR); + + return create(service, groupKey, memberKey, role); + } + + public static Directory.Members.Insert create( + final Directory.Members service, final String groupKey, final String memberKey, final String role) { + + Member content = new Member(); + content.setEmail(memberKey); + if (StringUtil.isBlank(role)) { + content.setRole("MEMBER"); + } else { + // OWNER. MANAGER. MEMBER. + content.setRole(role); + } + try { + return service.insert(groupKey, content).setFields(GoogleAppsUtil.EMAIL_ETAG); + } catch (IOException e) { + LOG.warn(e, "Failed to initialize Members#Insert"); + throw ConnectorException.wrap(e); + } + } + + public static Directory.Members.Patch update( + final Directory.Members service, final String groupKey, final String memberKey, final String role) { + + Member content = new Member(); + content.setEmail(memberKey); + + if (StringUtil.isBlank(role)) { + content.setRole("MEMBER"); + } else { + // OWNER. MANAGER. MEMBER. + content.setRole(role); + } + try { + return service.patch(groupKey, memberKey, content).setFields(GoogleAppsUtil.EMAIL_ETAG); + } catch (IOException e) { + LOG.warn(e, "Failed to initialize Members#Update"); + throw ConnectorException.wrap(e); + } + } + + public static Directory.Members.Delete delete( + final Directory.Members service, final String groupKey, final String memberKey) { + + try { + return service.delete(groupKey, memberKey); + } catch (IOException e) { + LOG.warn(e, "Failed to initialize Members#Delete"); + throw ConnectorException.wrap(e); + } + } + + public static ConnectorObject from(final String groupKey, final Member content) { + ConnectorObjectBuilder builder = new ConnectorObjectBuilder(); + builder.setObjectClass(GoogleAppsUtil.MEMBER); + + Uid uid = generateUid(groupKey, content); + builder.setUid(uid); + builder.setName(uid.getUidValue()); + + builder.addAttribute(AttributeBuilder.build(GoogleAppsUtil.GROUP_KEY_ATTR, content.getId())); + builder.addAttribute(AttributeBuilder.build(GoogleAppsUtil.EMAIL_ATTR, content.getEmail())); + builder.addAttribute(AttributeBuilder.build(GoogleAppsUtil.ROLE_ATTR, content.getRole())); + builder.addAttribute(AttributeBuilder.build(GoogleAppsUtil.TYPE_ATTR, content.getType())); + + return builder.build(); + } + + public static Uid generateUid(final String groupKey, final Member content) { + String memberName = groupKey + '/' + content.getEmail(); + + Uid uid; + if (null != content.getEtag()) { + uid = new Uid(memberName, content.getEtag()); + } else { + uid = new Uid(memberName); + } + return uid; + } + + private MembersHandler() { + // private constructor for static utility class + } +} diff --git a/src/main/java/net/tirasa/connid/bundles/googleapps/OrgunitsHandler.java b/src/main/java/net/tirasa/connid/bundles/googleapps/OrgunitsHandler.java index cad9b8c..8f4e39d 100644 --- a/src/main/java/net/tirasa/connid/bundles/googleapps/OrgunitsHandler.java +++ b/src/main/java/net/tirasa/connid/bundles/googleapps/OrgunitsHandler.java @@ -26,16 +26,17 @@ import com.google.api.services.admin.directory.Directory; import com.google.api.services.admin.directory.model.OrgUnit; import java.io.IOException; +import java.util.Optional; import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; import org.identityconnectors.common.CollectionUtil; import org.identityconnectors.common.StringUtil; import org.identityconnectors.common.logging.Log; import org.identityconnectors.framework.common.exceptions.ConnectorException; import org.identityconnectors.framework.common.exceptions.InvalidAttributeValueException; -import org.identityconnectors.framework.common.objects.Attribute; import org.identityconnectors.framework.common.objects.AttributeBuilder; import org.identityconnectors.framework.common.objects.AttributeInfoBuilder; -import org.identityconnectors.framework.common.objects.AttributeUtil; import org.identityconnectors.framework.common.objects.AttributesAccessor; import org.identityconnectors.framework.common.objects.ConnectorObject; import org.identityconnectors.framework.common.objects.ConnectorObjectBuilder; @@ -63,7 +64,7 @@ public final class OrgunitsHandler { // https://developers.google.com/admin-sdk/directory/v1/reference/orgunits // // ///////////// - public static ObjectClassInfo getOrgunitClassInfo() { + public static ObjectClassInfo getObjectClassInfo() { // @formatter:off /* * { @@ -94,12 +95,42 @@ public static ObjectClassInfo getOrgunitClassInfo() { return builder.build(); } - public static Directory.Orgunits.Insert createOrgunit( + private static Optional getOrgUnitNameFromPath(final Name name) { + if (null == name) { + return Optional.empty(); + } + String fullName = name.getNameValue(); + String[] elements = fullName.split("/"); + if (elements.length == 0) { + return Optional.of(fullName); + } + return Optional.of(elements[elements.length - 1]); + } + + private static String getParentOrgUnitPath(final AttributesAccessor attributes) { + // parentOrgUnitPath The organization unit's parent path. For example, + // /corp/sales is the parent path for /corp/sales/sales_support organization unit. + String parentOrgUnitPath = attributes.findString(GoogleAppsUtil.PARENT_ORG_UNIT_PATH_ATTR); + if (StringUtil.isNotBlank(parentOrgUnitPath)) { + if (parentOrgUnitPath.charAt(0) != '/') { + parentOrgUnitPath = "/" + parentOrgUnitPath; + } + } + + if (StringUtil.isBlank(parentOrgUnitPath)) { + throw new InvalidAttributeValueException( + "Missing required attribute 'parentOrgUnitPath'. " + + "The organization unit's parent path. Required when creating an orgunit."); + } + return parentOrgUnitPath; + } + + public static Directory.Orgunits.Insert create( final Directory.Orgunits service, final AttributesAccessor attributes) { OrgUnit resource = new OrgUnit(); resource.setParentOrgUnitPath(getParentOrgUnitPath(attributes)); - resource.setName(getOrgUnitNameFromPath(attributes.getName())); + getOrgUnitNameFromPath(attributes.getName()).ifPresent(resource::setName); // Optional resource.setBlockInheritance(attributes.findBoolean(GoogleAppsUtil.BLOCK_INHERITANCE_ATTR)); @@ -115,71 +146,52 @@ public static Directory.Orgunits.Insert createOrgunit( } } - public static Directory.Orgunits.Patch updateOrgunit( + private static void set(final AtomicReference content, final Consumer consumer) { + if (content.get() == null) { + content.set(new OrgUnit()); + } + consumer.accept(content.get()); + } + + public static Directory.Orgunits.Patch update( final Directory.Orgunits service, final String orgUnitPath, final AttributesAccessor attributes) { - OrgUnit resource = null; + AtomicReference content = new AtomicReference<>(); - String orgUnitName = getOrgUnitNameFromPath(attributes.getName()); - if (null != orgUnitName) { - resource = new OrgUnit(); - // Rename the object - resource.setName(orgUnitName); - } + getOrgUnitNameFromPath(attributes.getName()).ifPresent(name -> set(content, o -> o.setName(name))); - String parentOrgUnitPath = getParentOrgUnitPath(attributes); - if (null != parentOrgUnitPath) { - if (null == resource) { - resource = new OrgUnit(); - } - resource.setParentOrgUnitPath(parentOrgUnitPath); - } + Optional.ofNullable(attributes.findString(GoogleAppsUtil.PARENT_ORG_UNIT_PATH_ATTR)) + .ifPresent(ignore -> set(content, o -> o.setParentOrgUnitPath(getParentOrgUnitPath(attributes)))); - Attribute description = attributes.find(GoogleAppsUtil.DESCRIPTION_ATTR); - if (null != description) { - if (null == resource) { - resource = new OrgUnit(); - } - String stringValue = AttributeUtil.getStringValue(description); - if (null == stringValue) { - stringValue = GoogleAppsUtil.EMPTY_STRING; - } - resource.setDescription(stringValue); - } + Optional.ofNullable(attributes.find(GoogleAppsUtil.DESCRIPTION_ATTR)) + .flatMap(GoogleAppsUtil::getStringValue) + .ifPresent(stringValue -> set(content, o -> o.setDescription(stringValue))); - Attribute blockInheritance = attributes.find(GoogleAppsUtil.BLOCK_INHERITANCE_ATTR); - if (null != blockInheritance) { - if (null == resource) { - resource = new OrgUnit(); - } - Boolean booleanValue = AttributeUtil.getBooleanValue(blockInheritance); - if (null == booleanValue) { - // The default value is false - booleanValue = Boolean.FALSE; - } - resource.setBlockInheritance(booleanValue); - } + Optional.ofNullable(attributes.findBoolean(GoogleAppsUtil.BLOCK_INHERITANCE_ATTR)) + .ifPresent(blockInheritance -> set(content, u -> u.setBlockInheritance(!blockInheritance))); - if (null == resource) { + if (null == content.get()) { return null; } try { // Full path of the organization unit - return service.patch(GoogleAppsUtil.MY_CUSTOMER_ID, CollectionUtil.newList(orgUnitPath), resource) - .setFields(GoogleAppsUtil.ORG_UNIT_PATH_ETAG); + return service.patch( + GoogleAppsUtil.MY_CUSTOMER_ID, + CollectionUtil.newList(orgUnitPath), + content.get()).setFields(GoogleAppsUtil.ORG_UNIT_PATH_ETAG); } catch (IOException e) { LOG.warn(e, "Failed to initialize Orgunits#Patch"); throw ConnectorException.wrap(e); } } - public static ConnectorObject fromOrgunit(final OrgUnit content, final Set attributesToGet) { + public static ConnectorObject from(final OrgUnit content, final Set attributesToGet) { ConnectorObjectBuilder builder = new ConnectorObjectBuilder(); builder.setObjectClass(GoogleAppsUtil.ORG_UNIT); - builder.setUid(generateOrgUnitId(content)); + builder.setUid(generateUid(content)); builder.setName(content.getName()); // Optional @@ -202,38 +214,7 @@ public static ConnectorObject fromOrgunit(final OrgUnit content, final Set List buildObjs(final List valu }).filter(Objects::nonNull).collect(Collectors.toList()); } - private static void addOrReplaceCustomSchemas( - final String customSchemasJSON, final AttributesAccessor attributes, final User user) { + private static Object getValueByType( + final GoogleAppsCustomSchema innerSchema, + final AttributesAccessor attributes, + final String innerSchemaName) { + + return innerSchema.getMultiValued() + ? attributes.findStringList(innerSchemaName) + : attributes.findString(innerSchemaName); + } + + private static Map> buildCustomAttrs( + final String customSchemas, final AttributesAccessor attributes) { - List schemas = GoogleAppsUtil.extractCustomSchemas(customSchemasJSON); + List schemas = GoogleAppsUtil.extractCustomSchemas(customSchemas); Map> attrsToAdd = new HashMap<>(); for (GoogleAppsCustomSchema customSchema : schemas) { if (customSchema.getType().equals("object")) { @@ -700,8 +712,9 @@ private static void addOrReplaceCustomSchemas( for (GoogleAppsCustomSchema innerSchema : customSchema.getInnerSchemas()) { final String innerSchemaName = basicName + "." + innerSchema.getName(); if (attrsToAdd.containsKey(basicName)) { - attrsToAdd.get(basicName).put(innerSchema.getName(), getValueByType(innerSchema, attributes, - innerSchemaName)); + attrsToAdd.get(basicName).put( + innerSchema.getName(), + getValueByType(innerSchema, attributes, innerSchemaName)); } else { Map value = new HashMap<>(); value.put(innerSchema.getName(), getValueByType(innerSchema, attributes, innerSchemaName)); @@ -711,183 +724,106 @@ private static void addOrReplaceCustomSchemas( } else { LOG.warn("CustomSchema type {0} not allowed at this level", customSchema.getType()); } - user.setCustomSchemas(attrsToAdd); } + return attrsToAdd; } - private static Object getValueByType( - final GoogleAppsCustomSchema innerSchema, - final AttributesAccessor attributes, - final String innerSchemaName) { - - return innerSchema.getMultiValued() - ? attributes.findStringList(innerSchemaName) - : attributes.findString(innerSchemaName); + private static void set(final AtomicReference content, final Consumer consumer) { + if (content.get() == null) { + content.set(new User()); + } + consumer.accept(content.get()); } public static Directory.Users.Patch updateUser( - final Directory.Users users, + final Directory.Users service, final String userKey, final AttributesAccessor attributes, - final String customSchemasJSON) { + final String customSchemas) { - User content = null; + AtomicReference content = new AtomicReference<>(); - Name email = attributes.getName(); - if (email != null && email.getNameValue() != null) { - content = new User(); - content.setPrimaryEmail(email.getNameValue()); - } + Optional.ofNullable(attributes.getName()) + .filter(email -> email.getNameValue() != null) + .ifPresent(email -> set(content, u -> u.setPrimaryEmail(email.getNameValue()))); - Attribute givenName = attributes.find(GoogleAppsUtil.GIVEN_NAME_ATTR); - if (null != givenName) { - String stringValue = GoogleAppsUtil.getStringValueWithDefault(givenName, null); - if (null != stringValue) { - if (null == content) { - content = new User(); - } - content.setName(new UserName()); - content.getName().setGivenName(stringValue); - } - } + Optional.ofNullable(attributes.findBoolean(OperationalAttributes.ENABLE_NAME)) + .ifPresent(enable -> set(content, u -> u.setSuspended(!enable))); - Attribute familyName = attributes.find(GoogleAppsUtil.FAMILY_NAME_ATTR); - if (null != familyName) { - String stringValue = GoogleAppsUtil.getStringValueWithDefault(familyName, null); - if (null != stringValue) { - if (null == content) { - content = new User(); - } - if (null == content.getName()) { - content.setName(new UserName()); - } - content.getName().setFamilyName(stringValue); - } - } + Optional.ofNullable(attributes.getPassword()) + .ifPresent(password -> set(content, u -> u.setPassword(SecurityUtil.decrypt(password)))); - GuardedString password = attributes.getPassword(); - if (null != password) { - if (null == content) { - content = new User(); - } - content.setPassword(SecurityUtil.decrypt(password)); - } + Optional.ofNullable(attributes.find(GoogleAppsUtil.GIVEN_NAME_ATTR)) + .flatMap(GoogleAppsUtil::getStringValue) + .ifPresent(stringValue -> set(content, u -> { - Boolean enable = attributes.findBoolean(OperationalAttributes.ENABLE_NAME); - if (null != enable) { - if (null == content) { - content = new User(); - } + u.setName(new UserName()); + u.getName().setGivenName(stringValue); + })); - content.setSuspended(!enable); - } + Optional.ofNullable(attributes.find(GoogleAppsUtil.FAMILY_NAME_ATTR)) + .flatMap(GoogleAppsUtil::getStringValue) + .ifPresent(stringValue -> set(content, u -> { - Attribute changePasswordAtNextLogin = attributes.find(GoogleAppsUtil.CHANGE_PASSWORD_AT_NEXT_LOGIN_ATTR); - if (null != changePasswordAtNextLogin) { - Boolean booleanValue = - GoogleAppsUtil.getBooleanValueWithDefault(changePasswordAtNextLogin, null); - if (null != booleanValue) { - if (null == content) { - content = new User(); - } - content.setChangePasswordAtNextLogin(booleanValue); - } - } + Optional.ofNullable(u.getName()).orElseGet(() -> { + u.setName(new UserName()); + return u.getName(); + }).setFamilyName(stringValue); + })); - Attribute ipWhitelisted = attributes.find(GoogleAppsUtil.IP_WHITELISTED_ATTR); - if (null != ipWhitelisted) { - Boolean booleanValue = GoogleAppsUtil.getBooleanValueWithDefault(ipWhitelisted, null); - if (null != booleanValue) { - if (null == content) { - content = new User(); - } - content.setIpWhitelisted(booleanValue); - } - } + Optional.ofNullable(attributes.find(GoogleAppsUtil.CHANGE_PASSWORD_AT_NEXT_LOGIN_ATTR)) + .flatMap(changePasswordAtNextLogin -> GoogleAppsUtil.getBooleanValue(changePasswordAtNextLogin)) + .ifPresent(booleanValue -> set(content, u -> u.setChangePasswordAtNextLogin(booleanValue))); - Attribute emails = attributes.find(GoogleAppsUtil.EMAILS_ATTR); - if (null != emails) { - if (null == content) { - content = new User(); - } - content.setEmails(emails.getValue()); - } - // Complex attributes - fillAttr(content, user -> user.setIms(buildObjs(Optional.ofNullable(attributes.findList( - GoogleAppsUtil.IMS_ATTR)).orElse(null), UserIm.class))); - fillAttr(content, user -> user.setExternalIds(buildObjs(Optional.ofNullable(attributes.findList( - GoogleAppsUtil.EXTERNAL_IDS_ATTR)).orElse(null), UserExternalId.class))); - fillAttr(content, user -> user.setRelations(buildObjs(Optional.ofNullable(attributes.findList( - GoogleAppsUtil.RELATIONS_ATTR)).orElse(null), UserRelation.class))); - fillAttr(content, user -> user.setAddresses(buildObjs(Optional.ofNullable(attributes.findList( - GoogleAppsUtil.ADDRESSES_ATTR)).orElse(null), UserAddress.class))); - fillAttr(content, user -> user.setOrganizations(buildObjs(Optional.ofNullable(attributes.findList( - GoogleAppsUtil.ORGANIZATIONS_ATTR)).orElse(null), UserOrganization.class))); - fillAttr(content, user -> user.setPhones(buildObjs(Optional.ofNullable(attributes.findList( - GoogleAppsUtil.PHONES_ATTR)).orElse(null), UserPhone.class))); - - Attribute orgUnitPath = attributes.find(GoogleAppsUtil.ORG_UNIT_PATH_ATTR); - if (null != orgUnitPath) { - String stringValue = GoogleAppsUtil.getStringValueWithDefault(orgUnitPath, null); - if (null != stringValue) { - if (null == content) { - content = new User(); - } - content.setOrgUnitPath(stringValue); - } - } + Optional.ofNullable(attributes.find(GoogleAppsUtil.IP_WHITELISTED_ATTR)) + .flatMap(ipWhitelisted -> GoogleAppsUtil.getBooleanValue(ipWhitelisted)) + .ifPresent(booleanValue -> set(content, u -> u.setIpWhitelisted(booleanValue))); - Attribute includeInGlobalAddressList = attributes.find(GoogleAppsUtil.INCLUDE_IN_GLOBAL_ADDRESS_LIST_ATTR); - if (null != includeInGlobalAddressList) { - Boolean booleanValue = - GoogleAppsUtil.getBooleanValueWithDefault(includeInGlobalAddressList, null); - if (null != booleanValue) { - if (null == content) { - content = new User(); - } - content.setIncludeInGlobalAddressList(booleanValue); - } - } + Optional.ofNullable(attributes.find(GoogleAppsUtil.ORG_UNIT_PATH_ATTR)) + .flatMap(GoogleAppsUtil::getStringValue) + .ifPresent(stringValue -> set(content, u -> u.setOrgUnitPath(stringValue))); - // customSchemas - if (StringUtil.isNotBlank(customSchemasJSON)) { - addOrReplaceCustomSchemas(customSchemasJSON, attributes, content); + Optional.ofNullable(attributes.find(GoogleAppsUtil.INCLUDE_IN_GLOBAL_ADDRESS_LIST_ATTR)) + .flatMap(includeInGlobalAddressList -> GoogleAppsUtil.getBooleanValue(includeInGlobalAddressList)) + .ifPresent(booleanValue -> set(content, u -> u.setIncludeInGlobalAddressList(booleanValue))); + + // Complex attributes + Optional.ofNullable(attributes.find(GoogleAppsUtil.EMAILS_ATTR)) + .ifPresent(emails -> set(content, u -> u.setEmails(buildObjs(emails.getValue(), UserEmail.class)))); + Optional.ofNullable(attributes.findList(GoogleAppsUtil.IMS_ATTR)) + .ifPresent(value -> set(content, u -> u.setIms(buildObjs(value, UserIm.class)))); + Optional.ofNullable(attributes.findList(GoogleAppsUtil.EXTERNAL_IDS_ATTR)) + .ifPresent(value -> set(content, u -> u.setExternalIds(buildObjs(value, UserExternalId.class)))); + Optional.ofNullable(attributes.findList(GoogleAppsUtil.RELATIONS_ATTR)) + .ifPresent(value -> set(content, u -> u.setRelations(buildObjs(value, UserRelation.class)))); + Optional.ofNullable(attributes.findList(GoogleAppsUtil.ADDRESSES_ATTR)) + .ifPresent(value -> set(content, u -> u.setAddresses(buildObjs(value, UserAddress.class)))); + Optional.ofNullable(attributes.findList(GoogleAppsUtil.ORGANIZATIONS_ATTR)) + .ifPresent(value -> set(content, u -> u.setOrganizations(buildObjs(value, UserOrganization.class)))); + Optional.ofNullable(attributes.findList(GoogleAppsUtil.PHONES_ATTR)) + .ifPresent(value -> set(content, u -> u.setPhones(buildObjs(value, UserPhone.class)))); + + if (StringUtil.isNotBlank(customSchemas)) { + set(content, u -> u.setCustomSchemas(buildCustomAttrs(customSchemas, attributes))); } - if (null == content) { + if (null == content.get()) { return null; } try { - return users.patch(userKey, content).setFields(GoogleAppsUtil.ID_ETAG); - // } catch (HttpResponseException e){ + return service.patch(userKey, content.get()).setFields(GoogleAppsUtil.ID_ETAG); } catch (IOException e) { LOG.warn(e, "Failed to initialize Users#Patch"); throw ConnectorException.wrap(e); } } - private static void fillAttr(final User content, final Consumer r) { - r.accept(Optional.ofNullable(content).orElseGet(() -> new User())); - } - public static Directory.Users.Photos.Update createUpdateUserPhoto( final Directory.Users.Photos service, final String userKey, final byte[] data) { UserPhoto content = new UserPhoto(); - // Required content.setPhotoData(Base64.getMimeEncoder().encodeToString(data)); - // @formatter:off - /* - * content.setPhotoData(com.google.api.client.util.Base64 - * .encodeBase64URLSafeString((byte[]) data.get("photoData"))); - * content.setHeight((Integer) data.get("height")); - * content.setWidth((Integer) data.get("width")); - * - * // Allowed values are JPEG, PNG, GIF, BMP, TIFF, - * content.setMimeType((String) data.get("mimeType")); - */ - // @formatter:on try { return service.update(userKey, content).setFields(GoogleAppsUtil.ID_ATTR); } catch (IOException e) { diff --git a/src/main/java/net/tirasa/connid/bundles/googleapps/credentialsgenerator/CodeProcessorServlet.java b/src/main/java/net/tirasa/connid/bundles/googleapps/credentialsgenerator/CodeProcessorServlet.java index 075e277..ac8ae45 100644 --- a/src/main/java/net/tirasa/connid/bundles/googleapps/credentialsgenerator/CodeProcessorServlet.java +++ b/src/main/java/net/tirasa/connid/bundles/googleapps/credentialsgenerator/CodeProcessorServlet.java @@ -1,17 +1,24 @@ -/** - * Copyright © 2018 ConnId (connid-dev@googlegroups.com) +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Copyright (c) 2016 ConnId All Rights Reserved * - * http://www.apache.org/licenses/LICENSE-2.0 + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== */ package net.tirasa.connid.bundles.googleapps.credentialsgenerator; diff --git a/src/main/java/net/tirasa/connid/bundles/googleapps/credentialsgenerator/CredentialsGeneratorApplication.java b/src/main/java/net/tirasa/connid/bundles/googleapps/credentialsgenerator/CredentialsGeneratorApplication.java index 6350cff..3137d38 100644 --- a/src/main/java/net/tirasa/connid/bundles/googleapps/credentialsgenerator/CredentialsGeneratorApplication.java +++ b/src/main/java/net/tirasa/connid/bundles/googleapps/credentialsgenerator/CredentialsGeneratorApplication.java @@ -1,15 +1,24 @@ -/** - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2016 ConnId All Rights Reserved + * + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. + * + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== */ package net.tirasa.connid.bundles.googleapps.credentialsgenerator; diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index bb37d11..cd76290 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,18 +1,24 @@ +# +# ==================== +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. +# +# Copyright 2016 ConnId. All rights reserved. # -# Copyright © 2022 ConnId (connid-dev@googlegroups.com) +# The contents of this file are subject to the terms of the Common Development +# and Distribution License("CDDL") (the "License"). You may not use this file +# except in compliance with the License. # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# You can obtain a copy of the License at +# http://opensource.org/licenses/cddl1.php +# See the License for the specific language governing permissions and limitations +# under the License. # -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# When distributing the Covered Code, include this CDDL Header Notice in each file +# and include the License file at http://opensource.org/licenses/cddl1.php. +# If applicable, add the following below this CDDL Header, with the fields +# enclosed by brackets [] replaced by your own identifying information: +# "Portions Copyrighted [year] [name of copyright owner]" +# ==================== # redirect.uri=http://localhost:8080/code-processor - diff --git a/src/main/resources/net/tirasa/connid/bundles/googleapps/Messages.properties b/src/main/resources/net/tirasa/connid/bundles/googleapps/Messages.properties index 5e159e3..389e444 100644 --- a/src/main/resources/net/tirasa/connid/bundles/googleapps/Messages.properties +++ b/src/main/resources/net/tirasa/connid/bundles/googleapps/Messages.properties @@ -19,7 +19,7 @@ # enclosed by brackets [] replaced by your own identifying information: # "Portions Copyrighted [year] [name of copyright owner]" # ==================== -# Portions Copyrighted 2016 ConnId +# Portions Copyrighted 2016 ConnId. # GoogleApps.connector.display=GoogleApps Connector diff --git a/src/test/java/net/tirasa/connid/bundles/googleapps/GoogleAppsConnectorUnitTests.java b/src/test/java/net/tirasa/connid/bundles/googleapps/GoogleAppsConnectorUnitTests.java index dc74d6c..bb3d4d2 100644 --- a/src/test/java/net/tirasa/connid/bundles/googleapps/GoogleAppsConnectorUnitTests.java +++ b/src/test/java/net/tirasa/connid/bundles/googleapps/GoogleAppsConnectorUnitTests.java @@ -1,22 +1,30 @@ -/** - * Copyright © 2018 ConnId (connid-dev@googlegroups.com) +/* + * ==================== + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Copyright (c) 2016 ConnId All Rights Reserved * - * http://www.apache.org/licenses/LICENSE-2.0 + * The contents of this file are subject to the terms of the Common Development + * and Distribution License("CDDL") (the "License"). You may not use this file + * except in compliance with the License. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You can obtain a copy of the License at + * http://opensource.org/licenses/cddl1.php + * See the License for the specific language governing permissions and limitations + * under the License. + * + * When distributing the Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://opensource.org/licenses/cddl1.php. + * If applicable, add the following below this CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * ==================== */ package net.tirasa.connid.bundles.googleapps; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.io.IOException; import org.identityconnectors.common.security.GuardedString; import org.identityconnectors.framework.api.APIConfiguration; import org.identityconnectors.framework.api.ConnectorFacade; @@ -31,12 +39,27 @@ public class GoogleAppsConnectorUnitTests { + public static class GoogleAppsTestConnector extends GoogleAppsConnector { + + @Override + public void checkAlive() { + // nothing to do + } + } + private static ConnectorFacade CONNECTOR; private static GoogleAppsConfiguration CONN_CONF; + private static ConnectorFacade newFacade() { + ConnectorFacadeFactory factory = ConnectorFacadeFactory.getInstance(); + APIConfiguration impl = TestHelpers.createTestConfiguration(GoogleAppsTestConnector.class, CONN_CONF); + impl.getResultsHandlerConfiguration().setFilteredResultsHandlerInValidationMode(true); + return factory.newInstance(impl); + } + @BeforeAll - public static void setUp() { + public static void setUp() throws IOException { CONN_CONF = new GoogleAppsConfiguration(); CONN_CONF.setClientId("aclientid"); @@ -72,14 +95,8 @@ public static void setUp() { + "\"innerSchemas\": []" + "}]" + "}]"); - CONNECTOR = newFacade(); - } - private static ConnectorFacade newFacade() { - ConnectorFacadeFactory factory = ConnectorFacadeFactory.getInstance(); - APIConfiguration impl = TestHelpers.createTestConfiguration(GoogleAppsConnector.class, CONN_CONF); - impl.getResultsHandlerConfiguration().setFilteredResultsHandlerInValidationMode(true); - return factory.newInstance(impl); + CONNECTOR = newFacade(); } @Test