/**
* =============================================================================
*
* 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.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.UUID;
import javax.annotation.Resource;
import org.orcid.core.manager.ClientDetailsManager;
import org.orcid.core.manager.EncryptionManager;
import org.orcid.core.manager.OrcidProfileManager;
import org.orcid.core.manager.OrcidSSOManager;
import org.orcid.core.manager.ProfileEntityCacheManager;
import org.orcid.core.manager.ProfileEntityManager;
import org.orcid.jaxb.model.clientgroup.ClientType;
import org.orcid.jaxb.model.clientgroup.RedirectUri;
import org.orcid.jaxb.model.clientgroup.RedirectUriType;
import org.orcid.jaxb.model.message.ScopePathType;
import org.orcid.persistence.dao.ClientDetailsDao;
import org.orcid.persistence.dao.ClientRedirectDao;
import org.orcid.persistence.dao.GenericDao;
import org.orcid.persistence.dao.ProfileDao;
import org.orcid.persistence.jpa.entities.ClientDetailsEntity;
import org.orcid.persistence.jpa.entities.ClientRedirectUriEntity;
import org.orcid.persistence.jpa.entities.ClientScopeEntity;
import org.orcid.persistence.jpa.entities.ClientSecretEntity;
import org.orcid.persistence.jpa.entities.ProfileEntity;
import org.orcid.persistence.jpa.entities.keys.ClientScopePk;
import org.springframework.transaction.annotation.Transactional;
public class OrcidSSOManagerImpl implements OrcidSSOManager {
private final static String SSO_REDIRECT_URI_TYPE = RedirectUriType.SSO_AUTHENTICATION.value();
private final static ScopePathType [] PUBLIC_CLIENT_SCOPES = {ScopePathType.AUTHENTICATE, ScopePathType.READ_PUBLIC};
private final static String SSO_ROLE = "ROLE_PUBLIC";
private final static String RESOURCE_ID = "orcid";
@Resource
private ProfileEntityManager profileEntityManager;
@Resource
private EncryptionManager encryptionManager;
@Resource
private ClientDetailsManager clientDetailsManager;
@Resource
private ClientDetailsDao clientDetailsDao;
@Resource(name = "clientScopeDao")
private GenericDao<ClientScopeEntity, ClientScopePk> clientScopeDao;
@Resource
private ClientRedirectDao clientRedirectDao;
@Resource
private ProfileDao profileDao;
@Resource
private OrcidProfileManager orcidProfileManager;
@Resource(name = "profileEntityCacheManager")
ProfileEntityCacheManager profileEntityCacheManager;
@Override
@Transactional
public ClientDetailsEntity grantSSOAccess(String orcid, String name, String description, String website, Set<String> redirectUris) {
ProfileEntity profileEntity = profileEntityCacheManager.retrieve(orcid);
if (profileEntity == null) {
throw new IllegalArgumentException("ORCID does not exist for " + orcid + " cannot continue");
}
String clientId = null;
ClientDetailsEntity existingPublicClient = clientDetailsManager.getPublicClient(orcid);
// If it already have SSO client credentials, just return them
if (existingPublicClient != null) {
clientId = existingPublicClient.getId();
} else {
Set<String> clientScopes = new HashSet<>();
for(ScopePathType publicClientScope : PUBLIC_CLIENT_SCOPES) {
clientScopes.add(publicClientScope.getContent());
}
Set<String> clientResourceIds = new HashSet<>();
clientResourceIds.add(RESOURCE_ID);
Set<String> redirectUrisSet = new HashSet<String>();
for (String uri : redirectUris) {
redirectUrisSet.add(uri);
}
ClientDetailsEntity clientDetailsEntity = clientDetailsManager.createClientDetails(orcid, name, description, null, website, ClientType.PUBLIC_CLIENT, clientScopes,
clientResourceIds, getClientAuthorizedGrantTypes(), getClientRegisteredRedirectUris(redirectUrisSet), getClientGrantedAuthorities(), false);
clientId = clientDetailsEntity.getId();
}
ClientDetailsEntity clientDetailsEntity = clientDetailsManager.findByClientId(clientId);
if (clientDetailsEntity.getClientSecrets() != null) {
for (ClientSecretEntity updatedClientSecret : clientDetailsEntity.getClientSecrets()) {
updatedClientSecret.setDecryptedClientSecret(encryptionManager.decryptForInternalUse(updatedClientSecret.getClientSecret()));
}
}
return clientDetailsEntity;
}
@Override
@Transactional
public void revokeSSOAccess(String orcid) {
if(profileEntityManager.orcidExists(orcid)) {
ClientDetailsEntity publicClient = clientDetailsManager.getPublicClient(orcid);
if (publicClient != null) {
clientDetailsManager.removeByClientId(publicClient.getId());
}
}
}
@Override
public ClientDetailsEntity getUserCredentials(String orcid) {
ClientDetailsEntity existingClientDetails = clientDetailsManager.getPublicClient(orcid);
if (existingClientDetails != null) {
SortedSet<ClientRedirectUriEntity> allRedirectUris = existingClientDetails.getClientRegisteredRedirectUris();
SortedSet<ClientRedirectUriEntity> onlySSORedirectUris = new TreeSet<ClientRedirectUriEntity>();
if (allRedirectUris != null) {
for (ClientRedirectUriEntity rUri : allRedirectUris) {
// Leave only the redirect uris used for SSO authentication
if (SSO_REDIRECT_URI_TYPE.equals(rUri.getRedirectUriType())) {
onlySSORedirectUris.add(rUri);
}
}
}
existingClientDetails.setClientRegisteredRedirectUris(onlySSORedirectUris);
if (existingClientDetails.getClientSecrets() != null) {
for (ClientSecretEntity clientSecret : existingClientDetails.getClientSecrets()) {
clientSecret.setDecryptedClientSecret(encryptionManager.decryptForInternalUse(clientSecret.getClientSecret()));
}
}
}
return existingClientDetails;
}
@Override
public boolean resetClientSecret(String clientDetailsId) {
ClientDetailsEntity clientDetailsEntity = clientDetailsManager.findByClientId(clientDetailsId);
if (clientDetailsEntity == null) {
throw new IllegalArgumentException("ORCID " + clientDetailsId + " doesnt have client details assigned yet");
}
// Generate new client secret
String clientSecret = encryptionManager.encryptForInternalUse(UUID.randomUUID().toString());
return clientDetailsManager.resetClientSecret(clientDetailsEntity.getClientId(), clientSecret);
}
private Set<RedirectUri> getClientRegisteredRedirectUris(Set<String> redirectUris) {
Set<RedirectUri> clientRedirectUris = new HashSet<>();
for (String redirectUri : redirectUris) {
RedirectUri clientRedirectUriEntity = populateClientRedirectUri(redirectUri);
clientRedirectUris.add(clientRedirectUriEntity);
}
return clientRedirectUris;
}
private RedirectUri populateClientRedirectUri(String redirectUri) {
RedirectUri clientRedirectUri = new RedirectUri();
clientRedirectUri.setValue(redirectUri);
clientRedirectUri.setType(RedirectUriType.SSO_AUTHENTICATION);
return clientRedirectUri;
}
private ClientRedirectUriEntity populateClientRedirectUriEntity(String redirectUri, ClientDetailsEntity clientDetailsEntity) {
ClientRedirectUriEntity clientRedirectUriEntity = new ClientRedirectUriEntity();
clientRedirectUriEntity.setClientDetailsEntity(clientDetailsEntity);
clientRedirectUriEntity.setRedirectUri(redirectUri);
clientRedirectUriEntity.setRedirectUriType(SSO_REDIRECT_URI_TYPE);
return clientRedirectUriEntity;
}
private List<String> getClientGrantedAuthorities() {
List<String> clientGrantedAuthorities = new ArrayList<>();
clientGrantedAuthorities.add(SSO_ROLE);
return clientGrantedAuthorities;
}
private Set<String> getClientAuthorizedGrantTypes() {
Set<String> clientAuthorizedGrantTypes = new HashSet<>();
clientAuthorizedGrantTypes.add("authorization_code");
clientAuthorizedGrantTypes.add("client_credentials");
clientAuthorizedGrantTypes.add("refresh_token");
return clientAuthorizedGrantTypes;
}
@Override
@Transactional
public ClientDetailsEntity updateUserCredentials(String orcid, String name, String description, String website, Set<String> redirectUris) {
ProfileEntity profileEntity = profileEntityCacheManager.retrieve(orcid);
if (profileEntity == null) {
throw new IllegalArgumentException("ORCID does not exist for " + orcid + " cannot continue");
} else {
ClientDetailsEntity existingPublicClient = clientDetailsManager.getPublicClient(orcid);
if (existingPublicClient != null) {
// Set the decrypted secret
existingPublicClient.setDecryptedClientSecret(encryptionManager.decryptForInternalUse(existingPublicClient.getClientSecretForJpa()));
// Update the name
existingPublicClient.setClientName(name);
// Update the description
existingPublicClient.setClientDescription(description);
// Update the website if needed
existingPublicClient.setClientWebsite(website);
// Get the existing redirect uris
SortedSet<ClientRedirectUriEntity> clientRedirectUriEntities = existingPublicClient.getClientRegisteredRedirectUris();
// Create a set with the redirect uris that are not SSO and the
// ones that wasnt modified
Set<ClientRedirectUriEntity> redirectUrisToAdd = new HashSet<ClientRedirectUriEntity>();
for (ClientRedirectUriEntity existingEntity : clientRedirectUriEntities) {
// Add to the set all non SSO redirect uris
if (!SSO_REDIRECT_URI_TYPE.equals(existingEntity.getRedirectUriType())) {
redirectUrisToAdd.add(existingEntity);
} else {
// If the redirect uri exists and also comes in the new
// set of redirect uris, leave it
if (redirectUris.contains(existingEntity.getRedirectUri())) {
redirectUrisToAdd.add(existingEntity);
}
}
}
Map<String, ClientRedirectUriEntity> existingClientRedirectUriEntitiesMap = ClientRedirectUriEntity.mapByUri(redirectUrisToAdd);
// Now we need to check which redirect uris are new, in order to
// add them
for (String redirectUri : redirectUris) {
if (!existingClientRedirectUriEntitiesMap.containsKey(redirectUri)) {
// Add the new key
ClientRedirectUriEntity newRedirectUri = populateClientRedirectUriEntity(redirectUri, existingPublicClient);
redirectUrisToAdd.add(newRedirectUri);
}
}
// Clear the set for orphan removal
clientRedirectUriEntities.clear();
// Fill the collection with the redirect uris that should be
// kept
clientRedirectUriEntities.addAll(redirectUrisToAdd);
existingPublicClient = clientDetailsManager.merge(existingPublicClient);
if (existingPublicClient.getClientSecrets() != null) {
for (ClientSecretEntity updatedClientSecret : existingPublicClient.getClientSecrets()) {
updatedClientSecret.setDecryptedClientSecret(encryptionManager.decryptForInternalUse(updatedClientSecret.getClientSecret()));
}
}
return existingPublicClient;
}
}
return null;
}
}