org.wso2.carbon.identity.user.store.ws.WSUserStoreManager.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.carbon.identity.user.store.ws.WSUserStoreManager.java

Source

/*
*  Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
*  WSO2 Inc. 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.wso2.carbon.identity.user.store.ws;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethodBase;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.wso2.carbon.core.util.KeyStoreManager;
import org.wso2.carbon.identity.core.util.IdentityTenantUtil;
import org.wso2.carbon.identity.user.store.ws.cache.UserAttributeCache;
import org.wso2.carbon.identity.user.store.ws.cache.UserAttributeCacheEntry;
import org.wso2.carbon.identity.user.store.ws.cache.UserAttributeCacheKey;
import org.wso2.carbon.identity.user.store.ws.exception.WSUserStoreException;
import org.wso2.carbon.identity.user.store.ws.internal.WSUserStoreComponentHolder;
import org.wso2.carbon.identity.user.store.ws.security.DefaultJWTGenerator;
import org.wso2.carbon.identity.user.store.ws.security.SecurityTokenBuilder;
import org.wso2.carbon.identity.user.store.ws.util.EndpointUtil;
import org.wso2.carbon.user.api.ClaimMapping;
import org.wso2.carbon.user.api.Properties;
import org.wso2.carbon.user.api.Property;
import org.wso2.carbon.user.api.RealmConfiguration;
import org.wso2.carbon.user.core.UserCoreConstants;
import org.wso2.carbon.user.core.UserRealm;
import org.wso2.carbon.user.core.UserStoreException;
import org.wso2.carbon.user.core.claim.ClaimManager;
import org.wso2.carbon.user.core.common.AbstractUserStoreManager;
import org.wso2.carbon.user.core.common.RoleContext;
import org.wso2.carbon.user.core.profile.ProfileConfigurationManager;
import org.wso2.carbon.user.core.tenant.Tenant;
import org.wso2.carbon.user.core.util.DatabaseUtil;
import org.wso2.carbon.user.core.util.JDBCRealmUtil;
import org.wso2.carbon.user.core.util.UserCoreUtil;
import org.wso2.carbon.utils.multitenancy.MultitenantConstants;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.Key;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class WSUserStoreManager extends AbstractUserStoreManager {

    private static Log log = LogFactory.getLog(WSUserStoreManager.class);
    private static final String ENDPOINT = "EndPointURL";

    private HttpClient httpClient;
    private static Map<Integer, Key> privateKeys = new ConcurrentHashMap<>();

    public WSUserStoreManager() {

    }

    /**
     * @param realmConfig
     * @param tenantId
     * @throws UserStoreException
     */
    public WSUserStoreManager(RealmConfiguration realmConfig, int tenantId) throws UserStoreException {
        this.realmConfig = realmConfig;
        this.tenantId = tenantId;

        if (realmConfig.getUserStoreProperty(UserCoreConstants.RealmConfig.READ_GROUPS_ENABLED) != null) {
            readGroupsEnabled = Boolean.parseBoolean(
                    realmConfig.getUserStoreProperty(UserCoreConstants.RealmConfig.READ_GROUPS_ENABLED));
        }

        if (log.isDebugEnabled()) {
            if (readGroupsEnabled) {
                log.debug("ReadGroups is enabled for " + getMyDomainName());
            } else {
                log.debug("ReadGroups is disabled for " + getMyDomainName());
            }
        }

        if (realmConfig.getUserStoreProperty(UserCoreConstants.RealmConfig.WRITE_GROUPS_ENABLED) != null) {
            writeGroupsEnabled = Boolean.parseBoolean(
                    realmConfig.getUserStoreProperty(UserCoreConstants.RealmConfig.WRITE_GROUPS_ENABLED));
        } else {
            if (!isReadOnly()) {
                writeGroupsEnabled = true;
            }
        }

        if (log.isDebugEnabled()) {
            if (writeGroupsEnabled) {
                log.debug("WriteGroups is enabled for " + getMyDomainName());
            } else {
                log.debug("WriteGroups is disabled for " + getMyDomainName());
            }
        }

        if (writeGroupsEnabled) {
            readGroupsEnabled = true;
        }

        /* Initialize user roles cache as implemented in AbstractUserStoreManager */
        initUserRolesCache();
    }

    public WSUserStoreManager(org.wso2.carbon.user.api.RealmConfiguration realmConfig,
            Map<String, Object> properties, ClaimManager claimManager, ProfileConfigurationManager profileManager,
            UserRealm realm, Integer tenantId) throws UserStoreException {

        this(realmConfig, tenantId);
        this.realmConfig = realmConfig;
        this.tenantId = tenantId;
        this.userRealm = realm;
        this.httpClient = new HttpClient();

        if (log.isDebugEnabled()) {
            log.debug("Started " + System.currentTimeMillis());
        }
        this.claimManager = claimManager;
        this.userRealm = realm;

        dataSource = (DataSource) properties.get(UserCoreConstants.DATA_SOURCE);
        if (dataSource == null) {
            dataSource = DatabaseUtil.getRealmDataSource(realmConfig);
        }
        if (dataSource == null) {
            throw new UserStoreException("User Management Data Source is null");
        }

        properties.put(UserCoreConstants.DATA_SOURCE, dataSource);
        realmConfig.setUserStoreProperties(JDBCRealmUtil.getSQL(realmConfig.getUserStoreProperties()));

        this.persistDomain();
        doInitialSetup();
        if (realmConfig.isPrimary()) {
            addInitialAdminData(Boolean.parseBoolean(realmConfig.getAddAdmin()), !isInitSetupDone());
        }

        initUserRolesCache();

        if (log.isDebugEnabled()) {
            log.debug("Ended " + System.currentTimeMillis());
        }
    }

    private void addAttributesToCache(String userName, Map<String, String> attributes) {

        UserAttributeCacheKey cacheKey = new UserAttributeCacheKey(userName);
        UserAttributeCacheEntry cacheEntry = new UserAttributeCacheEntry();
        cacheEntry.setUserAttributes(attributes);
        UserAttributeCache.getInstance().addToCache(cacheKey, cacheEntry);
    }

    private UserAttributeCacheEntry getUserAttributesFromCache(String userName) {

        UserAttributeCacheKey cacheKey = new UserAttributeCacheKey(userName);
        return UserAttributeCache.getInstance().getValueFromCache(cacheKey);
    }

    protected void setAuthorizationHeader(HttpMethodBase request) throws WSUserStoreException {

        String token;
        SecurityTokenBuilder securityTokenBuilder = new DefaultJWTGenerator();
        token = securityTokenBuilder.buildSecurityToken(getTenantPrivateKey(tenantId));
        request.addRequestHeader("Authorization", "Bearer " + token);
    }

    private Key getTenantPrivateKey(int tenantId) throws WSUserStoreException {

        Key privateKey;
        String tenantDomain = IdentityTenantUtil.getTenantDomain(tenantId);

        if (!(privateKeys.containsKey(tenantId))) {
            KeyStoreManager tenantKSM = KeyStoreManager.getInstance(tenantId);

            if (!tenantDomain.equals(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME)) {
                String ksName = tenantDomain.trim().replace(".", "-");
                String jksName = ksName + ".jks";
                privateKey = tenantKSM.getPrivateKey(jksName, tenantDomain);

            } else {
                try {
                    privateKey = tenantKSM.getDefaultPrivateKey();
                } catch (Exception e) {
                    throw new WSUserStoreException("Error while obtaining private key for super tenant", e);
                }
            }
            privateKeys.put(tenantId, privateKey);
        } else {
            privateKey = privateKeys.get(tenantId);
        }
        return privateKey;
    }

    private String getHostName() {
        return this.realmConfig.getUserStoreProperty(ENDPOINT);
    }

    private StringRequestEntity getAuthenticateEntity(String userName, Object password)
            throws UnsupportedEncodingException {
        return new StringRequestEntity("{\"username\":" + userName + ",\"password\":" + password + "}",
                "application/json", "UTF-8");
    }

    public boolean doAuthenticate(String username, Object credential) throws UserStoreException {
        if (log.isDebugEnabled()) {
            log.debug("Processing authentication request for tenantId  - [" + this.tenantId + "]");
        }
        boolean authStatus = false;
        PostMethod postRequest = new PostMethod(EndpointUtil.getAuthenticateEndpoint(getHostName()));
        try {

            if (this.httpClient == null) {
                this.httpClient = new HttpClient();
            }
            setAuthorizationHeader(postRequest);
            postRequest.setRequestEntity(getAuthenticateEntity(username, credential));
            int response = httpClient.executeMethod(postRequest);
            if (response == HttpStatus.SC_OK) {
                String respStr = new String(postRequest.getResponseBody());
                JSONObject resultObj = new JSONObject(respStr);
                authStatus = (boolean) resultObj.get("authenticated");
            }

        } catch (IOException | WSUserStoreException | JSONException e) {
            log.error("Error occurred while calling backed to authenticate request for tenantId - [" + this.tenantId
                    + "]", e);
        }
        return authStatus;
    }

    @Override
    protected void doAddUser(String userName, Object credential, String[] roleList, Map<String, String> claims,
            String profileName, boolean requirePasswordChange) throws UserStoreException {
        throw new UserStoreException("UserStoreManager method not supported : #doAddUser");

    }

    @Override
    protected void doUpdateCredential(String userName, Object newCredential, Object oldCredential)
            throws UserStoreException {
        throw new UserStoreException("UserStoreManager method not supported : #doUpdateCredential");

    }

    @Override
    protected void doUpdateCredentialByAdmin(String userName, Object newCredential) throws UserStoreException {
        throw new UserStoreException("UserStoreManager method not supported : #doUpdateCredentialByAdmin");

    }

    @Override
    protected void doDeleteUser(String userName) throws UserStoreException {
        throw new UserStoreException("UserStoreManager method not supported : #doDeleteUser");

    }

    @Override
    protected void doSetUserClaimValue(String userName, String claimURI, String claimValue, String profileName)
            throws UserStoreException {
        throw new UserStoreException("UserStoreManager method not supported : #doSetUserClaimValue");

    }

    @Override
    protected void doSetUserClaimValues(String userName, Map<String, String> claims, String profileName)
            throws UserStoreException {
        throw new UserStoreException("UserStoreManager method not supported : #doSetUserClaimValues");

    }

    @Override
    protected void doDeleteUserClaimValue(String userName, String claimURI, String profileName)
            throws UserStoreException {
        throw new UserStoreException("UserStoreManager method not supported : #doDeleteUserClaimValue");

    }

    @Override
    protected void doDeleteUserClaimValues(String userName, String[] claims, String profileName)
            throws UserStoreException {
        throw new UserStoreException("UserStoreManager method not supported : #doDeleteUserClaimValues");

    }

    @Override
    protected void doUpdateUserListOfRole(String roleName, String[] deletedUsers, String[] newUsers)
            throws UserStoreException {
        throw new UserStoreException("UserStoreManager method not supported : #doUpdateUserListOfRole");

    }

    @Override
    protected void doUpdateRoleListOfUser(String userName, String[] deletedRoles, String[] newRoles)
            throws UserStoreException {
        throw new UserStoreException("UserStoreManager method not supported : #doUpdateRoleListOfUser");

    }

    public Properties getDefaultUserStoreProperties() {

        Properties properties = new Properties();
        Property endpoint = new Property(ENDPOINT, "", "Agent Server Host", null);
        Property disabled = new Property("Disabled", "false", "Disabled#Check to disable the user store", null);

        Property[] mandatoryProperties = new Property[] { endpoint };
        Property[] optionalProperties = new Property[] { disabled };

        properties.setOptionalProperties(optionalProperties);
        properties.setMandatoryProperties(mandatoryProperties);
        return properties;
    }

    private NameValuePair[] getQueryString(String paramName, String[] paramValues) {
        StringBuilder queryBuilder = new StringBuilder();

        for (String param : paramValues) {
            queryBuilder.append(",").append(param);
        }

        NameValuePair param = new NameValuePair(paramName, queryBuilder.toString().replaceFirst(",", ""));

        return new NameValuePair[] { param };
    }

    private String[] getAllClaimMapAttributes(ClaimMapping[] claimMappings) {

        List<String> mapAttributes = new ArrayList<>();
        for (ClaimMapping mapping : claimMappings) {
            mapAttributes.add(mapping.getMappedAttribute());
        }
        return mapAttributes.toArray(new String[mapAttributes.size()]);
    }

    public Map<String, String> getUserPropertyValues(String userName, String[] propertyNames, String profileName)
            throws UserStoreException {

        UserAttributeCacheEntry cacheEntry = getUserAttributesFromCache(userName);
        Map<String, String> allUserAttributes = new HashMap<>();
        Map<String, String> mapAttributes = new HashMap<>();
        if (cacheEntry == null) {
            GetMethod getMethod = new GetMethod(
                    EndpointUtil.getUserClaimRetrievalEndpoint(getHostName(), userName));
            try {
                if (this.httpClient == null) {
                    this.httpClient = new HttpClient();
                }

                ClaimManager claimManager = WSUserStoreComponentHolder.getInstance().getRealmService()
                        .getBootstrapRealm().getClaimManager();
                getMethod.setQueryString(
                        getQueryString("attributes", getAllClaimMapAttributes(claimManager.getAllClaimMappings())));
                setAuthorizationHeader(getMethod);
                int response = httpClient.executeMethod(getMethod);
                if (response == HttpStatus.SC_OK) {
                    String respStr = new String(getMethod.getResponseBody());
                    JSONObject resultObj = new JSONObject(respStr);
                    Iterator iterator = resultObj.keys();
                    while (iterator.hasNext()) {
                        String key = (String) iterator.next();
                        allUserAttributes.put(key, (String) resultObj.get(key));
                    }
                    addAttributesToCache(userName, allUserAttributes);
                }
            } catch (IOException | JSONException | WSUserStoreException e) {
                log.error("Error occurred while calling backed to authenticate request for tenantId - ["
                        + this.tenantId + "]", e);
                return Collections.EMPTY_MAP;
            } catch (org.wso2.carbon.user.api.UserStoreException e) {
                log.error("Error occurred while calling backed to authenticate request for tenantId - ["
                        + this.tenantId + "]", e);
                return Collections.EMPTY_MAP;
            }
        } else {
            allUserAttributes = cacheEntry.getUserAttributes();
        }
        for (String propertyName : propertyNames) {
            mapAttributes.put(propertyName, allUserAttributes.get(propertyName));
        }
        return mapAttributes;
    }

    //Todo: Implement doCheckExistingRole
    @Override
    protected boolean doCheckExistingRole(String roleName) throws UserStoreException {
        return true;
    }

    @Override
    protected RoleContext createRoleContext(String roleName) throws UserStoreException {
        throw new UserStoreException("UserStoreManager method not supported : #createRoleContext");
    }

    //Todo: Implement doCheckExistingUser
    @Override
    protected boolean doCheckExistingUser(String userName) throws UserStoreException {
        return true;
    }

    @Override
    protected String[] getUserListFromProperties(String property, String value, String profileName)
            throws UserStoreException {
        throw new UserStoreException("UserStoreManager method not supported : #getUserListFromProperties");
    }

    @Override
    public String[] getProfileNames(String userName) throws UserStoreException {
        throw new UserStoreException("UserStoreManager method not supported : #getProfileNames");
    }

    @Override
    public String[] getAllProfileNames() throws UserStoreException {
        throw new UserStoreException("UserStoreManager method not supported : #getAllProfileNames");
    }

    @Override
    public boolean isReadOnly() throws UserStoreException {
        if ("true".equalsIgnoreCase(
                realmConfig.getUserStoreProperty(UserCoreConstants.RealmConfig.PROPERTY_READ_ONLY))) {
            return true;
        }
        return false;
    }

    public Date getPasswordExpirationTime(String userName) throws UserStoreException {
        throw new UserStoreException("UserStoreManager method not supported : #getPasswordExpirationTime");
    }

    @Override
    public int getUserId(String username) throws UserStoreException {
        throw new UserStoreException("UserStoreManager method not supported : #getUserId");
    }

    @Override
    public int getTenantId(String username) throws UserStoreException {
        throw new UserStoreException("UserStoreManager method not supported : #getTenantId");
    }

    @Override
    public int getTenantId() throws UserStoreException {
        return this.tenantId;
    }

    @Override
    public Map<String, String> getProperties(org.wso2.carbon.user.api.Tenant tenant) throws UserStoreException {
        throw new UserStoreException("UserStoreManager method not supported : #getProperties");
    }

    @Override
    public boolean isMultipleProfilesAllowed() {
        return false;
    }

    @Override
    public void addRememberMe(String s, String s1) throws org.wso2.carbon.user.api.UserStoreException {
        throw new UserStoreException("UserStoreManager method not supported : #addRememberMe");

    }

    @Override
    public boolean isValidRememberMeToken(String s, String s1) throws org.wso2.carbon.user.api.UserStoreException {
        throw new UserStoreException("UserStoreManager method not supported : #isValidRememberMeToken");
    }

    @Override
    public Map<String, String> getProperties(Tenant tenant) throws UserStoreException {
        throw new UserStoreException("UserStoreManager method not supported : #getProperties");
    }

    @Override
    public boolean isBulkImportSupported() {
        return new Boolean(this.realmConfig.getUserStoreProperty("IsBulkImportSupported")).booleanValue();
    }

    @Override
    public RealmConfiguration getRealmConfiguration() {
        return this.realmConfig;
    }

    @Override
    protected String[] doGetSharedRoleNames(String tenantDomain, String filter, int maxItemLimit)
            throws UserStoreException {
        throw new UserStoreException("UserStoreManager method not supported : #doGetSharedRoleNames");
    }

    //Todo: Implement doGetUserListOfRole
    @Override
    protected String[] doGetUserListOfRole(String roleName, String filter) throws UserStoreException {
        return null;
    }

    public String[] doListUsers(String filter, int maxItemLimit) throws UserStoreException {

        if (log.isDebugEnabled()) {
            log.debug("Processing getListUsers request for tenantId  - [" + this.tenantId + "]");
        }
        GetMethod getMethod = new GetMethod(EndpointUtil.getUserListEndpoint(getHostName()));
        List<String> userList = new ArrayList<>();
        try {

            if (this.httpClient == null) {
                this.httpClient = new HttpClient();
            }

            getMethod.setQueryString(getQueryString("limit", new String[] { String.valueOf(maxItemLimit) }));
            setAuthorizationHeader(getMethod);
            int response = httpClient.executeMethod(getMethod);
            if (response == HttpStatus.SC_OK) {
                String respStr = new String(getMethod.getResponseBody());
                JSONObject resultObj = new JSONObject(respStr);
                JSONArray users = resultObj.getJSONArray("usernames");
                for (int i = 0; i < users.length(); i++) {
                    String user = (String) users.get(i);
                    if (!"wso2.anonymous.user".equals(user)) {
                        String domain = this.realmConfig.getUserStoreProperty("DomainName");
                        user = UserCoreUtil.addDomainToName(user, domain);
                    }
                    userList.add(user);
                }
            }

        } catch (IOException | JSONException | WSUserStoreException e) {
            log.error("Error occurred while calling backed to authenticate request for tenantId - [" + this.tenantId
                    + "]", e);
        }
        return userList.toArray(new String[userList.size()]);
    }

    @Override
    protected String[] doGetDisplayNamesForInternalRole(String[] userNames) throws UserStoreException {
        throw new UserStoreException("UserStoreManager method not supported : #doGetDisplayNamesForInternalRole");
    }

    @Override
    public boolean doCheckIsUserInRole(String userName, String roleName) throws UserStoreException {
        String[] roles = this.doGetExternalRoleListOfUser(userName, "*");
        if (roles != null) {
            String[] arr$ = roles;
            int len$ = roles.length;

            for (int i$ = 0; i$ < len$; ++i$) {
                String role = arr$[i$];
                if (role.equalsIgnoreCase(roleName)) {
                    return true;
                }
            }
        }

        return false;
    }

    public String[] doGetExternalRoleListOfUser(String userName, String filter) throws UserStoreException {

        if (log.isDebugEnabled()) {
            log.debug("Processing getRoleListOfUser request for tenantId  - [" + this.tenantId + "]");
        }
        GetMethod getMethod = new GetMethod(EndpointUtil.getUserRolesListEndpoint(getHostName(), userName));
        List<String> groupList = new ArrayList<>();
        try {

            if (this.httpClient == null) {
                this.httpClient = new HttpClient();
            }

            setAuthorizationHeader(getMethod);
            int response = httpClient.executeMethod(getMethod);
            if (response == HttpStatus.SC_OK) {
                String respStr = new String(getMethod.getResponseBody());
                JSONObject resultObj = new JSONObject(respStr);
                JSONArray users = resultObj.getJSONArray("groups");
                for (int i = 0; i < users.length(); i++) {
                    groupList.add((String) users.get(i));
                }
            }
        } catch (IOException | JSONException | WSUserStoreException e) {
            log.error("Error occurred while getting user groups for tenantId - [" + this.tenantId + "]", e);
        }
        return groupList.toArray(new String[groupList.size()]);
    }

    @Override
    protected String[] doGetSharedRoleListOfUser(String userName, String tenantDomain, String filter)
            throws UserStoreException {
        throw new UserStoreException("UserStoreManager method not supported : #doGetSharedRoleListOfUser");
    }

    @Override
    protected void doAddRole(String roleName, String[] userList, boolean shared) throws UserStoreException {
        throw new UserStoreException("UserStoreManager method not supported : #doAddRole");

    }

    @Override
    protected void doDeleteRole(String roleName) throws UserStoreException {
        throw new UserStoreException("UserStoreManager method not supported : #doDeleteRole");

    }

    @Override
    protected void doUpdateRoleName(String roleName, String newRoleName) throws UserStoreException {
        throw new UserStoreException("UserStoreManager method not supported : #doUpdateRoleName");

    }

    public String[] doGetRoleNames(String filter, int maxItemLimit) throws UserStoreException {
        if (log.isDebugEnabled()) {
            log.debug("Processing doGetRoleNames request for tenantId  - [" + this.tenantId + "]");
        }
        GetMethod getMethod = new GetMethod(EndpointUtil.getRoleListEndpoint(getHostName()));
        List<String> roleList = new ArrayList<>();
        try {
            if (this.httpClient == null) {
                this.httpClient = new HttpClient();
            }

            setAuthorizationHeader(getMethod);
            int response = httpClient.executeMethod(getMethod);
            if (response == HttpStatus.SC_OK) {
                String respStr = new String(getMethod.getResponseBody());
                JSONObject resultObj = new JSONObject(respStr);
                JSONArray groups = resultObj.getJSONArray("groups");
                String userStoreDomain = this.realmConfig.getUserStoreProperty("DomainName");
                for (int i = 0; i < groups.length(); i++) {
                    String roleName = (String) groups.get(i);
                    roleName = UserCoreUtil.addDomainToName(roleName, userStoreDomain);
                    roleList.add(roleName);
                }
            }

        } catch (IOException | JSONException | WSUserStoreException e) {
            log.error("Error occurred while get role names for tenantId - [" + this.tenantId + "]", e);
        }
        return roleList.toArray(new String[roleList.size()]);
    }

}