All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.airavata.MigrationManager Maven / Gradle / Ivy

The newest version!
/**
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */
package org.apache.airavata;

import org.apache.airavata.api.Airavata;
import org.apache.airavata.api.client.AiravataClientFactory;
import org.apache.airavata.common.exception.ApplicationSettingsException;
import org.apache.airavata.common.utils.Constants;
import org.apache.airavata.credential.store.client.CredentialStoreClientFactory;
import org.apache.airavata.credential.store.cpi.CredentialStoreService;
import org.apache.airavata.model.appcatalog.gatewayprofile.GatewayResourceProfile;
import org.apache.airavata.model.credential.store.PasswordCredential;
import org.apache.airavata.model.error.AiravataClientException;
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.model.workspace.Gateway;
import org.apache.airavata.model.workspace.GatewayApprovalStatus;
import org.apache.airavata.service.profile.client.ProfileServiceClientFactory;
import org.apache.airavata.service.profile.iam.admin.services.cpi.IamAdminServices;
import org.apache.airavata.service.profile.iam.admin.services.cpi.exception.IamAdminServicesException;
import org.apache.airavata.service.profile.tenant.cpi.TenantProfileService;
import org.apache.airavata.service.profile.tenant.cpi.exception.TenantProfileServiceException;
import org.apache.airavata.service.profile.user.cpi.UserProfileService;
import org.apache.thrift.TException;
import org.wso2.carbon.um.ws.api.stub.ClaimValue;
import org.wso2.carbon.um.ws.api.stub.RemoteUserStoreManagerServiceStub;
import org.wso2.carbon.um.ws.api.stub.RemoteUserStoreManagerServiceUserStoreExceptionException;

import java.io.FileInputStream;
import java.io.IOException;
import java.rmi.RemoteException;
import java.util.*;
import java.util.stream.Collectors;

public class MigrationManager {

    private ArrayList adminCredentials = new ArrayList();
    private static AuthzToken authzToken = new AuthzToken("empty_token");

    // Default values
    private String profileServiceServerHost = "localhost";
    private int profileServiceServerPort = 8962;
    private String airavataServiceServerHost = "localhost";
    private int airavataServiceServerPort = 8930;
    private boolean airavataServiceSecure = false;
    private Map roleConversionMap = createDefaultRoleConversionMap();
    private String gatewayId = "gateway-id";
    private String wso2ISAdminUsername = "username";
    private String wso2ISAdminPassword = "password";
    private String keycloakServiceURL = "https://iam.scigap.org/auth";
    private String keycloakAdminUsername = "username";
    private String keycloakAdminPassword = "password";
    private String keycloakTrustStorePath = "../../modules/configuration/server/src/main/resources/client_truststore.jks";
    private String keycloakTrustStorePassword = "password";
    private String keycloakTemporaryUserPassword = "tempPassword";
    // For some gateways in the legacy gateways table, the following information is missing and needs to be provided
    private String gatewayURL = "http://localhost";
    private String gatewayAdminUsername = "admin";
    private String gatewayAdminFirstName = "Admin";
    private String gatewayAdminLastName = "User";
    private String gatewayAdminEmailAddress = "[email protected]";

    // Names of properties in user-profile-migration.properties.template
    private final static String GATEWAY_ID = "gateway-id";
    private final static String GATEWAY_URL = "gateway.url";
    private final static String GATEWAY_ADMIN_USERNAME = "gateway.admin.username";
    private final static String GATEWAY_ADMIN_FIRST_NAME = "gateway.admin.first.name";
    private final static String GATEWAY_ADMIN_LAST_NAME = "gateway.admin.last.name";
    private final static String GATEWAY_ADMIN_EMAIL_ADDRESS = "gateway.admin.email.address";
    private final static String WSO2IS_ADMIN_USERNAME = "wso2is.admin.username";
    private final static String WSO2IS_ADMIN_PASSWORD = "wso2is.admin.password";
    private final static String WSO2IS_ADMIN_ROLENAME = "wso2is.admin.rolename";
    private final static String WSO2IS_ADMIN_READ_ONLY_ROLENAME = "wso2is.admin-read-only.rolename";
    private final static String WSO2IS_GATEWAY_USER_ROLENAME = "wso2is.gateway-user.rolename";
    private final static String WSO2IS_USER_PENDING_ROLENAME = "wso2is.user-pending.rolename";
    private final static String WSO2IS_GATEWAY_PROVIDER_ROLENAME = "wso2is.gateway-provider.rolename";
    private final static String AIRAVATA_SERVICE_HOST = "airavata.service.host";
    private final static String AIRAVATA_SERVICE_PORT = "airavata.service.port";
    private final static String AIRAVATA_SERVICE_SECURE = "airavata.service.secure";
    private final static String PROFILE_SERVICE_HOST = "profile.service.host";
    private final static String PROFILE_SERVICE_PORT = "profile.service.port";
    private final static String KEYCLOAK_ADMIN_USERNAME = "keycloak.admin.username";
    private final static String KEYCLOAK_ADMIN_PASSWORD = "keycloak.admin.password";
    private final static String KEYCLOAK_SERVICE_URL = "keycloak.service-url";
    private final static String KEYCLOAK_TRUSTSTORE_PATH = "keycloak.truststore.path";
    private final static String KEYCLOAK_TRUSTSTORE_PASSWORD = "keycloak.truststore.password";
    private final static String KEYCLOAK_USER_TEMP_PASSWORD = "keycloak.user.temp.password";


    private Map createDefaultRoleConversionMap() {
        Map 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(this.gatewayId, this.wso2ISAdminUsername, this.wso2ISAdminPassword));
        // new credential records here...
    }

    /* Method used to fetch all the user profiles from the registered tenants */

    public List getUserProfilesFromWso2IS(){
        ArrayList userProfileList = new ArrayList();
        for(Wso2ISLoginCredentialsDAO creds:adminCredentials){
            RemoteUserStoreManagerServiceStub isClient = Wso2IdentityServerClient.getAdminServiceClient(creds.getLoginUserName(),creds.getLoginPassword(),"RemoteUserStoreManagerService");
            String[] userList;
            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\tRoles");
                String[] claims = {"http://wso2.org/claims/givenname",
                        "http://wso2.org/claims/lastname",
                        "http://wso2.org/claims/emailaddress",
                        "http://wso2.org/claims/country",
                        "http://wso2.org/claims/organization",
                        "http://wso2.org/claims/mobile",
                        "http://wso2.org/claims/telephone",
                        "http://wso2.org/claims/streetaddress",
                        "http://wso2.org/claims/role",
                        "http://wso2.org/claims/identity/accountLocked"};
                for (String user : userList) {
                    UserProfileDAO userProfile = new UserProfileDAO();
                    ClaimValue[] retrievedClaimValues = isClient.getUserClaimValuesForClaims(user, claims, null);
                    List phones = new ArrayList();
                    for(ClaimValue claim:retrievedClaimValues){
                        if(claim.getClaimURI().equals(claims[0])){
                            userProfile.setFirstName(claim.getValue());
                        }else if(claim.getClaimURI().equals(claims[1])){
                            userProfile.setLastName(claim.getValue());
                        }else if(claim.getClaimURI().equals(claims[2])){
                            userProfile.setEmail(claim.getValue());
                        }else if(claim.getClaimURI().equals(claims[3])){
                            userProfile.setCountry(claim.getValue());
                        }else if(claim.getClaimURI().equals(claims[4])){
                            userProfile.setOrganization(claim.getValue());
                        }else if(claim.getClaimURI().equals(claims[5]) || claim.getClaimURI().equals(claims[6])){
                            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()));
                        } else if(claim.getClaimURI().equals(claims[9])){
                            userProfile.setAccountLocked(claim.getValue().equals("true"));
                        }
                    }
                    // Lowercase all usernames as required by Keycloak and User Profile service
                    userProfile.setUserName(user.toLowerCase());
                    userProfile.setGatewayID(creds.getGateway());
                    userProfile.setPhones(phones);
                    if (!userProfile.isAccountLocked()) {
                        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);
                    } else {
                        System.out.println("Skipping locked account for user " + user + "!");
                    }
                }
            } catch (RemoteException e) {
                System.out.println(e.getMessage());
                System.out.println(e.getCause());
                e.printStackTrace();
            } catch (RemoteUserStoreManagerServiceUserStoreExceptionException e) {
                System.out.println(e.getMessage());
                System.out.println(e.getCause());
                e.printStackTrace();
            }
        }
        System.out.println("User profiles from all the tenant are retrieved ...");
        return userProfileList;
    }

    private List convertCommaSeparatedRolesToList(String roles) {

        return Arrays.stream(roles.split(","))
                .filter(s -> !"Internal/everyone".equals(s))
                .filter(s -> !"Internal/identity".equals(s))
                .collect(Collectors.toList());
    }

    private TenantProfileService.Client getTenantProfileServiceClient() throws TenantProfileServiceException {

        return ProfileServiceClientFactory.createTenantProfileServiceClient(profileServiceServerHost, profileServiceServerPort);
    }

    private Airavata.Client getAiravataClient() throws AiravataClientException {
        return AiravataClientFactory.createAiravataClient(airavataServiceServerHost, airavataServiceServerPort);
    }

    private Airavata.Client getAiravataSecureClient() throws AiravataClientException {
        return AiravataClientFactory.createAiravataSecureClient(airavataServiceServerHost, airavataServiceServerPort, keycloakTrustStorePath, keycloakTrustStorePassword, 10000);
    }

    private IamAdminServices.Client getIamAdminServicesClient() throws IamAdminServicesException {
        return ProfileServiceClientFactory.createIamAdminServiceClient(profileServiceServerHost, profileServiceServerPort);
    }

    private PasswordCredential getPasswordCredential() {
        PasswordCredential passwordCredential = new PasswordCredential();
        passwordCredential.setGatewayId("dummy");
        passwordCredential.setPortalUserName("dummy");
        passwordCredential.setLoginUserName(keycloakAdminUsername);
        passwordCredential.setPassword(keycloakAdminPassword);
        return passwordCredential;
    }

    private boolean migrateGatewayProfileToAiravata() throws TException {

        TenantProfileService.Client tenantProfileServiceClient = getTenantProfileServiceClient();
        Airavata.Client airavataClient = airavataServiceSecure ? getAiravataSecureClient() : getAiravataClient();
        IamAdminServices.Client iamAdminServicesClient = getIamAdminServicesClient();

        // Get Gateway from Airavata API
        Gateway gateway = airavataClient.getGateway(authzToken, gatewayId);

        if (!GatewayApprovalStatus.APPROVED.equals(gateway.getGatewayApprovalStatus())) {
            throw new RuntimeException("Gateway " + gatewayId + " is not APPROVED! Status is " + gateway.getGatewayApprovalStatus());
        }
        // Add Gateway through TenantProfileService
        if (!tenantProfileServiceClient.isGatewayExist(authzToken, gatewayId)) {

            System.out.println("Gateway [" + gatewayId + "] doesn't exist, adding in Profile Service...");
            String airavataInternalGatewayId = tenantProfileServiceClient.addGateway(authzToken, gateway);
            gateway.setAiravataInternalGatewayId(airavataInternalGatewayId);
        } else {

            System.out.println("Gateway [" + gatewayId + "] already exists in Profile Service");
            gateway = tenantProfileServiceClient.getGateway(authzToken, gatewayId);
        }

        // Gateway URL is required by IAM Admin Services
        if (gateway.getGatewayURL() == null) {
            gateway.setGatewayURL(this.gatewayURL);
        }
        // Following are also required by IAM Admin Services in order to create an admin user for the realm
        if (gateway.getIdentityServerUserName() == null) {
            gateway.setIdentityServerUserName(this.gatewayAdminUsername);
        }
        if (gateway.getGatewayAdminFirstName() == null) {
            gateway.setGatewayAdminFirstName(this.gatewayAdminFirstName);
        }
        if (gateway.getGatewayAdminLastName() == null) {
            gateway.setGatewayAdminLastName(this.gatewayAdminLastName);
        }
        if (gateway.getGatewayAdminEmail() == null) {
            gateway.setGatewayAdminEmail(this.gatewayAdminEmailAddress);
        }

        // Add Keycloak Tenant for Gateway
        System.out.println("Creating Keycloak Tenant for gateway ...");
        Gateway gatewayWithIdAndSecret = iamAdminServicesClient.setUpGateway(authzToken, gateway);

        // Update Gateway profile with the client id and secret
        System.out.println("Updating gateway with OAuth client id and secret ...");
        tenantProfileServiceClient.updateGateway(authzToken, gatewayWithIdAndSecret);

        KeycloakIdentityServerClient keycloakIdentityServerClient = getKeycloakIdentityServerClient();
        // Set the admin user's password to the same as it was for wso2IS
        keycloakIdentityServerClient.setUserPassword(gatewayId, this.gatewayAdminUsername, this.wso2ISAdminPassword);

        // Create password credential for admin username and password
        String passwordToken = airavataClient.registerPwdCredential(authzToken, gatewayId, this.gatewayAdminUsername, this.gatewayAdminUsername, this.wso2ISAdminPassword, "Keycloak admin password for realm " + gatewayId);

        // Update gateway resource profile with tenant id (gatewayId) and admin user password token
        GatewayResourceProfile gatewayResourceProfile = airavataClient.getGatewayResourceProfile(authzToken, gatewayId);
        gatewayResourceProfile.setIdentityServerTenant(gatewayId);
        gatewayResourceProfile.setIdentityServerPwdCredToken(passwordToken);
        airavataClient.updateGatewayResourceProfile(authzToken, gatewayId, gatewayResourceProfile);
        return true;
    }

    /* Method used to migrate User profiles to Airavata DB by making a call to User profile thrift Service */
    private boolean migrateUserProfilesToAiravata(List ISProfileList) throws TException, ApplicationSettingsException {
        System.out.println("Initiating migration to Airavata internal DB ...");
        UserProfileService.Client client = ProfileServiceClientFactory.createUserProfileServiceClient(profileServiceServerHost, profileServiceServerPort);
        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());
            airavataUserProfile.setGatewayId(ISProfile.getGatewayID());
            List emails = new ArrayList();
            emails.add(ISProfile.getEmail());
            airavataUserProfile.setEmails(emails);
            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, but need to properly populate claims map
            AuthzToken authzToken = new AuthzToken("dummy_token");
            Map claimsMap = new HashMap<>();
            claimsMap.put(Constants.USER_NAME, ISProfile.getUserName());
            claimsMap.put(Constants.GATEWAY_ID, ISProfile.getGatewayID());
            authzToken.setClaimsMap(claimsMap);
            client.addUserProfile(authzToken, airavataUserProfile);
        }
        return false;
    }

    private void migrateUserProfilesToKeycloak(List Wso2ISProfileList){
        KeycloakIdentityServerClient client = getKeycloakIdentityServerClient();
        client.migrateUserStore(Wso2ISProfileList, this.gatewayId, this.keycloakTemporaryUserPassword, this.roleConversionMap);
    }

    private KeycloakIdentityServerClient getKeycloakIdentityServerClient() {
        return new KeycloakIdentityServerClient(this.keycloakServiceURL,
                    this.keycloakAdminUsername,
                    this.keycloakAdminPassword,
                    this.keycloakTrustStorePath,
                    this.keycloakTrustStorePassword);
    }

    private void loadConfigFile(String filename) {
        Properties properties = new Properties();
        try {
            properties.load(new FileInputStream(filename));
            // Load values from properties if they exist, otherwise will just use default values
            this.gatewayId = properties.getProperty(GATEWAY_ID, this.gatewayId);
            this.gatewayURL = properties.getProperty(GATEWAY_URL, this.gatewayURL);
            this.gatewayAdminUsername = properties.getProperty(GATEWAY_ADMIN_USERNAME, this.gatewayAdminUsername);
            this.gatewayAdminFirstName = properties.getProperty(GATEWAY_ADMIN_FIRST_NAME, this.gatewayAdminFirstName);
            this.gatewayAdminLastName = properties.getProperty(GATEWAY_ADMIN_LAST_NAME, this.gatewayAdminLastName);
            this.gatewayAdminEmailAddress = properties.getProperty(GATEWAY_ADMIN_EMAIL_ADDRESS, this.gatewayAdminEmailAddress);
            this.wso2ISAdminUsername = properties.getProperty(WSO2IS_ADMIN_USERNAME, this.wso2ISAdminUsername);
            this.wso2ISAdminPassword = properties.getProperty(WSO2IS_ADMIN_PASSWORD, this.wso2ISAdminPassword);
            this.airavataServiceServerHost = properties.getProperty(AIRAVATA_SERVICE_HOST, this.airavataServiceServerHost);
            this.airavataServiceServerPort = Integer.valueOf(properties.getProperty(AIRAVATA_SERVICE_PORT, Integer.toString(this.airavataServiceServerPort)));
            this.airavataServiceSecure = Boolean.valueOf(properties.getProperty(AIRAVATA_SERVICE_SECURE, "false"));
            this.profileServiceServerHost = properties.getProperty(PROFILE_SERVICE_HOST, this.profileServiceServerHost);
            this.profileServiceServerPort = Integer.valueOf(properties.getProperty(PROFILE_SERVICE_PORT, Integer.toString(this.profileServiceServerPort)));
            this.keycloakServiceURL = properties.getProperty(KEYCLOAK_SERVICE_URL, this.keycloakServiceURL);
            this.keycloakAdminUsername = properties.getProperty(KEYCLOAK_ADMIN_USERNAME, this.keycloakAdminUsername);
            this.keycloakAdminPassword = properties.getProperty(KEYCLOAK_ADMIN_PASSWORD, this.keycloakAdminPassword);
            this.keycloakTrustStorePath = properties.getProperty(KEYCLOAK_TRUSTSTORE_PATH, this.keycloakTrustStorePath);
            this.keycloakTrustStorePassword = properties.getProperty(KEYCLOAK_TRUSTSTORE_PASSWORD, this.keycloakTrustStorePassword);
            this.keycloakTemporaryUserPassword = properties.getProperty(KEYCLOAK_USER_TEMP_PASSWORD, this.keycloakTemporaryUserPassword);
            // Custom role names
            this.roleConversionMap.put(properties.getProperty(WSO2IS_ADMIN_ROLENAME, "admin"), "admin");
            this.roleConversionMap.put(properties.getProperty(WSO2IS_ADMIN_READ_ONLY_ROLENAME, "admin-read-only"), "admin-read-only");
            this.roleConversionMap.put(properties.getProperty(WSO2IS_GATEWAY_USER_ROLENAME, "gateway-user"), "gateway-user");
            this.roleConversionMap.put(properties.getProperty(WSO2IS_USER_PENDING_ROLENAME, "user-pending"), "user-pending");
            this.roleConversionMap.put(properties.getProperty(WSO2IS_GATEWAY_PROVIDER_ROLENAME, "gateway-provider"), "gateway-provider");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        MigrationManager migrationManager = new MigrationManager();
        if (args.length > 0) {
            String configFilename = args[0];
            migrationManager.loadConfigFile(configFilename);
        }
        migrationManager.setISLoginCredentials();
        List userProfileList = migrationManager.getUserProfilesFromWso2IS();
        try {
            migrationManager.migrateGatewayProfileToAiravata();
            // Must migrate profiles to Keycloak first because Profile Service will attempt to keep user profiles
            // in since with Keycloak user profiles
            migrationManager.migrateUserProfilesToKeycloak(userProfileList);
            migrationManager.migrateUserProfilesToAiravata(userProfileList);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy