AIRAVATA-2402 Migrate user roles to Keycloak
Project: http://git-wip-us.apache.org/repos/asf/airavata/repo Commit: http://git-wip-us.apache.org/repos/asf/airavata/commit/bd526ade Tree: http://git-wip-us.apache.org/repos/asf/airavata/tree/bd526ade Diff: http://git-wip-us.apache.org/repos/asf/airavata/diff/bd526ade Branch: refs/heads/develop Commit: bd526ade0ac87f9b3673cebd1e029761ee018a57 Parents: 020ecae Author: Marcus Christie <[email protected]> Authored: Thu Jun 1 16:13:10 2017 -0400 Committer: Marcus Christie <[email protected]> Committed: Thu Jun 1 16:13:10 2017 -0400 ---------------------------------------------------------------------- .../airavata/KeycloakIdentityServerClient.java | 76 ++++++++++++++++++-- .../org/apache/airavata/MigrationManager.java | 46 +++++++++--- .../org/apache/airavata/UserProfileDAO.java | 9 +++ 3 files changed, 119 insertions(+), 12 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/airavata/blob/bd526ade/modules/user-profile-migration/src/main/java/org/apache/airavata/KeycloakIdentityServerClient.java ---------------------------------------------------------------------- diff --git a/modules/user-profile-migration/src/main/java/org/apache/airavata/KeycloakIdentityServerClient.java b/modules/user-profile-migration/src/main/java/org/apache/airavata/KeycloakIdentityServerClient.java index cd55487..ed1bb8a 100644 --- a/modules/user-profile-migration/src/main/java/org/apache/airavata/KeycloakIdentityServerClient.java +++ b/modules/user-profile-migration/src/main/java/org/apache/airavata/KeycloakIdentityServerClient.java @@ -21,28 +21,79 @@ package org.apache.airavata; * */ +import org.apache.airavata.common.utils.ServerSettings; +import org.jboss.resteasy.client.jaxrs.ResteasyClient; +import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder; import org.keycloak.admin.client.Keycloak; +import org.keycloak.admin.client.KeycloakBuilder; import org.keycloak.admin.client.resource.UserResource; import org.keycloak.representations.idm.CredentialRepresentation; +import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.representations.idm.UserRepresentation; +import javax.management.relation.Role; import javax.ws.rs.core.Response; +import java.io.FileInputStream; +import java.io.IOException; +import java.security.KeyStore; import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; public class KeycloakIdentityServerClient { private Keycloak client; - public KeycloakIdentityServerClient(String adminUrl, String realm, String adminUserName, String adminUserPassword) { - this.client = Keycloak.getInstance( + public KeycloakIdentityServerClient(String adminUrl, String realm, String adminUserName, String adminUserPassword, String trustStorePath, String trustStorePassword) { + KeyStore trustKeyStore = loadKeyStore(trustStorePath, trustStorePassword); + this.client = getClient( adminUrl, realm, // the realm to log in to adminUserName, adminUserPassword, // the user - "admin-cli"); // admin-cli is the client ID used for keycloak admin operations. + "admin-cli", // admin-cli is the client ID used for keycloak admin operations. + trustKeyStore); } + private Keycloak getClient(String adminUrl, String realm, String adminUserName, String adminUserPassword, String clientId, KeyStore trustKeyStore) { - boolean migrateUserStore(List<UserProfileDAO> userProfiles, String targetRealm, String tempPassword){ + ResteasyClient resteasyClient = new ResteasyClientBuilder() + .connectionPoolSize(10) + .trustStore(trustKeyStore) + .build(); + return KeycloakBuilder.builder() + .serverUrl(adminUrl) + .realm(realm) + .username(adminUserName) + .password(adminUserPassword) + .clientId(clientId) + .resteasyClient(resteasyClient) + .build(); + } + + private KeyStore loadKeyStore(String trustStorePath, String trustStorePassword) { + + FileInputStream fis = null; + try { + fis = new java.io.FileInputStream(trustStorePath); + KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); + ks.load(fis, trustStorePassword.toCharArray()); + return ks; + } catch (Exception e) { + throw new RuntimeException("Failed to load trust store KeyStore instance", e); + } finally { + if (fis != null) { + try { + fis.close(); + } catch (IOException e) { + throw new RuntimeException("Failed to close trust store FileInputStream", e); + } + } + } + } + + boolean migrateUserStore(List<UserProfileDAO> userProfiles, String targetRealm, String tempPassword, Map<String,String> roleConversionMap){ + + Map<String, RoleRepresentation> allRealmRoles = getRealmRoleNameMap(targetRealm); for(UserProfileDAO userProfile : userProfiles){ UserRepresentation user = new UserRepresentation(); @@ -63,6 +114,17 @@ public class KeycloakIdentityServerClient { user.getEmail(), 0,1); UserResource retirievedUser = this.client.realm(targetRealm).users().get(retrieveCreatedUserList.get(0).getId()); + + // Add user to realm roles + List<RoleRepresentation> userRealmRoles = userProfile.getRoles().stream() + .filter(r -> roleConversionMap.containsKey(r)) + // Convert from IS role name to Keycloak role name + .map(r -> roleConversionMap.get(r)) + // Convert from Keycloak role name to RoleRepresentation + .map(r -> allRealmRoles.get(r)) + .collect(Collectors.toList()); + retirievedUser.roles().realmLevel().add(userRealmRoles); + CredentialRepresentation credential = new CredentialRepresentation(); credential.setType(CredentialRepresentation.PASSWORD); credential.setValue(tempPassword); @@ -74,4 +136,10 @@ public class KeycloakIdentityServerClient { return true; } + private Map<String,RoleRepresentation> getRealmRoleNameMap(String targetRealm) { + return this.client.realm(targetRealm).roles().list() + .stream() + .collect(Collectors.toMap(r -> r.getName(), r -> r)); + } + } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/airavata/blob/bd526ade/modules/user-profile-migration/src/main/java/org/apache/airavata/MigrationManager.java ---------------------------------------------------------------------- diff --git a/modules/user-profile-migration/src/main/java/org/apache/airavata/MigrationManager.java b/modules/user-profile-migration/src/main/java/org/apache/airavata/MigrationManager.java index 85353ce..2cfbe59 100644 --- a/modules/user-profile-migration/src/main/java/org/apache/airavata/MigrationManager.java +++ b/modules/user-profile-migration/src/main/java/org/apache/airavata/MigrationManager.java @@ -21,6 +21,7 @@ package org.apache.airavata; import org.apache.airavata.common.exception.ApplicationSettingsException; import org.apache.airavata.model.security.AuthzToken; +import org.apache.airavata.model.user.Status; import org.apache.airavata.model.user.UserProfile; import org.apache.airavata.service.profile.user.cpi.UserProfileService; import org.apache.thrift.TException; @@ -29,8 +30,8 @@ import org.wso2.carbon.um.ws.api.stub.RemoteUserStoreManagerServiceStub; import org.wso2.carbon.um.ws.api.stub.RemoteUserStoreManagerServiceUserStoreExceptionException; import java.rmi.RemoteException; -import java.util.ArrayList; -import java.util.List; +import java.util.*; +import java.util.stream.Collectors; public class MigrationManager { @@ -38,10 +39,21 @@ public class MigrationManager { private static AuthzToken authzToken = new AuthzToken("empy_token"); private String profileServiceServerHost = "localhost"; private int profileServiceServerPort = 8962; + private Map<String,String> roleConversionMap = createDefaultRoleConversionMap(); + + private Map<String,String> createDefaultRoleConversionMap() { + Map<String,String> roleConversionMap = new HashMap<>(); + roleConversionMap.put("admin", "admin"); + roleConversionMap.put("admin-read-only", "admin-read-only"); + roleConversionMap.put("gateway-user", "gateway-user"); + roleConversionMap.put("user-pending", "user-pending"); + roleConversionMap.put("gateway-provider", "gateway-provider"); + return roleConversionMap; + } /*Add the credentials for all the tenants from which the profile should be migrated to Airavata DB*/ public void setISLoginCredentials(){ - adminCredentials.add(new Wso2ISLoginCredentialsDAO("prod.seagrid","username","password")); + adminCredentials.add(new Wso2ISLoginCredentialsDAO("gateway-id","username","password")); // new credential records here... } @@ -55,7 +67,7 @@ public class MigrationManager { System.out.println("Fetching User Profiles for " + creds.getGateway() + " tenant ..."); try { userList = isClient.getUserList("http://wso2.org/claims/givenname", "*", "default"); - System.out.println("FirstName\tLastName\tEmail\t\t\tuserName\tCountry\tOrganization\tphone"); + System.out.println("FirstName\tLastName\tEmail\t\t\tuserName\tCountry\tOrganization\tphone\tRoles"); String[] claims = {"http://wso2.org/claims/givenname", "http://wso2.org/claims/lastname", "http://wso2.org/claims/emailaddress", @@ -63,7 +75,8 @@ public class MigrationManager { "http://wso2.org/claims/organization", "http://wso2.org/claims/mobile", "http://wso2.org/claims/telephone", - "http://wso2.org/claims/streetaddress"}; + "http://wso2.org/claims/streetaddress", + "http://wso2.org/claims/role"}; for (String user : userList) { UserProfileDAO userProfile = new UserProfileDAO(); ClaimValue[] retrievedClaimValues = isClient.getUserClaimValuesForClaims(user, claims, null); @@ -83,12 +96,14 @@ public class MigrationManager { phones.add(claim.getValue()); } else if(claim.getClaimURI().equals(claims[7])){ userProfile.setAddress(claim.getValue()); + } else if(claim.getClaimURI().equals(claims[8])){ + userProfile.setRoles(convertCommaSeparatedRolesToList(claim.getValue())); } } userProfile.setUserName(user); userProfile.setGatewayID(creds.getGateway()); userProfile.setPhones(phones); - System.out.println(userProfile.getFirstName()+"\t"+userProfile.getLastName()+"\t"+userProfile.getUserName()+"\t"+userProfile.getEmail()+"\t"+userProfile.getCountry()+"\t"+userProfile.getOrganization() + userProfile.getAddress()); + System.out.println(userProfile.getFirstName()+"\t"+userProfile.getLastName()+"\t"+userProfile.getUserName()+"\t"+userProfile.getEmail()+"\t"+userProfile.getCountry()+"\t"+userProfile.getOrganization() + "\t" + userProfile.getAddress() + "\t" + userProfile.getRoles()); userProfileList.add(userProfile); } } catch (RemoteException e) { @@ -105,6 +120,14 @@ public class MigrationManager { return userProfileList; } + private List<String> convertCommaSeparatedRolesToList(String roles) { + + return Arrays.stream(roles.split(",")) + .filter(s -> !"Internal/everyone".equals(s)) + .filter(s -> !"Internal/identity".equals(s)) + .collect(Collectors.toList()); + } + /* Method used to migrate User profiles to Airavata DB by making a call to User profile thrift Service */ private boolean migrateUserProfilesToAiravata(List<UserProfileDAO> ISProfileList) throws TException, ApplicationSettingsException { System.out.println("Initiating migration to Airavata internal DB ..."); @@ -113,6 +136,7 @@ public class MigrationManager { UserProfile airavataUserProfile = new UserProfile(); // Here are the data associations... for(UserProfileDAO ISProfile : ISProfileList){ + airavataUserProfile.setAiravataInternalUserId(ISProfile.getUserName() + "@" + ISProfile.getGatewayID()); airavataUserProfile.setFirstName(ISProfile.getFirstName()); airavataUserProfile.setLastName(ISProfile.getLastName()); airavataUserProfile.setUserId(ISProfile.getUserName()); @@ -123,6 +147,10 @@ public class MigrationManager { airavataUserProfile.setHomeOrganization(ISProfile.getOrganization()); airavataUserProfile.setPhones(ISProfile.getPhones()); airavataUserProfile.setCountry(ISProfile.getCountry()); + airavataUserProfile.setCreationTime(new Date().getTime()); + airavataUserProfile.setLastAccessTime(new Date().getTime()); + airavataUserProfile.setValidUntil(-1); + airavataUserProfile.setState(Status.ACTIVE); //TODO: fix authtzToken, for now we are using empty token client.addUserProfile(authzToken, airavataUserProfile); } @@ -133,8 +161,10 @@ public class MigrationManager { KeycloakIdentityServerClient client = new KeycloakIdentityServerClient("https://iam.scigap.org/auth", "master", "SuperRealmUsername", - "MasterRealmPassword"); - client.migrateUserStore(Wso2ISProfileList,"keycloakTargetRealm","tempPassword"); + "MasterRealmPassword", + "trustStorePath", + "trustStorePassword"); + client.migrateUserStore(Wso2ISProfileList,"keycloakTargetRealm","tempPassword", roleConversionMap); } public static void main(String[] args) { http://git-wip-us.apache.org/repos/asf/airavata/blob/bd526ade/modules/user-profile-migration/src/main/java/org/apache/airavata/UserProfileDAO.java ---------------------------------------------------------------------- diff --git a/modules/user-profile-migration/src/main/java/org/apache/airavata/UserProfileDAO.java b/modules/user-profile-migration/src/main/java/org/apache/airavata/UserProfileDAO.java index cb000cf..12571b2 100644 --- a/modules/user-profile-migration/src/main/java/org/apache/airavata/UserProfileDAO.java +++ b/modules/user-profile-migration/src/main/java/org/apache/airavata/UserProfileDAO.java @@ -32,6 +32,7 @@ public class UserProfileDAO { private List<String> Phones; private String gatewayID; private String address; + private List<String> roles; public String getAddress() { return address; @@ -115,4 +116,12 @@ public class UserProfileDAO { public void setEmail(String email) { this.email = email; } + + public List<String> getRoles() { + return roles; + } + + public void setRoles(List<String> roles) { + this.roles = roles; + } }
