eu.trentorise.smartcampus.permissionprovider.manager.ClientDetailsManager.java Source code

Java tutorial

Introduction

Here is the source code for eu.trentorise.smartcampus.permissionprovider.manager.ClientDetailsManager.java

Source

/**
 *    Copyright 2012-2013 Trento RISE
 *
 *    Licensed 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 eu.trentorise.smartcampus.permissionprovider.manager;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;

import javax.persistence.EntityNotFoundException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import eu.trentorise.smartcampus.permissionprovider.common.Utils;
import eu.trentorise.smartcampus.permissionprovider.jaxbmodel.AuthorityMapping;
import eu.trentorise.smartcampus.permissionprovider.model.ClientAppBasic;
import eu.trentorise.smartcampus.permissionprovider.model.ClientAppInfo;
import eu.trentorise.smartcampus.permissionprovider.model.ClientDetailsEntity;
import eu.trentorise.smartcampus.permissionprovider.repository.ClientDetailsRepository;
import eu.trentorise.smartcampus.permissionprovider.repository.ResourceParameterRepository;
import eu.trentorise.smartcampus.permissionprovider.repository.ResourceRepository;

/**
 * Support for the management of client app registration details
 * @author raman
 *
 */
@Component
@Transactional
public class ClientDetailsManager {

    /** GRANT TYPE: CLIENT CRIDENTIALS FLOW */
    private static final String GT_CLIENT_CREDENTIALS = "client_credentials";
    /** GRANT TYPE: IMPLICIT FLOW */
    private static final String GT_IMPLICIT = "implicit";
    /** GRANT TYPE: AUTHORIZATION GRANT FLOW */
    private static final String GT_AUTHORIZATION_CODE = "authorization_code";
    /** GRANT TYPE: REFRESH TOKEN */
    private static final String GT_REFRESH_TOKEN = "refresh_token";
    private Log log = LogFactory.getLog(getClass());

    @Autowired
    private ClientDetailsRepository clientDetailsRepository;
    @Autowired
    private ResourceRepository resourceRepository;
    @Autowired
    private ResourceParameterRepository resourceParameterRepository;
    @Autowired
    private AttributesAdapter attributesAdapter;

    /**
     * Generate new value to be used as clientId (String)
     * @return
     */
    public synchronized String generateClientId() {
        return UUID.randomUUID().toString();
    }

    /**
     * Generate new value to be used as client secret (String)
     * @return
     */
    public synchronized String generateClientSecret() {
        return UUID.randomUUID().toString();
    }

    /**
     * Convert DB objects to the simplified client representation
     * @param entities
     * @return
     */
    private List<ClientAppBasic> convertToClientApps(List<ClientDetailsEntity> entities) {
        if (entities == null) {
            return Collections.emptyList();
        }
        List<ClientAppBasic> res = new ArrayList<ClientAppBasic>();
        for (ClientDetailsEntity e : entities) {
            res.add(convertToClientApp(e));
        }
        return res;
    }

    /**
     * Convert DB object to the simplified client representation
     * @param e
     * @return
     */
    private ClientAppBasic convertToClientApp(ClientDetailsEntity e) {
        ClientAppBasic res = new ClientAppBasic();
        res.setClientId(e.getClientId());
        res.setClientSecret(e.getClientSecret());
        res.setClientSecretMobile(e.getClientSecretMobile());
        res.setGrantedTypes(e.getAuthorizedGrantTypes());

        // approval status
        res.setIdentityProviderApproval(new HashMap<String, Boolean>());
        // request status
        res.setIdentityProviders(new HashMap<String, Boolean>());
        for (String key : attributesAdapter.getAuthorityUrls().keySet()) {
            res.getIdentityProviders().put(key, false);
        }

        ClientAppInfo info = ClientAppInfo.convert(e.getAdditionalInformation());
        if (info != null) {
            res.setName(info.getName());
            res.setNativeAppsAccess(info.isNativeAppsAccess());
            res.setNativeAppSignatures(info.getNativeAppSignatures());
            if (info.getIdentityProviders() != null) {
                for (String key : info.getIdentityProviders().keySet()) {
                    switch (info.getIdentityProviders().get(key)) {
                    case ClientAppInfo.APPROVED:
                        res.getIdentityProviderApproval().put(key, true);
                        res.getIdentityProviders().put(key, true);
                        break;
                    case ClientAppInfo.REJECTED:
                        res.getIdentityProviderApproval().put(key, false);
                        res.getIdentityProviders().put(key, true);
                        break;
                    case ClientAppInfo.REQUESTED:
                        res.getIdentityProviders().put(key, true);
                        break;
                    default:
                        break;
                    }
                }
            }
        }
        // access server-side corresponds to the 'authorization grant' flow.
        res.setServerSideAccess(e.getAuthorizedGrantTypes().contains(GT_AUTHORIZATION_CODE));
        // browser access corresponds to the 'implicit' flow.
        res.setBrowserAccess(e.getAuthorizedGrantTypes().contains(GT_IMPLICIT));

        res.setRedirectUris(StringUtils.collectionToCommaDelimitedString(e.getRegisteredRedirectUri()));
        return res;
    }

    /**
     * Client types to be associated with client app by default
     * @return
     */
    public String defaultGrantTypes() {
        return GT_CLIENT_CREDENTIALS;
    }

    /**
     * Client authorities to be associated with client app by default
     * @return
     */
    public String defaultAuthorities() {
        return "ROLE_CLIENT";
    }

    /**
     * Fill in the DB object with the properties of {@link ClientAppBasic} instance. In case of problem, return null.
     * @param client
     * @param data
     * @return
     * @throws Exception 
     */
    public ClientDetailsEntity convertFromClientApp(ClientDetailsEntity client, ClientAppBasic data) {
        try {
            ClientAppInfo info = null;
            if (client.getAdditionalInformation() == null) {
                info = new ClientAppInfo();
            } else {
                info = ClientAppInfo.convert(client.getAdditionalInformation());
            }
            info.setName(data.getName());
            info.setNativeAppsAccess(data.isNativeAppsAccess());
            info.setNativeAppSignatures(Utils.normalizeValues(data.getNativeAppSignatures()));
            Set<String> types = new HashSet<String>(client.getAuthorizedGrantTypes());
            if (data.isBrowserAccess()) {
                types.add(GT_IMPLICIT);
            } else {
                types.remove(GT_IMPLICIT);
            }
            if (data.isServerSideAccess() || data.isNativeAppsAccess()) {
                types.add(GT_AUTHORIZATION_CODE);
                types.add(GT_REFRESH_TOKEN);
            } else {
                types.remove(GT_AUTHORIZATION_CODE);
                types.remove(GT_REFRESH_TOKEN);
            }
            client.setAuthorizedGrantTypes(StringUtils.collectionToCommaDelimitedString(types));
            if (info.getIdentityProviders() == null) {
                info.setIdentityProviders(new HashMap<String, Integer>());
            }

            for (String key : attributesAdapter.getAuthorityUrls().keySet()) {
                if (data.getIdentityProviders().get(key)) {
                    Integer value = info.getIdentityProviders().get(key);
                    AuthorityMapping a = attributesAdapter.getAuthority(key);
                    if (value == null || value == ClientAppInfo.UNKNOWN) {
                        info.getIdentityProviders().put(key,
                                a.isPublic() ? ClientAppInfo.APPROVED : ClientAppInfo.REQUESTED);
                    }
                } else {
                    info.getIdentityProviders().remove(key);
                }
            }

            client.setAdditionalInformation(info.toJson());
            client.setRedirectUri(Utils.normalizeValues(data.getRedirectUris()));
        } catch (Exception e) {
            log.error("failed to convert an object: " + e.getMessage(), e);
            return null;
        }
        return client;
    }

    /**
     * Validate correctness of the data specified for the app
     * @param client
     * @param data
     */
    public String validate(ClientDetailsEntity client, ClientAppBasic data) {
        if (client == null)
            return "app not found";
        // name should not be empty
        if (data.getName() == null || data.getName().trim().isEmpty()) {
            return "name cannot be empty";
        }
        // for server-side or native access redirect URLs are required
        if ((data.isServerSideAccess() || data.isNativeAppsAccess())
                && (data.getRedirectUris() == null || data.getRedirectUris().trim().isEmpty())) {
            return "redirect URL is required for Server-side or native access";
        }
        //      if (data.isNativeAppsAccess() && (data.getNativeAppSignatures() == null || data.getNativeAppSignatures().isEmpty())) {
        //         return "app signature is required for native access";
        //      }
        return null;
    }

    /**
     * Reset clientSecretMobile
     * @param clientId
     * @return updated {@link ClientDetailsEntity} instance
     */
    public ClientAppBasic resetClientSecretMobile(String clientId) {
        return convertToClientApp(resetClientData(clientId, true));
    }

    /**
     * Reset client secret
     * @param clientId
     * @return updated {@link ClientDetailsEntity} instance
     */
    public ClientAppBasic resetClientSecret(String clientId) {
        return convertToClientApp(resetClientData(clientId, false));
    }

    public ClientDetailsEntity resetClientData(String clientId, boolean resetClientSecretMobile) {
        ClientDetailsEntity client = clientDetailsRepository.findByClientId(clientId);
        if (client == null) {
            throw new IllegalArgumentException("client app not found");
        }
        if (resetClientSecretMobile) {
            client.setClientSecretMobile(generateClientSecret());
        } else {
            client.setClientSecret(generateClientSecret());
        }
        clientDetailsRepository.save(client);
        return client;
    }

    /**
     * Return the set of identity providers allowed for the client
     * @param clientId
     * @return set of identity providers IDs (authorities)
     */
    public Set<String> getIdentityProviders(String clientId) {
        ClientDetailsEntity entity = clientDetailsRepository.findByClientId(clientId);
        if (entity == null)
            throw new IllegalArgumentException("client not found");
        ClientAppInfo info = ClientAppInfo.convert(entity.getAdditionalInformation());
        Set<String> res = new HashSet<String>();
        if (info.getIdentityProviders() != null) {
            for (String s : info.getIdentityProviders().keySet()) {
                if (ClientAppInfo.APPROVED == info.getIdentityProviders().get(s)) {
                    res.add(s);
                }
            }
        }
        return res;
    }

    /**
     * @param userId
     * @return {@link List} of {@link ClientAppBasic} objects representing client apps
     */
    public List<ClientAppBasic> getByDeveloperId(Long userId) {
        return convertToClientApps(clientDetailsRepository.findByDeveloperId(userId));
    }

    /**
     * Create new Client from {@link ClientAppBasic} descriptor
     * @param appData
     * @param userId
     * @return {@link ClientAppBasic} descriptor of the created Client
     * @throws Exception 
     */
    public ClientAppBasic create(ClientAppBasic appData, Long userId) throws Exception {
        ClientDetailsEntity entity = new ClientDetailsEntity();
        ClientAppInfo info = new ClientAppInfo();
        if (!StringUtils.hasText(appData.getName())) {
            throw new IllegalArgumentException("An app name cannot be empty");
        }
        info.setName(appData.getName());
        for (ClientDetailsEntity cde : clientDetailsRepository.findAll()) {
            if (ClientAppInfo.convert(cde.getAdditionalInformation()).getName().equals(appData.getName())) {
                throw new IllegalArgumentException("An app with the same name already exists");
            }
        }
        entity.setAdditionalInformation(info.toJson());
        entity.setClientId(generateClientId());
        entity.setAuthorities(defaultAuthorities());
        entity.setAuthorizedGrantTypes(defaultGrantTypes());
        entity.setDeveloperId(userId);
        entity.setClientSecret(generateClientSecret());
        entity.setClientSecretMobile(generateClientSecret());

        entity = clientDetailsRepository.save(entity);
        return convertToClientApp(entity);

    }

    /**
     * delete the specified client
     * @param clientId
     * @return {@link ClientAppBasic} descriptor of the deleted client
     */
    public ClientAppBasic delete(String clientId) {
        ClientDetailsEntity client = clientDetailsRepository.findByClientId(clientId);
        if (client == null) {
            throw new EntityNotFoundException("client app not found");
        }
        clientDetailsRepository.delete(client);
        return convertToClientApp(client);
    }

    /**
     * Update client info
     * @param clientId
     * @param data
     * @return 
     */
    public ClientAppBasic update(String clientId, ClientAppBasic data) {
        ClientDetailsEntity client = clientDetailsRepository.findByClientId(clientId);
        String error = null;
        if ((error = validate(client, data)) != null) {
            throw new IllegalArgumentException(error);
        }
        client = convertFromClientApp(client, data);
        if (client != null) {
            clientDetailsRepository.save(client);
            return convertToClientApp(client);
        } else {
            log.error("Problem converting the client");
            throw new IllegalArgumentException("internal error");
        }

    }
}