diff --git a/java/middleware-commons/src/main/java/dev/sunbirdrc/registry/middleware/util/Constants.java b/java/middleware-commons/src/main/java/dev/sunbirdrc/registry/middleware/util/Constants.java index a6d1cbf8a..8cc179f75 100644 --- a/java/middleware-commons/src/main/java/dev/sunbirdrc/registry/middleware/util/Constants.java +++ b/java/middleware-commons/src/main/java/dev/sunbirdrc/registry/middleware/util/Constants.java @@ -123,6 +123,7 @@ public class Constants { public static final String USER_ID = "userId"; public static final String EMAIL = "email"; public static final String MOBILE = "mobile"; + public static final String ROLES = "roles"; public static final String SVG_MEDIA_TYPE = "image/svg+xml"; public static final String CONNECTION_FAILURE = "CONNECTION_FAILURE"; diff --git a/java/middleware-commons/src/main/java/dev/sunbirdrc/registry/middleware/util/JSONUtil.java b/java/middleware-commons/src/main/java/dev/sunbirdrc/registry/middleware/util/JSONUtil.java index 0afddd6c5..caf5b6599 100644 --- a/java/middleware-commons/src/main/java/dev/sunbirdrc/registry/middleware/util/JSONUtil.java +++ b/java/middleware-commons/src/main/java/dev/sunbirdrc/registry/middleware/util/JSONUtil.java @@ -68,6 +68,10 @@ public static Map convertJsonNodeToMap(JsonNode object) { return mapObject; } + public static List convertJsonNodeToList(Object obj){ + return new ObjectMapper().convertValue(obj, List.class); + } + public static String getStringWithReplacedText(String payload, String value, String replacement) { Pattern pattern = Pattern.compile(value); Matcher matcher = pattern.matcher(payload); diff --git a/java/middleware/registry-middleware/keycloak/pom.xml b/java/middleware/registry-middleware/keycloak/pom.xml index 84868de9e..f7dcfd988 100644 --- a/java/middleware/registry-middleware/keycloak/pom.xml +++ b/java/middleware/registry-middleware/keycloak/pom.xml @@ -44,6 +44,11 @@ 1.7.32 compile + + dev.sunbirdrc + middleware-commons + 2.0.3 + org.springframework.boot spring-boot-starter-test diff --git a/java/middleware/registry-middleware/keycloak/src/main/java/dev/sunbirdrc/keycloak/KeycloakAdminUtil.java b/java/middleware/registry-middleware/keycloak/src/main/java/dev/sunbirdrc/keycloak/KeycloakAdminUtil.java index c5569a576..c190a2dc9 100644 --- a/java/middleware/registry-middleware/keycloak/src/main/java/dev/sunbirdrc/keycloak/KeycloakAdminUtil.java +++ b/java/middleware/registry-middleware/keycloak/src/main/java/dev/sunbirdrc/keycloak/KeycloakAdminUtil.java @@ -1,5 +1,7 @@ package dev.sunbirdrc.keycloak; +import com.fasterxml.jackson.databind.JsonNode; +import dev.sunbirdrc.registry.middleware.util.JSONUtil; import dev.sunbirdrc.pojos.ComponentHealthInfo; import dev.sunbirdrc.pojos.HealthIndicator; import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder; @@ -9,6 +11,7 @@ import org.keycloak.admin.client.resource.*; import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.representations.idm.GroupRepresentation; +import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -81,8 +84,9 @@ private Keycloak buildKeycloak(int httpMaxConnections) { .build(); } - public String createUser(String entityName, String userName, String email, String mobile) throws OwnerCreationException { + public String createUser(String entityName, String userName, String email, String mobile, JsonNode realmRoles) throws OwnerCreationException { logger.info("Creating user with mobile_number : " + userName); + List roles = JSONUtil.convertJsonNodeToList(realmRoles); UserRepresentation newUser = createUserRepresentation(entityName, userName, email, mobile); GroupRepresentation entityGroup = createGroupRepresentation(entityName); keycloak.realm(realm).groups().add(entityGroup); @@ -93,12 +97,13 @@ public String createUser(String entityName, String userName, String email, Strin logger.info("User ID path" + response.getLocation().getPath()); String userID = response.getLocation().getPath().replaceAll(".*/([^/]+)$", "$1"); logger.info("User ID : " + userID); + addRolesToUser(roles, userID); if (!emailActions.isEmpty()) usersResource.get(userID).executeActionsEmail(emailActions); return userID; } else if (response.getStatus() == 409) { logger.info("UserID: {} exists", userName); - return updateExistingUserAttributes(entityName, userName, email, mobile); + return updateExistingUserAttributes(entityName, userName, email, mobile, roles); } else if (response.getStatus() == 500) { throw new OwnerCreationException("Keycloak user creation error"); } else { @@ -107,19 +112,38 @@ public String createUser(String entityName, String userName, String email, Strin } } + private void addRolesToUser(List roles, String userID){ + /** Add the 'view-realm' role to client to access the keycloak roles + * Go to Keycloak -> open client(which is configured as client_id in application.yml) -> + * Service Account Roles -> Client Roles, select 'realm-management' -> Assign 'view-relam' role + */ + if(!roles.isEmpty()) { + List roleToAdd = new ArrayList<>(); + for (String role : roles) { + roleToAdd.add(keycloak.realm(realm).roles().get(role).toRepresentation()); + } + UserResource userResource = keycloak.realm(realm).users().get(userID); + userResource.roles().realmLevel().add(roleToAdd); + logger.info("Added the roles: {}, to user: {}", roles, userID); + } else { + logger.info("No roles added to the user: {}", userID); + } + } + private GroupRepresentation createGroupRepresentation(String entityName) { GroupRepresentation groupRepresentation = new GroupRepresentation(); groupRepresentation.setName(entityName); return groupRepresentation; } - private String updateExistingUserAttributes(String entityName, String userName, String email, String mobile) throws OwnerCreationException { + private String updateExistingUserAttributes(String entityName, String userName, String email, String mobile, List roles) throws OwnerCreationException { Optional userRepresentationOptional = getUserByUsername(userName); if (userRepresentationOptional.isPresent()) { UserResource userResource = userRepresentationOptional.get(); UserRepresentation userRepresentation = userResource.toRepresentation(); updateUserAttributes(entityName, email, mobile, userRepresentation); userResource.update(userRepresentation); + addRolesToUser(roles, userName); return userRepresentation.getId(); } else { logger.error("Failed fetching user by username: {}", userName); diff --git a/java/middleware/registry-middleware/workflow/src/main/resources/workflow/statetransitions.drl b/java/middleware/registry-middleware/workflow/src/main/resources/workflow/statetransitions.drl index 1f6a878e0..b16362c9f 100644 --- a/java/middleware/registry-middleware/workflow/src/main/resources/workflow/statetransitions.drl +++ b/java/middleware/registry-middleware/workflow/src/main/resources/workflow/statetransitions.drl @@ -41,7 +41,7 @@ rule "Create entity owner for newly added owner fields" stateDefinition:StateContext(isOwnershipProperty() && isOwnerNewlyAdded() && isLoginEnabled()); then String owner = keycloakAdminUtil.createUser(stateDefinition.getEntityName(), stateDefinition.getUpdated().get("userId").textValue(), - stateDefinition.getUpdated().get("email").textValue(), stateDefinition.getUpdated().get("mobile").textValue()); + stateDefinition.getUpdated().get("email").textValue(), stateDefinition.getUpdated().get("mobile").textValue(), stateDefinition.getUpdated().get("roles")); stateDefinition.addOwner(owner); end diff --git a/java/registry/src/main/java/dev/sunbirdrc/registry/helper/EntityStateHelper.java b/java/registry/src/main/java/dev/sunbirdrc/registry/helper/EntityStateHelper.java index c7bae041f..48f5f8ea5 100644 --- a/java/registry/src/main/java/dev/sunbirdrc/registry/helper/EntityStateHelper.java +++ b/java/registry/src/main/java/dev/sunbirdrc/registry/helper/EntityStateHelper.java @@ -119,6 +119,7 @@ private ObjectNode createOwnershipNode(JsonNode entityNode, String entityName, O objectNode.put(MOBILE, entityNode.at(String.format("/%s%s", entityName, mobilePath)).asText("")); objectNode.put(EMAIL, entityNode.at(String.format("/%s%s", entityName, emailPath)).asText("")); objectNode.put(USER_ID, entityNode.at(String.format("/%s%s", entityName, userIdPath)).asText("")); + objectNode.set(ROLES, entityNode.at(String.format("/%s", entityName)).has(ROLES) ? entityNode.at(String.format("/%s%s", entityName, "/roles")) : new ObjectMapper().createArrayNode()); return objectNode; } diff --git a/java/registry/src/test/java/dev/sunbirdrc/registry/helper/EntityStateHelperTest.java b/java/registry/src/test/java/dev/sunbirdrc/registry/helper/EntityStateHelperTest.java index 834adcdbe..bae7c4f5e 100644 --- a/java/registry/src/test/java/dev/sunbirdrc/registry/helper/EntityStateHelperTest.java +++ b/java/registry/src/test/java/dev/sunbirdrc/registry/helper/EntityStateHelperTest.java @@ -39,6 +39,7 @@ import java.util.Map; import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; @@ -111,21 +112,21 @@ public void shouldBeNoStateChangeIfTheDataDidNotChange() throws IOException { @Test public void shouldCreateNewOwnersForNewlyAddedOwnerFields() throws IOException, DuplicateRecordException, EntityCreationException, OwnerCreationException { - when(keycloakAdminUtil.createUser(anyString(), anyString(), anyString(), anyString())).thenReturn("456"); + when(keycloakAdminUtil.createUser(anyString(), anyString(), anyString(), anyString(), any())).thenReturn("456"); JsonNode test = m.readTree(new File(getBaseDir() + "shouldAddNewOwner.json")); runTest(test.get("existing"), test.get("updated"), test.get("expected"), Collections.emptyList()); } @Test public void shouldNotCreateNewOwners() throws IOException, DuplicateRecordException, EntityCreationException, OwnerCreationException { - when(keycloakAdminUtil.createUser(anyString(), anyString(), anyString(), anyString())).thenReturn("456"); + when(keycloakAdminUtil.createUser(anyString(), anyString(), anyString(), anyString(), any())).thenReturn("456"); JsonNode test = m.readTree(new File(getBaseDir() + "shouldNotAddNewOwner.json")); runTest(test.get("existing"), test.get("updated"), test.get("expected"), Collections.emptyList()); } @Test public void shouldNotModifyExistingOwners() throws IOException, DuplicateRecordException, EntityCreationException, OwnerCreationException { - when(keycloakAdminUtil.createUser(anyString(), anyString(), anyString(), anyString())).thenReturn("456"); + when(keycloakAdminUtil.createUser(anyString(), anyString(), anyString(), anyString(),any())).thenReturn("456"); JsonNode test = m.readTree(new File(getBaseDir() + "shouldNotModifyExistingOwner.json")); runTest(test.get("existing"), test.get("updated"), test.get("expected"), Collections.emptyList()); } diff --git a/java/registry/src/test/java/dev/sunbirdrc/registry/helper/RegistryHelperTest.java b/java/registry/src/test/java/dev/sunbirdrc/registry/helper/RegistryHelperTest.java index cce15fd98..c3837d5c1 100644 --- a/java/registry/src/test/java/dev/sunbirdrc/registry/helper/RegistryHelperTest.java +++ b/java/registry/src/test/java/dev/sunbirdrc/registry/helper/RegistryHelperTest.java @@ -341,7 +341,7 @@ public void shouldCreateOwnersForInvite() throws Exception { JsonNode inviteJson = new ObjectMapper().readTree("{\"Institute\":{\"email\":\"gecasu.ihises@tovinit.com\",\"instituteName\":\"gecasu\"}}"); mockDefinitionManager(); String testUserId = "be6d30e9-7c62-4a05-b4c8-ee28364da8e4"; - when(keycloakAdminUtil.createUser(any(), any(), any(), any())).thenReturn(testUserId); + when(keycloakAdminUtil.createUser(any(), any(), any(), any(), any())).thenReturn(testUserId); when(registryService.addEntity(any(), any(), any(), anyBoolean())).thenReturn(UUID.randomUUID().toString()); when(shardManager.getShard(any())).thenReturn(new Shard()); ReflectionTestUtils.setField(registryHelper, "workflowEnabled", true); @@ -355,7 +355,7 @@ public void shouldSendInviteInvitationsAfterCreatingOwners() throws Exception { JsonNode inviteJson = new ObjectMapper().readTree("{\"Institute\":{\"email\":\"gecasu.ihises@tovinit.com\",\"instituteName\":\"gecasu\"}}"); mockDefinitionManager(); String testUserId = "be6d30e9-7c62-4a05-b4c8-ee28364da8e4"; - when(keycloakAdminUtil.createUser(any(), any(), any(), any())).thenReturn(testUserId); + when(keycloakAdminUtil.createUser(any(), any(), any(), any(), any())).thenReturn(testUserId); when(registryService.addEntity(any(), any(), any(), anyBoolean())).thenReturn(UUID.randomUUID().toString()); when(shardManager.getShard(any())).thenReturn(new Shard()); ReflectionTestUtils.setField(registryHelper, "notificationEnabled", true); @@ -386,7 +386,7 @@ public void shouldSendMultipleInviteInvitationsAfterCreatingOwners() throws Exce " \"adminMobile\": \"1234\"\n" + "}}"); String testUserId = "be6d30e9-7c62-4a05-b4c8-ee28364da8e4"; - when(keycloakAdminUtil.createUser(any(), any(), any(), any())).thenReturn(testUserId); + when(keycloakAdminUtil.createUser(any(), any(), any(), any(), any())).thenReturn(testUserId); when(registryService.addEntity(any(), any(), any(), anyBoolean())).thenReturn(UUID.randomUUID().toString()); when(shardManager.getShard(any())).thenReturn(new Shard()); mockDefinitionManager();