/** * ============================================================================= * * ORCID (R) Open Source * http://orcid.org * * Copyright (c) 2012-2014 ORCID, Inc. * Licensed under an MIT-Style License (MIT) * http://orcid.org/open-source-license * * This copyright and license information (including a link to the full license) * shall be included in its entirety in all copies or substantial portion of * the software. * * ============================================================================= */ package org.orcid.core.manager.impl; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.UUID; import javax.annotation.Resource; import org.apache.commons.lang.time.DateUtils; import org.orcid.core.manager.AppIdGenerationManager; import org.orcid.core.manager.ClientDetailsManager; import org.orcid.core.manager.EncryptionManager; import org.orcid.core.manager.ProfileEntityManager; import org.orcid.core.manager.SourceNameCacheManager; import org.orcid.core.manager.read_only.impl.ClientDetailsManagerReadOnlyImpl; import org.orcid.jaxb.model.clientgroup.ClientType; import org.orcid.jaxb.model.clientgroup.RedirectUri; import org.orcid.jaxb.model.message.ScopePathType; import org.orcid.persistence.dao.ClientDetailsDao; import org.orcid.persistence.dao.ClientRedirectDao; import org.orcid.persistence.dao.ClientSecretDao; import org.orcid.persistence.jpa.entities.ClientAuthorisedGrantTypeEntity; import org.orcid.persistence.jpa.entities.ClientDetailsEntity; import org.orcid.persistence.jpa.entities.ClientGrantedAuthorityEntity; import org.orcid.persistence.jpa.entities.ClientRedirectUriEntity; import org.orcid.persistence.jpa.entities.ClientResourceIdEntity; import org.orcid.persistence.jpa.entities.ClientScopeEntity; import org.orcid.persistence.jpa.entities.ClientSecretEntity; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.transaction.annotation.Transactional; public class ClientDetailsManagerImpl extends ClientDetailsManagerReadOnlyImpl implements ClientDetailsManager { private static final Logger LOGGER = LoggerFactory.getLogger(ClientDetailsManagerImpl.class); @Resource private ClientDetailsDao clientDetailsDao; @Resource private ClientDetailsDao clientDetailsDaoReadOnly; @Resource private ClientSecretDao clientSecretDao; @Resource private ClientRedirectDao clientRedirectDao; @Resource private EncryptionManager encryptionManager; @Resource private AppIdGenerationManager appIdGenerationManager; @Resource private SourceNameCacheManager sourceNameCacheManager; @Resource private ProfileEntityManager profileEntityManager; /** * Creates a new client without any knowledge of the client id or secret. * This can be called to create an initial client without the caller having * any knowledge of the clientId, and, or the client secret. This will then * be randomly generated and returned to the caller as part of the * {@link ClientDetailsEntity} * * @param name * The client name * @param description * The client description * @param website * The client website * @param clientScopes * the scopes that this client can request * @param clientResourceIds * the resource ids that this client has access to * @param clientAuthorizedGrantTypes * the grant types that this client has been granted. Clients * will commonly be granted "client_credentials" and * "authorization_code" * @param clientRegisteredRedirectUris * The redirect URIs that can be legally requested by the client. * @param clientGrantedAuthorities * the authorities that can be used to. These are likely to be * only "ROLE_CLIENT" * @param allowAutoDeprecate * Indicates if the client will enable auto deprecating unclaimed records. * @return */ @Override public ClientDetailsEntity createClientDetails(String memberId, String name, String description, String idp, String website, ClientType clientType, Set<String> clientScopes, Set<String> clientResourceIds, Set<String> clientAuthorizedGrantTypes, Set<RedirectUri> clientRegisteredRedirectUris, List<String> clientGrantedAuthorities, Boolean allowAutoDeprecate) { if (!profileEntityManager.orcidExists(memberId)) { throw new IllegalArgumentException("ORCID does not exist for " + memberId + " cannot continue"); } else { String clientSecret = encryptionManager.encryptForInternalUse(UUID.randomUUID().toString()); String clientId = appIdGenerationManager.createNewAppId(); return populateClientDetailsEntity(clientId, memberId, name, description, idp, website, clientSecret, clientType, clientScopes, clientResourceIds, clientAuthorizedGrantTypes, clientRegisteredRedirectUris, clientGrantedAuthorities, allowAutoDeprecate); } } @Override public void addClientRedirectUri(String clientId, String uri) { clientRedirectDao.addClientRedirectUri(clientId, uri); clientDetailsDao.updateLastModified(clientId); } private Set<ClientScopeEntity> getClientScopeEntities(Set<String> clientScopeStrings, ClientDetailsEntity clientDetailsEntity) { Set<ClientScopeEntity> clientScopeEntities = new HashSet<ClientScopeEntity>(clientScopeStrings.size()); for (String clientScope : clientScopeStrings) { ClientScopeEntity clientScopeEntity = new ClientScopeEntity(); clientScopeEntity.setClientDetailsEntity(clientDetailsEntity); clientScopeEntity.setScopeType(clientScope); clientScopeEntities.add(clientScopeEntity); } return clientScopeEntities; } private Set<ClientResourceIdEntity> getClientResourceIds(Set<String> clientResourceIds, ClientDetailsEntity clientDetailsEntity) { Set<ClientResourceIdEntity> clientResourceIdEntities = new HashSet<ClientResourceIdEntity>(clientResourceIds.size()); for (String clientResourceId : clientResourceIds) { ClientResourceIdEntity clientResourceIdEntity = new ClientResourceIdEntity(); clientResourceIdEntity.setClientDetailsEntity(clientDetailsEntity); clientResourceIdEntity.setResourceId(clientResourceId); clientResourceIdEntities.add(clientResourceIdEntity); } return clientResourceIdEntities; } private List<ClientGrantedAuthorityEntity> getClientGrantedAuthorities(List<String> clientGrantedAuthorities, ClientDetailsEntity clientDetailsEntity) { List<ClientGrantedAuthorityEntity> clientGrantedAuthorityEntities = new ArrayList<ClientGrantedAuthorityEntity>(clientGrantedAuthorities.size()); for (String clientGrantedAuthority : clientGrantedAuthorities) { ClientGrantedAuthorityEntity clientGrantedAuthorityEntity = new ClientGrantedAuthorityEntity(); clientGrantedAuthorityEntity.setClientDetailsEntity(clientDetailsEntity); clientGrantedAuthorityEntity.setAuthority(clientGrantedAuthority); clientGrantedAuthorityEntities.add(clientGrantedAuthorityEntity); } return clientGrantedAuthorityEntities; } private SortedSet<ClientRedirectUriEntity> getClientRegisteredRedirectUris(Set<RedirectUri> clientRegisteredRedirectUris, ClientDetailsEntity clientDetailsEntity) { SortedSet<ClientRedirectUriEntity> clientRedirectUriEntities = new TreeSet<ClientRedirectUriEntity>(); for (RedirectUri clientRegisteredRedirectUri : clientRegisteredRedirectUris) { ClientRedirectUriEntity clientRedirectUriEntity = new ClientRedirectUriEntity(); clientRedirectUriEntity.setClientDetailsEntity(clientDetailsEntity); clientRedirectUriEntity.setRedirectUri(clientRegisteredRedirectUri.getValue()); clientRedirectUriEntity.setRedirectUriType(clientRegisteredRedirectUri.getType().value()); List<ScopePathType> scopesForRedirect = clientRegisteredRedirectUri.getScope(); String clientPredefinedScopes = scopesForRedirect != null ? ScopePathType.getScopesAsSingleString(scopesForRedirect) : null; clientRedirectUriEntity.setPredefinedClientScope(clientPredefinedScopes); clientRedirectUriEntity.setUriActType(clientRegisteredRedirectUri.getActType()); clientRedirectUriEntity.setUriGeoArea(clientRegisteredRedirectUri.getGeoArea()); clientRedirectUriEntities.add(clientRedirectUriEntity); } return clientRedirectUriEntities; } private Set<ClientAuthorisedGrantTypeEntity> getClientAuthorizedGrantTypes(Set<String> clientAuthorizedGrantTypes, ClientDetailsEntity clientDetailsEntity) { Set<ClientAuthorisedGrantTypeEntity> clientAuthorisedGrantTypeEntities = new HashSet<ClientAuthorisedGrantTypeEntity>(clientAuthorizedGrantTypes.size()); for (String clientAuthorisedGrantType : clientAuthorizedGrantTypes) { ClientAuthorisedGrantTypeEntity grantTypeEntity = new ClientAuthorisedGrantTypeEntity(); grantTypeEntity.setClientDetailsEntity(clientDetailsEntity); grantTypeEntity.setGrantType(clientAuthorisedGrantType); clientAuthorisedGrantTypeEntities.add(grantTypeEntity); } return clientAuthorisedGrantTypeEntities; } @Override public ClientDetailsEntity populateClientDetailsEntity(String clientId, String memberId, String name, String description, String idp, String website, String clientSecret, ClientType clientType, Set<String> clientScopes, Set<String> clientResourceIds, Set<String> clientAuthorizedGrantTypes, Set<RedirectUri> clientRegisteredRedirectUris, List<String> clientGrantedAuthorities, Boolean allowAutoDeprecate) { ClientDetailsEntity clientDetailsEntity = new ClientDetailsEntity(); clientDetailsEntity.setId(clientId); clientDetailsEntity.setClientType(clientType); clientDetailsEntity.setClientName(name); clientDetailsEntity.setClientDescription(description); clientDetailsEntity.setAuthenticationProviderId(idp); clientDetailsEntity.setClientWebsite(website); clientDetailsEntity.setClientSecretForJpa(clientSecret, true); clientDetailsEntity.setDecryptedClientSecret(encryptionManager.decryptForInternalUse(clientSecret)); clientDetailsEntity.setClientScopes(getClientScopeEntities(clientScopes, clientDetailsEntity)); clientDetailsEntity.setClientResourceIds(getClientResourceIds(clientResourceIds, clientDetailsEntity)); clientDetailsEntity.setClientAuthorizedGrantTypes(getClientAuthorizedGrantTypes(clientAuthorizedGrantTypes, clientDetailsEntity)); clientDetailsEntity.setClientRegisteredRedirectUris(getClientRegisteredRedirectUris(clientRegisteredRedirectUris, clientDetailsEntity)); clientDetailsEntity.setPersistentTokensEnabled(true); clientDetailsEntity.setClientGrantedAuthorities(getClientGrantedAuthorities(clientGrantedAuthorities, clientDetailsEntity)); clientDetailsEntity.setGroupProfileId(memberId); clientDetailsEntity.setAllowAutoDeprecate(allowAutoDeprecate == null ? false : allowAutoDeprecate); clientDetailsDao.persist(clientDetailsEntity); return clientDetailsEntity; } @Override public void removeByClientId(String clientId) { clientDetailsDao.remove(clientId); } @Override public ClientDetailsEntity merge(ClientDetailsEntity clientDetails) { ClientDetailsEntity result = clientDetailsDao.merge(clientDetails); clientDetailsDao.updateLastModified(result.getId()); // Evict the name in the source name manager sourceNameCacheManager.remove(result.getId()); return result; } @Override public void updateLastModified(String clientId) { clientDetailsDao.updateLastModified(clientId); } /** * Set a new client secret for the specific client and set the other keys as * non primaries * * @param clientId * @param clientSecret * @return true if the new key has been added * */ @Override @Transactional public boolean resetClientSecret(String clientId, String clientSecret) { // #1 Set all existing client secrets as non primary clientSecretDao.revokeAllKeys(clientId); // #2 Create the new client secret as primary boolean result = clientSecretDao.createClientSecret(clientId, clientSecret); // #3 if it was created, update the last modified for the client details if (result) clientDetailsDao.updateLastModified(clientId); return result; } /** * Removes all non primary client secret keys * * @param clientId * */ @Override @Transactional public void cleanOldClientKeys() { LOGGER.info("Starting cron to delete non primary client keys"); Date currentDate = new Date(); List<ClientDetailsEntity> allClientDetails = this.getAll(); if (allClientDetails != null && allClientDetails != null) { for (ClientDetailsEntity clientDetails : allClientDetails) { String clientId = clientDetails.getClientId(); LOGGER.info("Deleting non primary keys for client: {}", clientId); Set<ClientSecretEntity> clientSecrets = clientDetails.getClientSecrets(); for (ClientSecretEntity clientSecret : clientSecrets) { if (!clientSecret.isPrimary()) { Date dateRevoked = clientSecret.getLastModified(); Date timeToDeleteMe = DateUtils.addHours(dateRevoked, 24); // If the key have been revokend more than 24 hours ago if (timeToDeleteMe.before(currentDate)) { LOGGER.info("Deleting key for client {}", clientId); clientSecretDao.removeClientSecret(clientId, clientSecret.getClientSecret()); } } } } } LOGGER.info("Cron done"); } @Override public List<ClientDetailsEntity> getAll() { return clientDetailsDaoReadOnly.getAll(); } @Override public boolean exists(String clientId) { return clientDetailsDao.exists(clientId); } /** * Verifies if a client belongs to the given group id * * @param clientId * @param groupId * @return true if clientId belongs to groupId * */ @Override public boolean belongsTo(String clientId, String groupId) { return clientDetailsDao.belongsTo(clientId, groupId); } /** * Fetch all clients that belongs to a group * * @param groupId * Group id * @return A list containing all clients that belongs to the given group * */ @Override public List<ClientDetailsEntity> findByGroupId(String groupId) { return clientDetailsDao.findByGroupId(groupId); } /** * Get the public profile that belongs to the given orcid ID * * @param ownerId * The user or group id * @return the public client that belongs to the given user * */ @Override public ClientDetailsEntity getPublicClient(String ownerId) { return clientDetailsDao.getPublicClient(ownerId); } /** * Get member name * * @param clientId * The client id * @return the name of the member owner of the given client * */ @Override public String getMemberName(String clientId) { return clientDetailsDao.getMemberName(clientId); } @Override public Date getLastModified(String clientId) { return clientDetailsDao.getLastModified(clientId); } @Override public Date getLastModifiedByIdp(String idp) { try { return clientDetailsDao.getLastModifiedByIdP(idp); } catch(Exception e) { LOGGER.warn("There is no client with the IdP: " + idp); } return null; } @Override public ClientDetailsEntity findByIdP(String idp) { try { ClientDetailsEntity result = clientDetailsDao.findByIdP(idp); return result; } catch(Exception e) { LOGGER.warn("There is no client with the IdP: " + idp); } return null; } }