diff --git a/src/main/java/com/evolveum/polygon/connector/msgraphapi/GroupProcessing.java b/src/main/java/com/evolveum/polygon/connector/msgraphapi/GroupProcessing.java index 28d11ff..7dd148b 100644 --- a/src/main/java/com/evolveum/polygon/connector/msgraphapi/GroupProcessing.java +++ b/src/main/java/com/evolveum/polygon/connector/msgraphapi/GroupProcessing.java @@ -22,6 +22,7 @@ public class GroupProcessing extends ObjectProcessing { private final static String GROUPS = "/groups"; private final static String USERS = "/users"; + private final static String ROLE_ASSIGNMENT = "/roleManagement/directory/roleAssignments"; private static final String ATTR_ALLOWEXTERNALSENDERS = "allowExternalSenders"; private static final String ATTR_AUTOSUBSCRIBENEWMEMBERS = "autoSubscribeNewMembers"; @@ -47,10 +48,13 @@ public class GroupProcessing extends ObjectProcessing { private static final String ATTR_ISASSIGNABLETOROLE = "isAssignableToRole"; private static final String ATTR_MEMBERS = "members"; private static final String ATTR_OWNERS = "owners"; + private static final String ATTR_MEMBER_OF_ROLE = "memberOfRole"; + protected static final Set EXCLUDE_ATTRS_OF_GROUP = Stream.of( ATTR_MEMBERS, - ATTR_OWNERS + ATTR_OWNERS, + ATTR_MEMBER_OF_ROLE ).collect(Collectors.toSet()); protected static final Set UPDATABLE_MULTIPLE_VALUE_ATTRS_OF_GROUP = Stream.of( @@ -192,6 +196,12 @@ protected ObjectClassInfo objectClassInfo() { attrOwners.setType(String.class).setCreateable(true).setUpdateable(true).setReadable(true).setMultiValued(true).setReturnedByDefault(false); groupObjClassBuilder.addAttributeInfo(attrOwners.build()); + AttributeInfoBuilder attrRoleMembers = new AttributeInfoBuilder(ATTR_MEMBER_OF_ROLE); + attrRoleMembers.setRequired(false).setType(String.class).setMultiValued(true) + .setCreateable(false).setUpdateable(false).setReadable(true) + .setReturnedByDefault(false); + groupObjClassBuilder.addAttributeInfo(attrRoleMembers.build()); + return groupObjClassBuilder.build(); } @@ -589,6 +599,24 @@ private JSONObject saturateGroupOwnership(JSONObject group) { return group; } + /** + * Query a group's roles, add them to the group's JSON attributes (multivalue) + * + * @param group Group to query for (JSON object resulting from previous API call) + * @return Original JSON, enriched with roles information + */ + private JSONObject saturateGroupRoleMembership(JSONObject group) { + final GraphEndpoint endpoint = getGraphEndpoint(); + final String uid = group.getString(ATTR_ID); + + LOG.info("[GET] - saturateRoleMembership(), for group with UID: {0}", uid); + + final String customQuery = "$expand=roleDefinition&$select=roleDefinitionId&$filter=principalId eq '" + uid + "'"; + final JSONArray groupMembership = endpoint.executeListRequest(ROLE_ASSIGNMENT, customQuery, null, true); + group.put(ATTR_MEMBER_OF_ROLE, getJSONArray(groupMembership, "roleDefinitionId")); + return group; + } + /** * Adds group accounts to the group JSON Object. Decides upon members or owners based on @param isMembers. * @param group Original group object to extend with owners or members account ids @@ -628,10 +656,15 @@ protected boolean handleJSONObject(OperationOptions options, JSONObject group, R group = saturateGroupOwnership(group); } + if (shouldSaturate(options, ObjectClass.GROUP_NAME, ATTR_MEMBER_OF_ROLE)) { + group = saturateGroupRoleMembership(group); + } + ConnectorObjectBuilder builder = convertGroupJSONObjectToConnectorObject(group); incompleteIfNecessary(options, ObjectClass.GROUP_NAME, ATTR_MEMBERS, builder); incompleteIfNecessary(options, ObjectClass.GROUP_NAME, ATTR_OWNERS, builder); + incompleteIfNecessary(options, ObjectClass.GROUP_NAME, ATTR_MEMBER_OF_ROLE, builder); final ConnectorObject connectorObject = builder.build(); LOG.ok("handleJSONObject, group: {0}, \n\tconnectorObject: {1}", group.get("id"), connectorObject); @@ -671,6 +704,7 @@ private ConnectorObjectBuilder convertGroupJSONObjectToConnectorObject(JSONObjec getMultiIfExists(group, ATTR_MEMBERS, builder); getMultiIfExists(group, ATTR_OWNERS, builder); + getMultiIfExists(group, ATTR_MEMBER_OF_ROLE, builder); return builder; } diff --git a/src/test/java/com/evolveum/polygon/connector/msgraphapi/common/ObjectConstants.java b/src/test/java/com/evolveum/polygon/connector/msgraphapi/common/ObjectConstants.java index ad0575a..ccce124 100644 --- a/src/test/java/com/evolveum/polygon/connector/msgraphapi/common/ObjectConstants.java +++ b/src/test/java/com/evolveum/polygon/connector/msgraphapi/common/ObjectConstants.java @@ -114,6 +114,7 @@ public interface ObjectConstants { String ATTR_UNSEENCOUNT = "unseenCount"; String ATTR_VISIBILITY = "visibility"; String ATTR_OWNERS = "owners"; + String ATTR_MEMBER_OF_ROLE = "memberOfRole"; // Role @@ -128,5 +129,4 @@ public interface ObjectConstants { String ATTR_INHERIT_PERMISSIONS_FROM_ODATA_CONTEXT = "inheritsPermissionsFrom@odata.context"; String ATTR_INHERIT_PERMISSIONS_FROM = "inheritsPermissionsFrom"; String ATTR_INHERIT_PERMISSIONS_FROM_ALL = ATTR_INHERIT_PERMISSIONS_FROM + "." + ATTR_ID; - } diff --git a/src/test/java/com/evolveum/polygon/connector/msgraphapi/integration/GroupPerformanceTests.java b/src/test/java/com/evolveum/polygon/connector/msgraphapi/integration/GroupPerformanceTests.java index 9fd3477..1b546f4 100644 --- a/src/test/java/com/evolveum/polygon/connector/msgraphapi/integration/GroupPerformanceTests.java +++ b/src/test/java/com/evolveum/polygon/connector/msgraphapi/integration/GroupPerformanceTests.java @@ -379,4 +379,39 @@ public void CreateSecurityGroupTest() { connector.delete(objectClassGroup, groupUid, options); connector.dispose(); } + + @Test(priority = 31) + public void GetGroupRoleMembershipTest() { + MSGraphConnector connector = new MSGraphConnector(); + MSGraphConfiguration conf = getConfiguration(); + + Map operationOptions = new HashMap<>(); + operationOptions.put("ALLOW_PARTIAL_ATTRIBUTE_VALUES", false); + operationOptions.put(OperationOptions.OP_ATTRIBUTES_TO_GET, new String[]{ ATTR_MEMBER_OF_ROLE }); + operationOptions.put(OperationOptions.OP_PAGED_RESULTS_OFFSET, 1); + operationOptions.put(OperationOptions.OP_PAGE_SIZE, 100); + OperationOptions options = new OperationOptions(operationOptions); + + final ArrayList resultsGroup = new ArrayList<>(); + SearchResultsHandler handlerGroup = new SearchResultsHandler() { + + @Override + public boolean handle(ConnectorObject connectorObject) { + resultsGroup.add(connectorObject); + return true; + } + + @Override + public void handleResult(SearchResult result) { + } + }; + connector.init(conf); + connector.executeQuery(ObjectClass.GROUP, null, handlerGroup, options); + + msGraphConnector.dispose(); + + if (resultsGroup.size() < 100) { + throw new InvalidAttributeValueException("Non exist 100 groups."); + } + } }