From 13a7bfb519c0936c88fc2485cc0a25197af6e716 Mon Sep 17 00:00:00 2001 From: amontenegro Date: Tue, 3 Mar 2026 16:16:41 -0600 Subject: [PATCH 01/16] Initial set of changes, nothing works --- orcid-api-web/pom.xml | 6 +- orcid-core/pom.xml | 4 - .../oauth/OrcidOauth2TokenDetailService.java | 83 --- .../service/OrcidAuthorizationEndpoint.java | 173 ------ .../service/OrcidOAuth2RequestValidator.java | 118 ---- .../OrcidOauth2TokenDetailServiceImpl.java | 281 --------- .../core/oauth/service/OrcidTokenStore.java | 30 - .../service/OrcidTokenStoreServiceImpl.java | 560 ------------------ .../src/main/resources/orcid-core-context.xml | 46 +- orcid-persistence/pom.xml | 6 +- .../jpa/entities/ClientDetailsEntity.java | 32 +- orcid-pub-web/pom.xml | 4 - orcid-web/pom.xml | 4 - .../web/controllers/OpenIDController.java | 7 +- .../resources/orcid-core-context-spam.xml | 32 +- pom.xml | 7 - 16 files changed, 13 insertions(+), 1380 deletions(-) delete mode 100644 orcid-core/src/main/java/org/orcid/core/oauth/OrcidOauth2TokenDetailService.java delete mode 100644 orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidAuthorizationEndpoint.java delete mode 100644 orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidOAuth2RequestValidator.java delete mode 100644 orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidOauth2TokenDetailServiceImpl.java delete mode 100644 orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidTokenStore.java delete mode 100644 orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidTokenStoreServiceImpl.java diff --git a/orcid-api-web/pom.xml b/orcid-api-web/pom.xml index 5a3cdfcf8f0..7f0c9be7db7 100644 --- a/orcid-api-web/pom.xml +++ b/orcid-api-web/pom.xml @@ -38,11 +38,7 @@ org.springframework.security spring-security-web - - org.springframework.security.oauth - spring-security-oauth2 - - + ${project.groupId} diff --git a/orcid-core/pom.xml b/orcid-core/pom.xml index c5163a6ac6a..2971359b4ce 100644 --- a/orcid-core/pom.xml +++ b/orcid-core/pom.xml @@ -79,10 +79,6 @@ org.springframework spring-jdbc - - org.springframework.security.oauth - spring-security-oauth2 - org.freemarker freemarker diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/OrcidOauth2TokenDetailService.java b/orcid-core/src/main/java/org/orcid/core/oauth/OrcidOauth2TokenDetailService.java deleted file mode 100644 index a26fbe676c5..00000000000 --- a/orcid-core/src/main/java/org/orcid/core/oauth/OrcidOauth2TokenDetailService.java +++ /dev/null @@ -1,83 +0,0 @@ -package org.orcid.core.oauth; - -import java.util.List; -import java.util.Set; - -import org.orcid.core.constants.RevokeReason; -import org.orcid.persistence.dao.OrcidOauth2TokenDetailDao; -import org.orcid.persistence.jpa.entities.OrcidOauth2TokenDetail; - -/** - * @author Declan Newman (declan) Date: 19/04/2012 - */ -public interface OrcidOauth2TokenDetailService { - - void setOrcidOauth2TokenDetailDao(OrcidOauth2TokenDetailDao orcidOauth2TokenDetailDao); - - OrcidOauth2TokenDetail findNonDisabledByTokenValue(String token); - - OrcidOauth2TokenDetail findIgnoringDisabledByTokenValue(String token); - - OrcidOauth2TokenDetail findByRefreshTokenValue(String refreshTokenValue); - - void removeByRefreshTokenValue(String refreshToken); - - List findByAuthenticationKey(String authKey); - - List findByUserName(String userName); - - List findByClientId(String clientId); - - List findByClientIdAndUserName(String clientId, String userName); - - boolean doesClientKnowUser(String clientId, String userName); - - /** - * This should NOT delete the row, but merely set it as disabled - * - * @param accessToken - * the value to use to identify the row containing the access - * token - */ - void disableAccessToken(String accessToken); - - /** - * This should NOT delete the row, but merely set it as disabled and store the revoke date - * - * @param accessToken - * the value to use to identify the row containing the access - * token - */ - void revokeAccessToken(String accessToken); - - void disableAccessTokenByRefreshToken(String refreshTokenValue); - - /** - * This should NOT delete the row, but merely set it as disabled - * - * @param tokenId - * the id of the token that should be disabled - * @param userOrcid - * the id of the user owner of the token - */ - void disableAccessToken(Long tokenId, String userOrcid); - - void createNew(OrcidOauth2TokenDetail detail); - - boolean hasToken(String userName, long lastModified); - - int disableAccessTokenByCodeAndClient(String authorizationCode, String clientID, RevokeReason reason); - - /** - * This should NOT delete the row, but merely set it as disabled - * - * @param userOrcid - * the id of the user owner of the token - */ - void disableAccessTokenByUserOrcid(String userOrcid, RevokeReason reason); - - void disableClientAccess(String clientDetailsId, String userOrcid); - - boolean updateScopes(String acessToken, Set newScopes); - -} diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidAuthorizationEndpoint.java b/orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidAuthorizationEndpoint.java deleted file mode 100644 index e586c118a79..00000000000 --- a/orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidAuthorizationEndpoint.java +++ /dev/null @@ -1,173 +0,0 @@ -package org.orcid.core.oauth.service; - -import java.net.URI; -import java.net.URISyntaxException; -import java.security.Principal; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import org.apache.commons.lang3.StringUtils; -import org.orcid.core.constants.OrcidOauth2Constants; -import org.orcid.core.oauth.service.OrcidOAuth2RequestValidator.OpenIDConnectScopesToIgnore; -import org.orcid.jaxb.model.message.ScopePathType; -import org.springframework.security.oauth2.common.exceptions.ClientAuthenticationException; -import org.springframework.security.oauth2.common.exceptions.InvalidScopeException; -import org.springframework.security.oauth2.common.exceptions.OAuth2Exception; -import org.springframework.security.oauth2.common.exceptions.RedirectMismatchException; -import org.springframework.security.oauth2.common.util.OAuth2Utils; -import org.springframework.security.oauth2.provider.ClientDetails; -import org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint; -import org.springframework.web.HttpSessionRequiredException; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.support.SessionStatus; -import org.springframework.web.context.request.ServletWebRequest; -import org.springframework.web.servlet.ModelAndView; -import org.springframework.web.servlet.view.RedirectView; - -@RequestMapping(value = "/oauth/authorize") -public class OrcidAuthorizationEndpoint extends AuthorizationEndpoint { - - private String redirectUriError = "forward:/oauth/error/redirect-uri-mismatch"; - private String oauthError = "forward:/oauth/error"; - private OrcidOAuth2RequestValidator orcidOAuth2RequestValidator; - - @Override - @ExceptionHandler(HttpSessionRequiredException.class) - public ModelAndView handleHttpSessionRequiredException(HttpSessionRequiredException e, ServletWebRequest webRequest) throws Exception { - return new ModelAndView("redirect:" + buildRedirectUri(webRequest).toString()); - } - - @Override - @ExceptionHandler(OAuth2Exception.class) - public ModelAndView handleOAuth2Exception(OAuth2Exception e, ServletWebRequest webRequest) throws Exception { - logger.info("Handling OAuth2 error: " + e.getSummary()); - if (e instanceof RedirectMismatchException) { - return new ModelAndView(redirectUriError); - } else if (e instanceof ClientAuthenticationException) { - return new ModelAndView(oauthError); - } - - return super.handleOAuth2Exception(e, webRequest); - } - - @Override - @RequestMapping - public ModelAndView authorize(Map model, - @RequestParam Map requestParameters, SessionStatus sessionStatus, Principal principal) { - try { - modifyRequestParameters(requestParameters); - } catch (InvalidScopeException ise) { - String redirectUri = requestParameters.get("redirect_uri"); - String redirectUriWithParams = ""; - if(redirectUri != null) { - redirectUriWithParams = redirectUri; - } - redirectUriWithParams += "?error=invalid_scope&error_description=" + ise.getMessage(); - RedirectView rView = new RedirectView(redirectUriWithParams); - - ModelAndView error = new ModelAndView(); - error.setView(rView); - return error; - } - return super.authorize(model, requestParameters, sessionStatus, principal); - } - - private void modifyRequestParameters(Map requestParameters) throws InvalidScopeException { - for (Map.Entry entry : requestParameters.entrySet()) { - requestParameters.put(entry.getKey(), entry.getValue().trim()); - } - String scopes = requestParameters.get(OAuth2Utils.SCOPE); - if (scopes != null) { - requestParameters.put(OAuth2Utils.SCOPE, trimClientCredentialScopes(scopes.trim().replaceAll(" +", " "))); - } - // if we have an id_token response_type but no token scope, add it in. - // this is because spring can't cope without the 'token' response type. - if(isOpenIdWithTokenResponseType(requestParameters.get(OAuth2Utils.SCOPE), requestParameters.get(OAuth2Utils.RESPONSE_TYPE))) { - requestParameters.put(OAuth2Utils.RESPONSE_TYPE, "id_token token"); - } - } - - /** - * Validate if the given client have the defined scope - * @param scopes a space or comma separated list of scopes - * @param clientDetails - * @param responseType - * @throws InvalidScopeException in case the given client doesnt have any of the given scopes - * */ - public void validateScope(String scopes, ClientDetails clientDetails, String responseType) throws InvalidScopeException { - Map parameters = new HashMap(); - parameters.put(OAuth2Utils.SCOPE, scopes); - - //Check the user have permissions to the other scopes - orcidOAuth2RequestValidator.validateParameters(parameters, clientDetails,responseType); - } - - private URI buildRedirectUri(ServletWebRequest webRequest) throws URISyntaxException { - String[] referers = webRequest.getHeaderValues("referer"); - if (referers != null && referers.length > 0) { - return new URI(referers[0]); - } - String uri = "/session-expired"; - String contextPath = webRequest.getContextPath(); - if (contextPath != null) { - uri = contextPath + uri; - } - return new URI(uri); - } - - private String trimClientCredentialScopes(String scopes) throws InvalidScopeException { - String result = scopes; - for (String scope : OAuth2Utils.parseParameterList(scopes)) { - if (StringUtils.isNotBlank(scope)) { - if (OpenIDConnectScopesToIgnore.contains(scope)) {//remove openid connect scopes we do not support. - result = result.replaceAll(scope, ""); - continue; - } - ScopePathType scopeType = null; - try { - scopeType = ScopePathType.fromValue(scope); - } catch (Exception e) { - throw new InvalidScopeException("Invalid scope: " + scope); - } - if (scopeType.isClientCreditalScope()) { - if (result.contains(ScopePathType.ORCID_PROFILE_CREATE.getContent())) - result = result.replaceAll(ScopePathType.ORCID_PROFILE_CREATE.getContent(), ""); - else if (result.contains(ScopePathType.READ_PUBLIC.getContent())) - result = result.replaceAll(ScopePathType.READ_PUBLIC.getContent(), ""); - else if (result.contains(ScopePathType.WEBHOOK.getContent())) - result = result.replaceAll(ScopePathType.WEBHOOK.getContent(), ""); - } - } - } - - return result; - } - - public OrcidOAuth2RequestValidator getOrcidOAuth2RequestValidator() { - return orcidOAuth2RequestValidator; - } - - public void setOrcidOAuth2RequestValidator(OrcidOAuth2RequestValidator orcidOAuth2RequestValidator) { - this.orcidOAuth2RequestValidator = orcidOAuth2RequestValidator; - } - - public boolean isOpenIdWithTokenResponseType(String scopes, String responseTypes) { - // if we have an id_token response_type but no token scope, add it in. - // this is because spring can't cope without the 'token' response type. - List scopesList = Arrays.stream(scopes.split("\\s+")).map(String::trim).collect(Collectors.toList()); - boolean isOpenId = scopesList.contains(ScopePathType.OPENID.value()); - if (isOpenId && responseTypes != null) { - List responseTypeList = Arrays.stream(responseTypes.split("\\s+")).collect(Collectors.toList()); - if (responseTypeList != null - && (responseTypeList.contains(OrcidOauth2Constants.ID_TOKEN) || responseTypeList.contains(OrcidOauth2Constants.IMPLICIT_TOKEN_RESPONSE_TYPE))) { - return true; - } - } - return false; - } -} diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidOAuth2RequestValidator.java b/orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidOAuth2RequestValidator.java deleted file mode 100644 index 565a82e6683..00000000000 --- a/orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidOAuth2RequestValidator.java +++ /dev/null @@ -1,118 +0,0 @@ -package org.orcid.core.oauth.service; - -import java.util.Map; -import java.util.Set; - -import org.orcid.core.exception.ClientDeactivatedException; -import org.orcid.core.exception.LockedException; -import org.orcid.core.manager.ProfileEntityCacheManager; -import org.orcid.jaxb.model.message.ScopePathType; -import org.orcid.persistence.jpa.entities.ClientDetailsEntity; -import org.orcid.persistence.jpa.entities.ProfileEntity; -import org.springframework.security.oauth2.common.exceptions.InvalidScopeException; -import org.springframework.security.oauth2.common.util.OAuth2Utils; -import org.springframework.security.oauth2.provider.AuthorizationRequest; -import org.springframework.security.oauth2.provider.ClientDetails; -import org.springframework.security.oauth2.provider.TokenRequest; -import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestValidator; - -public class OrcidOAuth2RequestValidator extends DefaultOAuth2RequestValidator { - - public static enum ImplicitScopes { - OPENID("openid"), AUTHENTICATE("/authenticate"); - private String value; - ImplicitScopes(String v){ - value=v; - } - public static boolean isValid(Set test) { - for (String t : test){ - boolean found = false; - for (ImplicitScopes scope : ImplicitScopes.values()) { - if (scope.value.equals(t)) { - found = true; - } - } - if (!found) - return false; - } - return true; - } - public String toString(){ - return value; - } - }; - - public static enum OpenIDConnectScopesToIgnore { - PROFILE("profile"), EMAIL("email"), ADDRESS("address"), PHONE("phone"), OFFLINE_ACCESS("offline_access"); - private String value; - OpenIDConnectScopesToIgnore(String v){ - value = v; - } - public static boolean contains(String test) { - for (OpenIDConnectScopesToIgnore scope : OpenIDConnectScopesToIgnore.values()) { - if (scope.value.equals(test)) { - return true; - } - } - return false; - } - public String toString(){ - return value; - } - } - - private ProfileEntityCacheManager profileEntityCacheManager; - - public OrcidOAuth2RequestValidator(ProfileEntityCacheManager profileEntityCacheManager) { - this.profileEntityCacheManager = profileEntityCacheManager; - } - - //called by spring AuthorizationEndpoint using unmodified parameter map - //also called by LoginController before login. - public void validateParameters(Map parameters, ClientDetails clientDetails, String responseType) { - if (parameters.containsKey("scope")) { - if (clientDetails.isScoped()) { - Set validScope = clientDetails.getScope(); - for (String scope : OAuth2Utils.parseParameterList(parameters.get("scope"))) { - if (OpenIDConnectScopesToIgnore.contains(scope)) {//ignore openid scopes - continue; - } - ScopePathType scopeType = null; - try { - scopeType = ScopePathType.fromValue(scope); - } catch(Exception e) { - throw new InvalidScopeException("Invalid scope: " + scope); - } - if (scopeType.isClientCreditalScope()) - throw new InvalidScopeException("Invalid scope: " + scope); - if (!validScope.contains(scope)) - throw new InvalidScopeException("Invalid scope: " + scope); - } - } - } - } - - public void validateClientIsEnabled(ClientDetailsEntity clientDetails) throws LockedException { - ProfileEntity memberEntity = profileEntityCacheManager.retrieve(clientDetails.getGroupProfileId()); - //If it is locked - if(!memberEntity.isAccountNonLocked()) { - throw new LockedException("The client is locked"); - } - - if (clientDetails.getDeactivatedDate() != null) { - throw new ClientDeactivatedException(clientDetails.getClientId()); - } - } - - //included for clarity - @Override - public void validateScope(AuthorizationRequest authorizationRequest, ClientDetails client) throws InvalidScopeException { - super.validateScope(authorizationRequest, client); - } - - @Override - public void validateScope(TokenRequest tokenRequest, ClientDetails client) throws InvalidScopeException { - super.validateScope(tokenRequest, client); - } - -} diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidOauth2TokenDetailServiceImpl.java b/orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidOauth2TokenDetailServiceImpl.java deleted file mode 100644 index 5b1cbc6c4cb..00000000000 --- a/orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidOauth2TokenDetailServiceImpl.java +++ /dev/null @@ -1,281 +0,0 @@ -package org.orcid.core.oauth.service; - -import java.util.Arrays; -import java.util.Date; -import java.util.List; -import java.util.Set; - -import javax.annotation.Resource; -import javax.persistence.NoResultException; - -import org.apache.commons.lang3.StringUtils; -import org.orcid.core.constants.RevokeReason; -import org.orcid.core.oauth.OrcidOauth2TokenDetailService; -import org.orcid.core.utils.cache.redis.RedisClient; -import org.orcid.jaxb.model.message.ScopePathType; -import org.orcid.persistence.dao.OrcidOauth2TokenDetailDao; -import org.orcid.persistence.jpa.entities.OrcidOauth2TokenDetail; -import org.orcid.pojo.ajaxForm.PojoUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.security.oauth2.common.util.OAuth2Utils; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -/** - * @author Declan Newman (declan) Date: 19/04/2012 - */ -@Service -public class OrcidOauth2TokenDetailServiceImpl implements OrcidOauth2TokenDetailService { - - private static final Logger LOGGER = LoggerFactory.getLogger(OrcidOauth2TokenDetailServiceImpl.class); - - @Resource(name="orcidOauth2TokenDetailDao") - private OrcidOauth2TokenDetailDao orcidOauth2TokenDetailDao; - - @Resource(name="orcidOauth2TokenDetailDaoReadOnly") - private OrcidOauth2TokenDetailDao orcidOauth2TokenDetailDaoReadOnly; - - @Resource - private RedisClient redisClient; - - @Value("${org.orcid.core.utils.cache.redis.enabled:true}") - private boolean isTokenCacheEnabled; - - @Override - public void setOrcidOauth2TokenDetailDao(OrcidOauth2TokenDetailDao orcidOauth2TokenDetailDao) { - this.orcidOauth2TokenDetailDao = orcidOauth2TokenDetailDao; - } - - @Override - public OrcidOauth2TokenDetail findNonDisabledByTokenValue(String token) { - if(StringUtils.isBlank(token)) { - return null; - } - try { - return orcidOauth2TokenDetailDaoReadOnly.findNonDisabledByTokenValue(token); - } catch (NoResultException e) { - LOGGER.debug("No token found for token value {}", e, token); - return null; - } - } - - @Override - public OrcidOauth2TokenDetail findIgnoringDisabledByTokenValue(String token) { - try { - return orcidOauth2TokenDetailDaoReadOnly.findByTokenValue(token); - } catch (NoResultException e) { - LOGGER.debug("No token found for token value {}", e, token); - return null; - } - } - - @Override - public OrcidOauth2TokenDetail findByRefreshTokenValue(String refreshTokenValue) { - try { - return orcidOauth2TokenDetailDaoReadOnly.findByRefreshTokenValue(refreshTokenValue); - } catch (NoResultException e) { - LOGGER.debug("No token found for refresh token value {}", e, refreshTokenValue); - return null; - } - } - - @Override - @Transactional - public void removeByRefreshTokenValue(String refreshToken) { - orcidOauth2TokenDetailDao.removeByRefreshTokenValue(refreshToken); - } - - @Override - public List findByAuthenticationKey(String authKey) { - try { - return orcidOauth2TokenDetailDaoReadOnly.findByAuthenticationKey(authKey); - } catch (NoResultException e) { - LOGGER.debug("No token found for auth token {}", e, authKey); - return null; - } - } - - @Override - public List findByUserName(String userName) { - try { - return orcidOauth2TokenDetailDaoReadOnly.findByUserName(userName); - } catch (NoResultException e) { - LOGGER.debug("No token found for username {}", e, userName); - return null; - } - } - - @Override - @Cacheable(value = "count-tokens", key = "#userName.concat('-').concat(#lastModified)") - public boolean hasToken(String userName, long lastModified) { - return orcidOauth2TokenDetailDaoReadOnly.hasToken(userName); - } - - @Override - public List findByClientId(String clientId) { - try { - return orcidOauth2TokenDetailDaoReadOnly.findByClientId(clientId); - } catch (NoResultException e) { - LOGGER.debug("No token found for client id {}", e, clientId); - return null; - } - } - - /** - * This should NOT delete the row, but merely set it as disabled - * - * @param accessToken - * the value to use to identify the row containing the access - * token - */ - @Override - @Transactional - public void disableAccessToken(String accessToken) { - orcidOauth2TokenDetailDao.disableAccessToken(accessToken); - } - - @Override - @Transactional - public void revokeAccessToken(String accessToken) { - // Remove the token from the cache - if(isTokenCacheEnabled) { - redisClient.remove(accessToken); - } - // Revoke the token - orcidOauth2TokenDetailDao.revokeAccessToken(accessToken); - } - - /** - * This should NOT delete the row, but merely set it as disabled - * - * @param tokenId - * the id of the token that should be disabled - * @param userOrcid - * the id of the user owner of the token - */ - @Override - @Transactional - public void disableAccessToken(Long tokenId, String userOrcid) { - if(PojoUtil.isEmpty(userOrcid) || tokenId == null) { - throw new IllegalArgumentException("One of the provided params is empty: userOrcid='" + userOrcid + "' tokenId='" + String.valueOf(tokenId) + "'"); - } - - //Iterate over all tokens that belongs to this user and client, to remove all the ones that have the same scopes - OrcidOauth2TokenDetail tokenToDisable = orcidOauth2TokenDetailDaoReadOnly.find(tokenId); - String scopesToDisableString = tokenToDisable.getScope(); - Set scopesToDisable = ScopePathType.getScopesFromSpaceSeparatedString(scopesToDisableString); - - List allTokens = orcidOauth2TokenDetailDaoReadOnly.findByClientIdAndUserName(tokenToDisable.getClientDetailsId(), userOrcid); - //Iterate over all tokens and verify we disable all the ones that have the same scopes - for(OrcidOauth2TokenDetail token : allTokens) { - if(token.getTokenDisabled() == null || !token.getTokenDisabled()) { - if(!PojoUtil.isEmpty(token.getScope())) { - Set tokenScopes = ScopePathType.getScopesFromSpaceSeparatedString(token.getScope()); - if(scopesToDisable.equals(tokenScopes)) { - orcidOauth2TokenDetailDao.disableAccessTokenById(token.getId(), userOrcid); - } - } - } - } - } - - /** - * This should NOT delete the row, but merely remove the value from it - * - * @param refreshTokenValue - * the value to use to identify the row containing the access - * token - */ - @Override - @Transactional - public void disableAccessTokenByRefreshToken(String refreshTokenValue) { - orcidOauth2TokenDetailDao.disableAccessTokenByRefreshToken(refreshTokenValue); - } - - @Override - @Transactional - public void createNew(OrcidOauth2TokenDetail detail) { - orcidOauth2TokenDetailDao.persist(detail); - orcidOauth2TokenDetailDao.flush(); - } - - @Override - public List findByClientIdAndUserName(String clientId, String userName) { - try { - return orcidOauth2TokenDetailDaoReadOnly.findByClientIdAndUserName(clientId, userName); - } catch (NoResultException e) { - LOGGER.debug("No token found for client id {}", e, clientId); - return null; - } - } - - @Override - public boolean doesClientKnowUser(String clientId, String userOrcid) { - List existingTokens = orcidOauth2TokenDetailDaoReadOnly.findByClientIdAndUserName(clientId, userOrcid); - if (existingTokens == null || existingTokens.isEmpty()) { - return false; - } - Date now = new Date(); - for (OrcidOauth2TokenDetail token : existingTokens) { - if (token.getTokenExpiration() != null && token.getTokenExpiration().after(now) && (token.getTokenDisabled() == null || !token.getTokenDisabled())) { - // Verify the token have at least one of the required scopes - List scopes = Arrays.asList(ScopePathType.ACTIVITIES_UPDATE.value(), ScopePathType.AFFILIATIONS_CREATE.value(), ScopePathType.AFFILIATIONS_UPDATE.value()); - if(!PojoUtil.isEmpty(token.getScope())) { - for(String scope : token.getScope().split(" ")) { - if(scopes.contains(scope.trim())) { - return true; - } - } - } - } - } - return false; - } - - @Override - @Transactional - public int disableAccessTokenByCodeAndClient(String authorizationCode, String clientID, RevokeReason reason) { - // Find the tokens to disable - List tokensToDisable = orcidOauth2TokenDetailDao.findAccessTokenByCodeAndClient(authorizationCode, clientID); - // Remove them from the cache - for(String accessToken : tokensToDisable) { - LOGGER.info("Token {} will be disabled because auth code {} was reused", accessToken, authorizationCode); - if(isTokenCacheEnabled) { - redisClient.remove(accessToken); - } - } - // Disable them - return orcidOauth2TokenDetailDao.disableAccessTokenByCodeAndClient(authorizationCode, clientID, reason.name()); - } - - @Override - @Transactional - public void disableAccessTokenByUserOrcid(String userOrcid, RevokeReason reason) { - orcidOauth2TokenDetailDao.disableAccessTokenByUserOrcid(userOrcid, reason.name()); - } - - @Override - @Transactional - public void disableClientAccess(String clientDetailsId, String userOrcid) { - // As a security measure, remove any user tokens from the cache - List userTokens = findByUserName(userOrcid); - if(userTokens != null && !userTokens.isEmpty()) { - for(OrcidOauth2TokenDetail token : userTokens) { - if(clientDetailsId.equals(token.getClientDetailsId())) { - redisClient.remove(token.getTokenValue()); - } - } - } - // And then disable all user tokens - orcidOauth2TokenDetailDao.disableClientAccessTokensByUserOrcid(userOrcid, clientDetailsId); - } - - @Override - @Transactional - public boolean updateScopes(String acessToken, Set newScopes) { - return orcidOauth2TokenDetailDao.updateScopes(acessToken, OAuth2Utils.formatParameterList(newScopes)); - } -} diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidTokenStore.java b/orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidTokenStore.java deleted file mode 100644 index 1397e77f4e2..00000000000 --- a/orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidTokenStore.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.orcid.core.oauth.service; - -import java.util.Map; - -import org.orcid.core.constants.RevokeReason; -import org.orcid.persistence.jpa.entities.OrcidOauth2TokenDetail; -import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; -import org.springframework.security.oauth2.common.OAuth2AccessToken; -import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; -import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.security.oauth2.provider.token.TokenStore; - -public interface OrcidTokenStore extends TokenStore { - - OrcidOauth2TokenDetail readOrcidOauth2TokenDetail(String tokenValue); - - OAuth2AccessToken readEvenDisabledAccessToken(String tokenValue); - - OAuth2Authentication readAuthenticationEvenOnDisabledTokens(String tokenValue); - - void storeRevokedAccessToken(DefaultOAuth2AccessToken token, OAuth2Authentication authentication, RevokeReason revokeReason); - - void removeAccessToken(String accessTokenValue); - - OAuth2Authentication readAuthenticationFromCachedToken(Map cachedTokenData); - - void isClientEnabled(String clientId) throws InvalidTokenException; - - String readClientId(String tokenValue); -} diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidTokenStoreServiceImpl.java b/orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidTokenStoreServiceImpl.java deleted file mode 100644 index 61eb00eec8d..00000000000 --- a/orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidTokenStoreServiceImpl.java +++ /dev/null @@ -1,560 +0,0 @@ -package org.orcid.core.oauth.service; - -import java.text.ParseException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.annotation.Resource; - -import org.apache.commons.lang.StringUtils; -import org.orcid.core.constants.OrcidOauth2Constants; -import org.orcid.core.constants.RevokeReason; -import org.orcid.core.exception.ClientDeactivatedException; -import org.orcid.core.exception.LockedException; -import org.orcid.core.manager.ClientDetailsEntityCacheManager; -import org.orcid.core.manager.ProfileEntityCacheManager; -import org.orcid.core.oauth.OrcidOAuth2Authentication; -import org.orcid.core.oauth.OrcidOauth2TokenDetailService; -import org.orcid.core.oauth.OrcidOauth2UserAuthentication; -import org.orcid.persistence.jpa.entities.ClientDetailsEntity; -import org.orcid.persistence.jpa.entities.OrcidOauth2TokenDetail; -import org.orcid.persistence.jpa.entities.ProfileEntity; -import org.orcid.pojo.ajaxForm.PojoUtil; -import org.springframework.security.core.Authentication; -import org.springframework.security.oauth2.common.DefaultExpiringOAuth2RefreshToken; -import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; -import org.springframework.security.oauth2.common.DefaultOAuth2RefreshToken; -import org.springframework.security.oauth2.common.ExpiringOAuth2RefreshToken; -import org.springframework.security.oauth2.common.OAuth2AccessToken; -import org.springframework.security.oauth2.common.OAuth2RefreshToken; -import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; -import org.springframework.security.oauth2.common.util.OAuth2Utils; -import org.springframework.security.oauth2.provider.AuthorizationRequest; -import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.security.oauth2.provider.OAuth2Request; -import org.springframework.security.oauth2.provider.token.AuthenticationKeyGenerator; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Propagation; -import org.springframework.transaction.annotation.Transactional; - -import com.nimbusds.jwt.SignedJWT; - -/** - * @author Declan Newman (declan) Date: 15/04/2012 - */ - -@Service("orcidTokenStore") -public class OrcidTokenStoreServiceImpl implements OrcidTokenStore { - - @Resource - private OrcidOauth2TokenDetailService orcidOauthTokenDetailService; - - @Resource(name = "profileEntityCacheManager") - ProfileEntityCacheManager profileEntityCacheManager; - - @Resource - ClientDetailsEntityCacheManager clientDetailsEntityCacheManager; - - @Resource - private OrcidOAuth2RequestValidator orcidOAuth2RequestValidator; - - @Resource - private AuthenticationKeyGenerator authenticationKeyGenerator; - - @Override - public OrcidOauth2TokenDetail readOrcidOauth2TokenDetail(String token) { - return orcidOauthTokenDetailService.findIgnoringDisabledByTokenValue(token); - } - - @Override - public String readClientId(String token) { - String clientId = null; - OrcidOauth2TokenDetail orcidTokenDetail = orcidOauthTokenDetailService.findIgnoringDisabledByTokenValue(token); - if(orcidTokenDetail != null) { - clientId = orcidTokenDetail.getClientDetailsId(); - } - return clientId; - } - - /** - * Read the authentication stored under the specified token value. - * - * @param token - * The token value under which the authentication is stored. - * @return The authentication, or null if none. - */ - @Override - @Transactional - public OAuth2Authentication readAuthentication(String token) { - OrcidOauth2TokenDetail detail = orcidOauthTokenDetailService.findNonDisabledByTokenValue(token); - return getOAuth2AuthenticationFromDetails(detail); - } - - @Override - @Transactional - public OAuth2Authentication readAuthenticationEvenOnDisabledTokens(String token) { - OrcidOauth2TokenDetail detail = orcidOauthTokenDetailService.findIgnoringDisabledByTokenValue(token); - return getOAuth2AuthenticationFromDetails(detail); - } - - @Override - @Transactional - public OAuth2Authentication readAuthentication(OAuth2AccessToken token) { - return readAuthentication(token.getValue()); - } - - @Override - public OAuth2Authentication readAuthenticationFromCachedToken(Map cachedTokenData) { - String accessTokenValue = (String) cachedTokenData.get(OrcidOauth2Constants.ACCESS_TOKEN); - long tokenExpirationTime = Long.valueOf((String) cachedTokenData.get(OrcidOauth2Constants.TOKEN_EXPIRATION_TIME)); - isTokenExpired(tokenExpirationTime, accessTokenValue); - - String clientId = (String) cachedTokenData.get(OrcidOauth2Constants.CLIENT_ID); - String scopeString = (String) cachedTokenData.get(OrcidOauth2Constants.SCOPE_PARAM); - String resourceId = (String) cachedTokenData.get(OrcidOauth2Constants.RESOURCE_IDS); - String orcid = (String) cachedTokenData.get(OrcidOauth2Constants.ORCID); - boolean approved = Boolean.valueOf((String) cachedTokenData.get(OrcidOauth2Constants.APPROVED)); - - OrcidOauth2TokenDetail tokenDetails = new OrcidOauth2TokenDetail(); - tokenDetails.setClientDetailsId(clientId); - tokenDetails.setScope(scopeString); - tokenDetails.setResourceId(resourceId); - tokenDetails.setTokenValue(accessTokenValue); - tokenDetails.setOrcid(orcid); - tokenDetails.setApproved(approved); - return getOAuth2AuthenticationFromDetails(tokenDetails); - } - - /** - * Store an access token. - * - * @param token - * The token to store. - * @param authentication - * The authentication associated with the token. - */ - @Deprecated(since = "Once the authorization server is deployed", forRemoval = true) - @Override - public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) { - OrcidOauth2TokenDetail detail = populatePropertiesFromTokenAndAuthentication(token, authentication, null); - orcidOauthTokenDetailService.createNew(detail); - // Set the token id in the additional details - token.getAdditionalInformation().put(OrcidOauth2Constants.TOKEN_ID, detail.getId()); - if(detail.getOboClientDetailsId() != null) { - token.getAdditionalInformation().put(OrcidOauth2Constants.IS_OBO_TOKEN, "true"); - } - } - - /** - * Store a revoked access token with the intention of allowing the member to use it for deleting their own activities when required - * */ - @Override - public void storeRevokedAccessToken(DefaultOAuth2AccessToken token, OAuth2Authentication authentication, RevokeReason revokeReason) { - OrcidOauth2TokenDetail detail = populatePropertiesFromTokenAndAuthentication(token, authentication, null); - detail.setTokenDisabled(true); - detail.setRevocationDate(new Date()); - if(revokeReason != null) { - detail.setRevokeReason(revokeReason.name()); - } - orcidOauthTokenDetailService.createNew(detail); - // Set the token id in the additional details - token.getAdditionalInformation().put(OrcidOauth2Constants.TOKEN_ID, detail.getId()); - } - - /** - * Read an access token from the store. - * - * @param tokenValue - * The token value. - * @return The access token to read. - */ - @Override - public OAuth2AccessToken readAccessToken(String tokenValue) { - return readAccessToken(tokenValue, false); - } - - /** - * Read an access token from the store even if they are marked as disabled. - * - * @param tokenValue - * The token value. - * @return The access token to read. - */ - @Override - public OAuth2AccessToken readEvenDisabledAccessToken(String tokenValue) { - return readAccessToken(tokenValue, true); - } - - /** - * Read an access token from the store. If `includeDisabled` is false, the - * token will not be returned if it is disabled - * - * @param tokenValue - * The token value. - * @param includeDisabled - * @return The access token to read. - */ - private OAuth2AccessToken readAccessToken(String tokenValue, boolean includeDisabled) { - if (tokenValue == null) { - return null; - } - - OrcidOauth2TokenDetail detail; - if (includeDisabled) { - detail = orcidOauthTokenDetailService.findIgnoringDisabledByTokenValue(tokenValue); - } else { - detail = orcidOauthTokenDetailService.findNonDisabledByTokenValue(tokenValue); - } - return getOauth2AccessTokenFromDetails(detail); - } - - /** - * Disable an access token from the database. - * - * @param tokenValue - * The token to remove from the database. - */ - @Override - @Transactional(propagation = Propagation.REQUIRES_NEW) - public void removeAccessToken(OAuth2AccessToken accessToken) { - orcidOauthTokenDetailService.disableAccessToken(accessToken.getValue()); - } - - /** - * Disable an access token from the database. - * - * @param tokenValue - * The token to remove from the database. - */ - @Override - @Transactional(propagation = Propagation.REQUIRES_NEW) - public void removeAccessToken(String accessTokenValue) { - orcidOauthTokenDetailService.disableAccessToken(accessTokenValue); - } - - /** - * Store the specified refresh token in the database. - * - * @param refreshToken - * The refresh token to store. - * @param authentication - * The authentication associated with the refresh token. - */ - @Override - public void storeRefreshToken(OAuth2RefreshToken refreshToken, OAuth2Authentication authentication) { - // The refresh token will be stored during the token creation. We don't - // want to create it beforehand - } - - /** - * Read a refresh token from the store. - * - * @param refreshTokenValue - * The value of the token to read. - * @return The token. - */ - @Override - public ExpiringOAuth2RefreshToken readRefreshToken(String refreshTokenValue) { - OrcidOauth2TokenDetail detail = orcidOauthTokenDetailService.findByRefreshTokenValue(refreshTokenValue); - if (detail != null && StringUtils.isNotBlank(detail.getTokenValue())) { - return new DefaultExpiringOAuth2RefreshToken(detail.getRefreshTokenValue(), detail.getRefreshTokenExpiration()); - } - return null; - } - - /** - * @param refreshTokenValue - * a refresh token value - * @return the authentication originally used to grant the refresh token - */ - @Override - public OAuth2Authentication readAuthenticationForRefreshToken(OAuth2RefreshToken refreshToken) { - OrcidOauth2TokenDetail detail = orcidOauthTokenDetailService.findByRefreshTokenValue(refreshToken.getValue()); - return getOAuth2AuthenticationFromDetails(detail); - } - - /** - * Remove a refresh token from the database. - * - * @param refreshTokenValue - * The value of the token to remove from the database. - */ - @Override - @Transactional - public void removeRefreshToken(OAuth2RefreshToken refreshToken) { - orcidOauthTokenDetailService.removeByRefreshTokenValue(refreshToken.getValue()); - } - - /** - * Remove an access token using a refresh token. This functionality is - * necessary so refresh tokens can't be used to create an unlimited number - * of access tokens. - * - * @param refreshTokenValue - * The refresh token. - */ - @Override - @Transactional - public void removeAccessTokenUsingRefreshToken(OAuth2RefreshToken refreshToken) { - orcidOauthTokenDetailService.disableAccessTokenByRefreshToken(refreshToken.getValue()); - } - - /** - * Retrieve an access token stored against the provided authentication key, - * if it exists. - * - * @param authentication - * the authentication key for the access token - * @return the access token or null if there was none - */ - @Override - public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) { - String authKey = authenticationKeyGenerator.extractKey(authentication); - List details = orcidOauthTokenDetailService.findByAuthenticationKey(authKey); - // Since we are now able to have more than one token for the combo - // user-scopes, we will need to return the oldest token, the first token - // created with these scopes - if (details != null && !details.isEmpty()) { - OrcidOauth2TokenDetail oldestToken = null; - for (OrcidOauth2TokenDetail tokenDetails : details) { - if (oldestToken == null) { - oldestToken = tokenDetails; - } else { - if (tokenDetails.getDateCreated().before(oldestToken.getDateCreated())) { - oldestToken = tokenDetails; - } - } - } - return getOauth2AccessTokenFromDetails(oldestToken); - } - return null; - } - - /** - * @param userName - * the user name to search - * @return a collection of access tokens - */ - public Collection findTokensByUserName(String userName) { - List details = orcidOauthTokenDetailService.findByUserName(userName); - List accessTokens = new ArrayList(); - if (details != null && !details.isEmpty()) { - for (OrcidOauth2TokenDetail detail : details) { - accessTokens.add(getOauth2AccessTokenFromDetails(detail)); - } - } - return accessTokens; - } - - /** - * @param clientId - * the client id - * @return a collection of access tokens - */ - @Override - public Collection findTokensByClientId(String clientId) { - List details = orcidOauthTokenDetailService.findByClientId(clientId); - List accessTokens = new ArrayList(); - if (details != null && !details.isEmpty()) { - for (OrcidOauth2TokenDetail detail : details) { - accessTokens.add(getOauth2AccessTokenFromDetails(detail)); - } - } - return accessTokens; - } - - private OAuth2AccessToken getOauth2AccessTokenFromDetails(OrcidOauth2TokenDetail detail) { - DefaultOAuth2AccessToken token = null; - if (detail != null && StringUtils.isNotBlank(detail.getTokenValue())) { - token = new DefaultOAuth2AccessToken(detail.getTokenValue()); - token.setExpiration(detail.getTokenExpiration()); - token.setScope(OAuth2Utils.parseParameterList(detail.getScope())); - token.setTokenType(detail.getTokenType()); - String refreshToken = detail.getRefreshTokenValue(); - OAuth2RefreshToken rt; - if (StringUtils.isNotBlank(refreshToken)) { - if (detail.getRefreshTokenExpiration() != null) { - rt = new DefaultExpiringOAuth2RefreshToken(detail.getRefreshTokenValue(), detail.getRefreshTokenExpiration()); - } else { - rt = new DefaultOAuth2RefreshToken(detail.getRefreshTokenValue()); - } - token.setRefreshToken(rt); - } - String orcid = detail.getOrcid(); - if (orcid != null) { - Map additionalInfo = new HashMap(); - additionalInfo.put(OrcidOauth2Constants.ORCID, orcid); - additionalInfo.put(OrcidOauth2Constants.PERSISTENT, detail.isPersistent()); - additionalInfo.put(OrcidOauth2Constants.DATE_CREATED, detail.getDateCreated()); - additionalInfo.put(OrcidOauth2Constants.TOKEN_VERSION, detail.getVersion()); - token.setAdditionalInformation(additionalInfo); - } - - String clientId = detail.getClientDetailsId(); - if(!PojoUtil.isEmpty(clientId)) { - Map additionalInfo = new HashMap(); - Map additionalInfoInToken = token.getAdditionalInformation(); - if(additionalInfoInToken != null && !additionalInfoInToken.isEmpty()) { - additionalInfo.putAll(additionalInfoInToken); - } - // Copy to a new one to avoid unmodifiable - additionalInfo.put(OrcidOauth2Constants.CLIENT_ID, clientId); - token.setAdditionalInformation(additionalInfo); - } - } - - return token; - } - - private OAuth2Authentication getOAuth2AuthenticationFromDetails(OrcidOauth2TokenDetail details) { - if (details != null) { - // Check member is not locked - isClientEnabled(details.getClientDetailsId()); - ClientDetailsEntity clientDetailsEntity = clientDetailsEntityCacheManager.retrieve(details.getClientDetailsId()); - Authentication authentication = null; - AuthorizationRequest request = null; - if (clientDetailsEntity != null) { - Set scopes = OAuth2Utils.parseParameterList(details.getScope()); - request = new AuthorizationRequest(clientDetailsEntity.getClientId(), scopes); - request.setAuthorities(clientDetailsEntity.getAuthorities()); - Set resourceIds = new HashSet<>(); - resourceIds.add(details.getResourceId()); - request.setResourceIds(resourceIds); - request.setApproved(details.isApproved()); - String orcid = details.getOrcid(); - if (orcid != null) { - ProfileEntity profile = profileEntityCacheManager.retrieve(orcid); - authentication = new OrcidOauth2UserAuthentication(profile, details.isApproved()); - } - } - return new OrcidOAuth2Authentication(request, authentication, details.getTokenValue()); - } - throw new InvalidTokenException("Token not found"); - } - - private OrcidOauth2TokenDetail populatePropertiesFromTokenAndAuthentication(OAuth2AccessToken token, OAuth2Authentication authentication, - OrcidOauth2TokenDetail detail) { - OAuth2Request authorizationRequest = authentication.getOAuth2Request(); - if (detail == null) { - detail = new OrcidOauth2TokenDetail(); - } - - //Update to put auth code in token detail so it can be revoked based on code if needed. - if (authentication.getOAuth2Request().getRequestParameters().get("code") != null){ - detail.setAuthorizationCode(authentication.getOAuth2Request().getRequestParameters().get("code").toString()); - } - - String clientId = authorizationRequest.getClientId(); - String authKey = authenticationKeyGenerator.extractKey(authentication); - detail.setAuthenticationKey(authKey); - detail.setClientDetailsId(clientId); - - OAuth2RefreshToken refreshToken = token.getRefreshToken(); - if (refreshToken != null && StringUtils.isNotBlank(refreshToken.getValue())) { - if (refreshToken instanceof ExpiringOAuth2RefreshToken) { - // Override the refresh token expiration from the client - // details, and make it the same as the token itself - detail.setRefreshTokenExpiration(token.getExpiration()); - } - detail.setRefreshTokenValue(refreshToken.getValue()); - } - if (!authentication.isClientOnly()) { - Object principal = authentication.getPrincipal(); - if (principal instanceof ProfileEntity) { - ProfileEntity profileEntity = (ProfileEntity) authentication.getPrincipal(); - detail.setOrcid(profileEntity.getId()); - } - } - - detail.setTokenValue(token.getValue()); - detail.setTokenType(token.getTokenType()); - detail.setTokenExpiration(token.getExpiration()); - detail.setApproved(authorizationRequest.isApproved()); - detail.setRedirectUri(authorizationRequest.getRedirectUri()); - - Set resourceIds = authorizationRequest.getResourceIds(); - if(resourceIds == null || resourceIds.isEmpty()) { - ClientDetailsEntity clientDetails = clientDetailsEntityCacheManager.retrieve(clientId); - resourceIds = clientDetails.getResourceIds(); - } - - detail.setResourceId(OAuth2Utils.formatParameterList(resourceIds)); - detail.setResponseType(OAuth2Utils.formatParameterList(authorizationRequest.getResponseTypes())); - detail.setScope(OAuth2Utils.formatParameterList(authorizationRequest.getScope())); - - Map additionalInfo = token.getAdditionalInformation(); - if (additionalInfo != null) { - if (additionalInfo.containsKey(OrcidOauth2Constants.TOKEN_VERSION)) { - String sVersion = String.valueOf(additionalInfo.get(OrcidOauth2Constants.TOKEN_VERSION)); - detail.setVersion(Long.valueOf(sVersion)); - } else { - // TODO: As of Jan 2015 all tokens will be new tokens, so, we - // will have to remove the token version code and - // treat all tokens as new tokens - detail.setVersion(Long.valueOf(OrcidOauth2Constants.PERSISTENT_TOKEN)); - } - - if (additionalInfo.containsKey(OrcidOauth2Constants.PERSISTENT)) { - boolean isPersistentKey = (Boolean) additionalInfo.get(OrcidOauth2Constants.PERSISTENT); - detail.setPersistent(isPersistentKey); - } else { - detail.setPersistent(false); - } - } else { - detail.setPersistent(false); - } - - //Set OBO client if possible. - if(OrcidOauth2Constants.IETF_EXCHANGE_GRANT_TYPE.equals(authentication.getOAuth2Request().getGrantType()) - && authentication.getOAuth2Request().getRequestParameters().containsKey(OrcidOauth2Constants.IETF_EXCHANGE_SUBJECT_TOKEN) - && OrcidOauth2Constants.IETF_EXCHANGE_ID_TOKEN.equals(authentication.getOAuth2Request().getRequestParameters().get(OrcidOauth2Constants.IETF_EXCHANGE_SUBJECT_TOKEN_TYPE))) { - try { - SignedJWT claims = SignedJWT.parse(authentication.getOAuth2Request().getRequestParameters().get(OrcidOauth2Constants.IETF_EXCHANGE_SUBJECT_TOKEN)); - detail.setOboClientDetailsId(claims.getJWTClaimsSet().getAudience().get(0)); - } catch (ParseException e) { - throw new IllegalArgumentException("Unexpected id token value, cannot parse the id_token"); - } - } - - return detail; - } - - @Override - public Collection findTokensByClientIdAndUserName(String clientId, String userName) { - List details = orcidOauthTokenDetailService.findByClientIdAndUserName(clientId, userName); - List accessTokens = new ArrayList(); - if (details != null && !details.isEmpty()) { - for (OrcidOauth2TokenDetail detail : details) { - if(detail.getTokenDisabled() == null || !detail.getTokenDisabled()) { - accessTokens.add(getOauth2AccessTokenFromDetails(detail)); - } - } - } - return accessTokens; - } - - private void isTokenExpired(long expirationTime, String tokenValue) { - Date expirationDate = new Date(expirationTime); - if(expirationDate.before(new Date())) { - removeAccessToken(tokenValue); - throw new InvalidTokenException("Access token expired: " + tokenValue); - } - } - - public void isClientEnabled(String clientId) throws InvalidTokenException { - ClientDetailsEntity clientEntity = clientDetailsEntityCacheManager.retrieve(clientId); - try { - orcidOAuth2RequestValidator.validateClientIsEnabled(clientEntity); - } catch (LockedException le) { - throw new InvalidTokenException(le.getMessage()); - } catch (ClientDeactivatedException e) { - throw new InvalidTokenException(e.getMessage()); - } - } -} diff --git a/orcid-core/src/main/resources/orcid-core-context.xml b/orcid-core/src/main/resources/orcid-core-context.xml index cbfaecf05bb..78fbe4c6335 100644 --- a/orcid-core/src/main/resources/orcid-core-context.xml +++ b/orcid-core/src/main/resources/orcid-core-context.xml @@ -41,34 +41,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -82,17 +54,13 @@ - - - - - - - - - - - + + + + + + + diff --git a/orcid-persistence/pom.xml b/orcid-persistence/pom.xml index b071541a8d2..3be4e120a5d 100644 --- a/orcid-persistence/pom.xml +++ b/orcid-persistence/pom.xml @@ -103,11 +103,7 @@ org.springframework.security spring-security-core - - org.springframework.security.oauth - spring-security-oauth2 - - + org.aspectj diff --git a/orcid-persistence/src/main/java/org/orcid/persistence/jpa/entities/ClientDetailsEntity.java b/orcid-persistence/src/main/java/org/orcid/persistence/jpa/entities/ClientDetailsEntity.java index 431893bf308..edb52480e83 100644 --- a/orcid-persistence/src/main/java/org/orcid/persistence/jpa/entities/ClientDetailsEntity.java +++ b/orcid-persistence/src/main/java/org/orcid/persistence/jpa/entities/ClientDetailsEntity.java @@ -25,7 +25,6 @@ import org.hibernate.annotations.Sort; import org.hibernate.annotations.SortType; import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.oauth2.provider.ClientDetails; import org.springframework.util.StringUtils; /** @@ -33,7 +32,7 @@ */ @Entity @Table(name = "client_details") -public class ClientDetailsEntity extends BaseEntity implements ClientDetails, Serializable { +public class ClientDetailsEntity extends BaseEntity implements Serializable { private static final long serialVersionUID = 1L; @@ -194,7 +193,6 @@ public void setGroupProfileId(String groupProfileId) { * * @return The client id. */ - @Override @Transient public String getClientId() { return clientId; @@ -205,7 +203,6 @@ public String getClientId() { * * @return The resources of this client. */ - @Override @Transient public Set getResourceIds() { Set rids = new HashSet(); @@ -222,7 +219,6 @@ public Set getResourceIds() { * * @return Whether a secret is required to authenticate this client. */ - @Override @Transient public boolean isSecretRequired() { return StringUtils.hasText(clientSecret); @@ -234,7 +230,6 @@ public boolean isSecretRequired() { * * @return The client secret. */ - @Override @Transient public String getClientSecret() { return getDecryptedClientSecret(); @@ -310,7 +305,6 @@ public void setDecryptedClientSecret(String decryptedClientSecret) { * * @return Whether this client is limited to a specific scope. */ - @Override @Transient public boolean isScoped() { return this.clientScopes != null && !this.clientScopes.isEmpty(); @@ -322,7 +316,6 @@ public boolean isScoped() { * * @return The scope of this client. */ - @Override @Transient public Set getScope() { Set sps = new HashSet(); @@ -339,7 +332,6 @@ public Set getScope() { * * @return The grant types for which this client is authorized. */ - @Override @Transient public Set getAuthorizedGrantTypes() { Set grants = new HashSet(); @@ -357,7 +349,6 @@ public Set getAuthorizedGrantTypes() { * * @return The pre-defined redirect URI for this client. */ - @Override @Transient public Set getRegisteredRedirectUri() { Set redirects = null; @@ -378,7 +369,6 @@ public Set getRegisteredRedirectUri() { * * @return The authorities. */ - @Override @Transient public Collection getAuthorities() { Collection gas = new ArrayList(); @@ -394,31 +384,11 @@ public Collection getAuthorities() { * * @return the access token validity period */ - @Override @Transient public Integer getAccessTokenValiditySeconds() { return accessTokenValiditySeconds; } - @Override - @Transient - public Integer getRefreshTokenValiditySeconds() { - // Not currently required - return null; - } - - @Override - @Transient - public Map getAdditionalInformation() { - // Not currently required - return null; - } - - @Override - @Transient - public boolean isAutoApprove(String scope) { - return false; - } @Column(name = "authentication_provider_id") public String getAuthenticationProviderId() { diff --git a/orcid-pub-web/pom.xml b/orcid-pub-web/pom.xml index 417c07440fa..8f8c49ed465 100644 --- a/orcid-pub-web/pom.xml +++ b/orcid-pub-web/pom.xml @@ -38,10 +38,6 @@ org.springframework.security spring-security-web - - org.springframework.security.oauth - spring-security-oauth2 - diff --git a/orcid-web/pom.xml b/orcid-web/pom.xml index 00c9512757e..233d25b585d 100644 --- a/orcid-web/pom.xml +++ b/orcid-web/pom.xml @@ -99,10 +99,6 @@ org.springframework.security spring-security-core - - org.springframework.security.oauth - spring-security-oauth2 - org.springframework.security spring-security-web diff --git a/orcid-web/src/main/java/org/orcid/frontend/web/controllers/OpenIDController.java b/orcid-web/src/main/java/org/orcid/frontend/web/controllers/OpenIDController.java index e07a47db3ec..7b589d15088 100644 --- a/orcid-web/src/main/java/org/orcid/frontend/web/controllers/OpenIDController.java +++ b/orcid-web/src/main/java/org/orcid/frontend/web/controllers/OpenIDController.java @@ -16,8 +16,6 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.security.oauth2.common.OAuth2AccessToken; -import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @@ -41,9 +39,6 @@ public class OpenIDController { @Resource(name = "personDetailsManagerReadOnlyV3") private PersonDetailsManagerReadOnly personDetailsManagerReadOnly; - @Resource(name="orcidTokenStore") - private TokenStore tokenStore; - @Resource OpenIDConnectDiscoveryService openIDConnectDiscoveryService; @Value("${org.orcid.core.baseUri}") @@ -91,6 +86,8 @@ public class OpenIDController { //lookup token, check it's valid, check scope. //deal with incorrect bearer case in request (I'm looking at you spring security!) private OpenIDConnectUserInfo getInfoFromToken(String tokenValue) { + //TODO: Refactor this to get the information from the database + // This is unexpected and should be done with token introspection form the oauth server OAuth2AccessToken tok = tokenStore.readAccessToken(tokenValue); if (tok != null && !tok.isExpired()){ boolean hasScope = false; diff --git a/orcid-web/src/main/resources/orcid-core-context-spam.xml b/orcid-web/src/main/resources/orcid-core-context-spam.xml index 0a6853e380d..5581f7b200d 100644 --- a/orcid-web/src/main/resources/orcid-core-context-spam.xml +++ b/orcid-web/src/main/resources/orcid-core-context-spam.xml @@ -38,29 +38,9 @@ - - - - - - - - - - - - - - - - - - - - @@ -71,17 +51,7 @@ - - - - - - - - - - - + diff --git a/pom.xml b/pom.xml index 9826712f270..ff319996d1f 100644 --- a/pom.xml +++ b/pom.xml @@ -507,13 +507,6 @@ the software. - - - org.springframework.security.oauth - spring-security-oauth2 - 2.5.2.RELEASE - compile - org.springframework.security spring-security-acl From 23e3438c72af9ca11e866a04461b68f9947850ef Mon Sep 17 00:00:00 2001 From: amontenegro Date: Wed, 4 Mar 2026 14:12:27 -0600 Subject: [PATCH 02/16] Nothing works yet --- ...rcidClientCredentialEndPointDelegator.java | 16 - ...ClientCredentialEndPointDelegatorImpl.java | 358 ------------------ .../orcid-oauth2-api-common-config.xml | 80 +--- orcid-core/pom.xml | 4 + .../openid/OpenIDConnectDiscoveryService.java | 125 ------ .../oauth/openid/OpenIDConnectKeyService.java | 128 ------- .../openid/OpenIDConnectTokenEnhancer.java | 154 -------- .../OrcidRandomValueTokenServicesImpl.java | 14 - .../src/main/resources/orcid-core-context.xml | 22 +- .../web/controllers/OpenIDController.java | 72 ++-- .../resources/orcid-core-context-spam.xml | 20 +- 11 files changed, 38 insertions(+), 955 deletions(-) delete mode 100644 orcid-api-common/src/main/java/org/orcid/api/common/oauth/OrcidClientCredentialEndPointDelegator.java delete mode 100644 orcid-api-common/src/main/java/org/orcid/api/common/oauth/OrcidClientCredentialEndPointDelegatorImpl.java delete mode 100644 orcid-core/src/main/java/org/orcid/core/oauth/openid/OpenIDConnectDiscoveryService.java delete mode 100644 orcid-core/src/main/java/org/orcid/core/oauth/openid/OpenIDConnectKeyService.java delete mode 100644 orcid-core/src/main/java/org/orcid/core/oauth/openid/OpenIDConnectTokenEnhancer.java diff --git a/orcid-api-common/src/main/java/org/orcid/api/common/oauth/OrcidClientCredentialEndPointDelegator.java b/orcid-api-common/src/main/java/org/orcid/api/common/oauth/OrcidClientCredentialEndPointDelegator.java deleted file mode 100644 index ade7ee9897f..00000000000 --- a/orcid-api-common/src/main/java/org/orcid/api/common/oauth/OrcidClientCredentialEndPointDelegator.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.orcid.api.common.oauth; - -import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.core.Response; - -/** - * @author Declan Newman (declan) Date: 18/04/2012 - */ -public interface OrcidClientCredentialEndPointDelegator { - - Response obtainOauth2Token(String authorization, MultivaluedMap formParams); - - void setTokenCacheEnabled(boolean enabled); - - boolean isTokenCacheEnabled(); -} diff --git a/orcid-api-common/src/main/java/org/orcid/api/common/oauth/OrcidClientCredentialEndPointDelegatorImpl.java b/orcid-api-common/src/main/java/org/orcid/api/common/oauth/OrcidClientCredentialEndPointDelegatorImpl.java deleted file mode 100644 index df5f9b9dedf..00000000000 --- a/orcid-api-common/src/main/java/org/orcid/api/common/oauth/OrcidClientCredentialEndPointDelegatorImpl.java +++ /dev/null @@ -1,358 +0,0 @@ -package org.orcid.api.common.oauth; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import javax.annotation.Resource; -import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.core.Response; - -import org.apache.commons.lang.StringUtils; -import org.orcid.core.constants.OrcidOauth2Constants; -import org.orcid.core.exception.OrcidInvalidScopeException; -import org.orcid.core.locale.LocaleManager; -import org.orcid.core.oauth.OAuthError; -import org.orcid.core.oauth.OAuthErrorUtils; -import org.orcid.core.utils.JsonUtils; -import org.orcid.core.utils.cache.redis.RedisClient; -import org.orcid.jaxb.model.message.ScopePathType; -import org.orcid.persistence.dao.OrcidOauth2AuthoriziationCodeDetailDao; -import org.orcid.persistence.dao.ProfileLastModifiedDao; -import org.orcid.persistence.jpa.entities.IndexingStatus; -import org.orcid.persistence.jpa.entities.OrcidOauth2AuthoriziationCodeDetail; -import org.orcid.persistence.jpa.entities.OrcidOauth2TokenDetail; -import org.orcid.pojo.ajaxForm.PojoUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.security.authentication.InsufficientAuthenticationException; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; -import org.springframework.security.oauth2.common.OAuth2AccessToken; -import org.springframework.security.oauth2.common.exceptions.InvalidGrantException; -import org.springframework.security.oauth2.common.exceptions.UnsupportedGrantTypeException; -import org.springframework.security.oauth2.common.util.OAuth2Utils; -import org.springframework.security.oauth2.provider.AuthorizationRequest; -import org.springframework.security.oauth2.provider.TokenRequest; -import org.springframework.security.oauth2.provider.endpoint.AbstractEndpoint; -import org.springframework.stereotype.Component; -import org.springframework.transaction.annotation.Transactional; - -/** - * @author Declan Newman (declan) Date: 18/04/2012 - */ -@Component("orcidClientCredentialEndPointDelegator") -public class OrcidClientCredentialEndPointDelegatorImpl extends AbstractEndpoint implements OrcidClientCredentialEndPointDelegator { - - private static final Logger LOGGER = LoggerFactory.getLogger(OrcidClientCredentialEndPointDelegatorImpl.class); - - @Resource - private OrcidOauth2AuthoriziationCodeDetailDao orcidOauth2AuthoriziationCodeDetailDao; - - @Resource - protected LocaleManager localeManager; - - @Resource(name="profileLastModifiedDao") - private ProfileLastModifiedDao profileLastModifiedDao; - - @Resource - private RedisClient redisClient; - - @Value("${org.orcid.core.utils.cache.redis.enabled:true}") - private boolean isTokenCacheEnabled; - - @Transactional - public Response obtainOauth2Token(String authorization, MultivaluedMap formParams) { - String code = formParams.getFirst("code"); - String clientId = formParams.getFirst(OrcidOauth2Constants.CLIENT_ID_PARAM); - String state = formParams.getFirst(OrcidOauth2Constants.STATE_PARAM); - String redirectUri = formParams.getFirst(OrcidOauth2Constants.REDIRECT_URI_PARAM); - String refreshToken = formParams.getFirst(OrcidOauth2Constants.REFRESH_TOKEN); - String scopeList = formParams.getFirst(OrcidOauth2Constants.SCOPE_PARAM); - String grantType = formParams.getFirst(OrcidOauth2Constants.GRANT_TYPE); - Boolean revokeOld = formParams.containsKey(OrcidOauth2Constants.REVOKE_OLD) ? Boolean.valueOf(formParams.getFirst(OrcidOauth2Constants.REVOKE_OLD)) : true; - Long expiresIn = calculateExpiresIn(formParams); - //IETF Token exchange - String subjectToken = formParams.getFirst(OrcidOauth2Constants.IETF_EXCHANGE_SUBJECT_TOKEN); - String subjectTokenType = formParams.getFirst(OrcidOauth2Constants.IETF_EXCHANGE_SUBJECT_TOKEN_TYPE); - String requestedTokenType = formParams.getFirst(OrcidOauth2Constants.IETF_EXCHANGE_REQUESTED_TOKEN_TYPE); - - String bearerToken = null; - Set scopes = new HashSet(); - if (StringUtils.isNotEmpty(scopeList)) { - scopes = OAuth2Utils.parseParameterList(scopeList); - } - if(OrcidOauth2Constants.REFRESH_TOKEN.equals(grantType)) { - if(!PojoUtil.isEmpty(authorization)) { - if ((authorization.toLowerCase().startsWith(OAuth2AccessToken.BEARER_TYPE.toLowerCase()))) { - String authHeaderValue = authorization.substring(OAuth2AccessToken.BEARER_TYPE.length()).trim(); - int commaIndex = authHeaderValue.indexOf(','); - if (commaIndex > 0) { - authHeaderValue = authHeaderValue.substring(0, commaIndex); - } - bearerToken = authHeaderValue; - if(PojoUtil.isEmpty(bearerToken)) { - throw new IllegalArgumentException("Refresh token request doesnt include the authorization"); - } - } - } - } - - Authentication client = getClientAuthentication(); - if (!client.isAuthenticated()) { - LOGGER.error("Not authenticated for OAuth2: clientId={}, grantType={}, refreshToken={}, code={}, scopes={}, state={}, redirectUri={}", new Object[] { - clientId, grantType, refreshToken, code, scopes, state, redirectUri }); - throw new InsufficientAuthenticationException(localeManager.resolveMessage("apiError.client_not_authenticated.exception")); - } - - /** - * Patch, update any orcid-grants scope to funding scope - * */ - for (String scope : scopes) { - if (scope.contains("orcid-grants")) { - String newScope = scope.replace("orcid-grants", "funding"); - LOGGER.info("Client {} provided a grants scope {} which will be updated to {}", new Object[] { clientId, scope, newScope }); - scopes.remove(scope); - scopes.add(newScope); - } - } - - try { - if (scopes != null) { - List toRemove = new ArrayList(); - for (String scope : scopes) { - ScopePathType scopeType = ScopePathType.fromValue(scope); - if(scopeType.isInternalScope()) { - // You should not allow any internal scope here! go away! - String message = localeManager.resolveMessage("apiError.9015.developerMessage", new Object[]{}); - throw new OrcidInvalidScopeException(message, clientId, scope); - } else if(OrcidOauth2Constants.GRANT_TYPE_CLIENT_CREDENTIALS.equals(grantType)) { - if(!scopeType.isClientCreditalScope()) - toRemove.add(scope); - } else { - if(scopeType.isClientCreditalScope()) - toRemove.add(scope); - } - } - - for (String remove : toRemove) { - scopes.remove(remove); - } - } - } catch (IllegalArgumentException iae) { - String message = localeManager.resolveMessage("apiError.9015.developerMessage", new Object[]{}); - if(scopes != null) { - message += " Provided scopes: " + String.join(",", scopes); - } - throw new OrcidInvalidScopeException(message, clientId, iae.getMessage()); - } - - try{ - OAuth2AccessToken token = generateToken(client, scopes, code, redirectUri, grantType, refreshToken, state, bearerToken, revokeOld, expiresIn, subjectToken, subjectTokenType, requestedTokenType); - - //Lets check if the user needs to be indexed - if(token != null && token.getAdditionalInformation() != null) { - if(token.getAdditionalInformation().containsKey(OrcidOauth2Constants.ORCID)) { - String orcidId = (String) token.getAdditionalInformation().get(OrcidOauth2Constants.ORCID); - if(!PojoUtil.isEmpty(orcidId)) { - profileLastModifiedDao.updateIndexingStatus(Arrays.asList(orcidId), IndexingStatus.PENDING); - } - } - } - - // Do not put the token in the cache if the token is disabled - if(token.getAdditionalInformation() != null && !token.getAdditionalInformation().containsKey(OrcidOauth2Constants.TOKEN_DISABLED)) { - setToCache(client.getName(), token); - } - removeMetadataFromToken(token); - return getResponse(token); - } catch (InvalidGrantException e){ //this needs to be caught here so the transaction doesn't roll back - OAuthError error = OAuthErrorUtils.getOAuthError(e); - return Response.status(error.getResponseStatus().getStatusCode()).entity(error).build(); - } - } - - /** - * Set the access token in the cache - * */ - protected void setToCache(String clientId, OAuth2AccessToken accessToken) { - if(isTokenCacheEnabled) { - try { - String tokenValue = accessToken.getValue(); - Map tokenData = new HashMap(); - tokenData.put(OrcidOauth2Constants.ACCESS_TOKEN, tokenValue); - tokenData.put(OrcidOauth2Constants.TOKEN_EXPIRATION_TIME, String.valueOf(accessToken.getExpiration().getTime())); - StringBuilder sb = new StringBuilder(); - accessToken.getScope().forEach(x -> {sb.append(x); sb.append(' ');}); - tokenData.put(OrcidOauth2Constants.SCOPE_PARAM, sb.toString()); - tokenData.put(OrcidOauth2Constants.ORCID, (String) accessToken.getAdditionalInformation().get(OrcidOauth2Constants.ORCID)); - tokenData.put(OrcidOauth2Constants.CLIENT_ID, clientId); - tokenData.put(OrcidOauth2Constants.RESOURCE_IDS, OrcidOauth2Constants.ORCID); - tokenData.put(OrcidOauth2Constants.APPROVED, Boolean.TRUE.toString()); - if(accessToken.getAdditionalInformation().containsKey(OrcidOauth2Constants.IS_OBO_TOKEN)) { - tokenData.put(OrcidOauth2Constants.IS_OBO_TOKEN, Boolean.TRUE.toString()); - } - redisClient.set(tokenValue, JsonUtils.convertToJsonString(tokenData)); - } catch(Exception e) { - LOGGER.info("Unable to set token in Redis cache", e); - } - } - } - - /** - * Calculates the real value of the "expires_in" param based on the current time - * - * @param formParams - * The params container - * - * @return the expiration time in milliseconds based on the param OrcidOauth2Constants.EXPIRES_IN. - * @throws IllegalArgumentException in case the parameter is not a number - * */ - private Long calculateExpiresIn(MultivaluedMap formParams) { - if(!formParams.containsKey(OrcidOauth2Constants.EXPIRES_IN)){ - return 0L; - } - - String expiresInParam = formParams.getFirst(OrcidOauth2Constants.EXPIRES_IN); - Long result = 0L; - - try { - result = Long.valueOf(expiresInParam); - } catch(Exception e) { - throw new IllegalArgumentException(expiresInParam + " is not a number"); - } - - return result == 0 ? result : (System.currentTimeMillis() + (result * 1000)); - } - - protected OAuth2AccessToken generateToken(Authentication client, Set scopes, String grantType) { - return generateToken(client, scopes, null, null, grantType, null, null, null, false, 0L, null, null, null); - } - - protected OAuth2AccessToken generateToken(Authentication client, Set scopes, String code, String redirectUri, String grantType, String refreshToken, String state, String authorization, boolean revokeOld, Long expiresIn, String subjectToken, String subjectTokenType, String requestedTokenType) { - String clientId = client.getName(); - Map authorizationParameters = new HashMap(); - - if(scopes != null) { - String scopesString = StringUtils.join(scopes, ' '); - authorizationParameters.put(OAuth2Utils.SCOPE, scopesString); - } - - authorizationParameters.put(OAuth2Utils.CLIENT_ID, clientId); - if (code != null) { - authorizationParameters.put("code", code); - OrcidOauth2AuthoriziationCodeDetail authorizationCodeEntity = orcidOauth2AuthoriziationCodeDetailDao.find(code); - - if(authorizationCodeEntity != null) { - if(orcidOauth2AuthoriziationCodeDetailDao.isPersistentToken(code)) { - authorizationParameters.put(OrcidOauth2Constants.IS_PERSISTENT, "true"); - } else { - authorizationParameters.put(OrcidOauth2Constants.IS_PERSISTENT, "false"); - } - - if(!authorizationParameters.containsKey(OAuth2Utils.SCOPE) || PojoUtil.isEmpty(authorizationParameters.get(OAuth2Utils.SCOPE))) { - //////// - // TODO: The name should change to `scopes` once the authorization server generates all authorization codes - //////// - Set newScopes = StringUtils.isNotBlank(authorizationCodeEntity.getNewScopes()) ? Stream.of(authorizationCodeEntity.getNewScopes().split(",")).map(String::trim).collect(Collectors.toSet()) : Set.of(); - - Set allScopes = new HashSet(); - allScopes.addAll(newScopes); - allScopes.addAll(authorizationCodeEntity.getScopes()); - - String scopesString = StringUtils.join(allScopes, ' '); - authorizationParameters.put(OAuth2Utils.SCOPE, scopesString); - } - - //This will pass through to the token generator as a request param. - if (authorizationCodeEntity.getNonce() !=null){ - authorizationParameters.put(OrcidOauth2Constants.NONCE, authorizationCodeEntity.getNonce()); - } - } else { - authorizationParameters.put(OrcidOauth2Constants.IS_PERSISTENT, "false"); - } - } - - //If it is a refresh token request, set the needed authorization parameters - if(OrcidOauth2Constants.REFRESH_TOKEN.equals(grantType)) { - authorizationParameters.put(OrcidOauth2Constants.AUTHORIZATION, authorization); - authorizationParameters.put(OrcidOauth2Constants.REVOKE_OLD, String.valueOf(revokeOld)); - authorizationParameters.put(OrcidOauth2Constants.EXPIRES_IN, String.valueOf(expiresIn)); - authorizationParameters.put(OrcidOauth2Constants.REFRESH_TOKEN, String.valueOf(refreshToken)); - } else if (OrcidOauth2Constants.IETF_EXCHANGE_GRANT_TYPE.equals(grantType)) { - authorizationParameters.put(OrcidOauth2Constants.IETF_EXCHANGE_SUBJECT_TOKEN, String.valueOf(subjectToken)); - authorizationParameters.put(OrcidOauth2Constants.IETF_EXCHANGE_SUBJECT_TOKEN_TYPE, String.valueOf(subjectTokenType)); - authorizationParameters.put(OrcidOauth2Constants.IETF_EXCHANGE_REQUESTED_TOKEN_TYPE, String.valueOf(requestedTokenType)); - //required so OrcidRandomValueTokenServicesImpl doesn't generate a refresh token - authorizationParameters.put(OrcidOauth2Constants.GRANT_TYPE, OrcidOauth2Constants.IETF_EXCHANGE_GRANT_TYPE); - } - - if (redirectUri != null) { - authorizationParameters.put(OAuth2Utils.REDIRECT_URI, redirectUri); - } - AuthorizationRequest authorizationRequest = getOAuth2RequestFactory().createAuthorizationRequest(authorizationParameters); - - TokenRequest tokenRequest = getOAuth2RequestFactory().createTokenRequest(authorizationRequest, grantType); - //Need to change this to either the DefaultTokenType or start using a different token type. - OAuth2AccessToken token = getTokenGranter().grant(grantType, tokenRequest); - Object params[] = {grantType}; - if (token == null) { - LOGGER.error("Unsupported grant type for OAuth2: clientId={}, grantType={}, code={}", new Object[] { - clientId, grantType, code}); - throw new UnsupportedGrantTypeException(localeManager.resolveMessage("apiError.unsupported_client_type.exception", params)); - } - - Long tokenId = token.getAdditionalInformation() != null ? (Long) token.getAdditionalInformation().get(OrcidOauth2Constants.TOKEN_ID) : null; - - LOGGER.info("OAuth2 access token granted: tokenId={}, clientId={}, code={}, scopes={}", new Object[] { - tokenId, clientId, code, token.getScope() }); - - return token; - } - - protected void removeMetadataFromToken(OAuth2AccessToken accessToken) { - if(accessToken != null && accessToken.getAdditionalInformation() != null) { - if(accessToken.getAdditionalInformation().containsKey(OrcidOauth2Constants.TOKEN_VERSION)) - accessToken.getAdditionalInformation().remove(OrcidOauth2Constants.TOKEN_VERSION); - if(accessToken.getAdditionalInformation().containsKey(OrcidOauth2Constants.PERSISTENT)) - accessToken.getAdditionalInformation().remove(OrcidOauth2Constants.PERSISTENT); - if(accessToken.getAdditionalInformation().containsKey(OrcidOauth2Constants.DATE_CREATED)) - accessToken.getAdditionalInformation().remove(OrcidOauth2Constants.DATE_CREATED); - if(accessToken.getAdditionalInformation().containsKey(OrcidOauth2Constants.TOKEN_ID)) - accessToken.getAdditionalInformation().remove(OrcidOauth2Constants.TOKEN_ID); - if(accessToken.getAdditionalInformation().containsKey(OrcidOauth2Constants.TOKEN_DISABLED)) - accessToken.getAdditionalInformation().remove(OrcidOauth2Constants.TOKEN_DISABLED); - } - } - - protected Response getResponse(OAuth2AccessToken accessToken) { - return Response.ok((DefaultOAuth2AccessToken)accessToken).header("Cache-Control", "no-store").header("Pragma", "no-cache").build(); - } - - protected Authentication getClientAuthentication() { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - if (authentication != null) { - return authentication; - } else { - throw new InsufficientAuthenticationException(localeManager.resolveMessage("apiError.client_authentication_notfound.exception")); - } - - } - - public boolean isTokenCacheEnabled() { - return isTokenCacheEnabled; - } - - public void setTokenCacheEnabled(boolean isTokenCacheEnabled) { - this.isTokenCacheEnabled = isTokenCacheEnabled; - } - -} diff --git a/orcid-api-common/src/main/resources/orcid-oauth2-api-common-config.xml b/orcid-api-common/src/main/resources/orcid-oauth2-api-common-config.xml index 89682ff1873..75d7bacafd6 100644 --- a/orcid-api-common/src/main/resources/orcid-oauth2-api-common-config.xml +++ b/orcid-api-common/src/main/resources/orcid-oauth2-api-common-config.xml @@ -46,85 +46,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/orcid-core/pom.xml b/orcid-core/pom.xml index 2971359b4ce..cbfbebdab72 100644 --- a/orcid-core/pom.xml +++ b/orcid-core/pom.xml @@ -79,6 +79,10 @@ org.springframework spring-jdbc + + org.springframework + spring-web + org.freemarker freemarker diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/openid/OpenIDConnectDiscoveryService.java b/orcid-core/src/main/java/org/orcid/core/oauth/openid/OpenIDConnectDiscoveryService.java deleted file mode 100644 index 90d586e8525..00000000000 --- a/orcid-core/src/main/java/org/orcid/core/oauth/openid/OpenIDConnectDiscoveryService.java +++ /dev/null @@ -1,125 +0,0 @@ -package org.orcid.core.oauth.openid; - -import java.util.List; - -import org.springframework.beans.factory.annotation.Value; - -import com.google.common.collect.Lists; - -public class OpenIDConnectDiscoveryService { - - private OpenIDConnectDiscoveryServiceConfig config; - - public static class OpenIDConnectDiscoveryServiceConfig{ - @Value("${org.orcid.core.baseUri}") - private String path; - - private List token_endpoint_auth_signing_alg_values_supported = Lists.newArrayList("RS256"); - private List id_token_signing_alg_values_supported = Lists.newArrayList("RS256"); - private String userinfo_endpoint = "/oauth/userinfo"; - private String authorization_endpoint = "/oauth/authorize"; - private String token_endpoint = "/oauth/token"; - private String jwks_uri = "/oauth/jwks"; - private List claims_supported = Lists.newArrayList("family_name","given_name","name","auth_time","iss","sub"); - private List scopes_supported = Lists.newArrayList("openid"); - private List subject_types_supported = Lists.newArrayList("public"); - private List response_types_supported = Lists.newArrayList("code","id_token","id_token token"); - private Boolean claims_parameter_supported = false; - private List token_endpoint_auth_methods_supported = Lists.newArrayList("client_secret_post"); - private List grant_types_supported = Lists.newArrayList("authorization_code","implicit","refresh_token"); - public String getIssuer() { - return path; - } - public List getToken_endpoint_auth_signing_alg_values_supported() { - return token_endpoint_auth_signing_alg_values_supported; - } - public List getId_token_signing_alg_values_supported() { - return id_token_signing_alg_values_supported; - } - public String getUserinfo_endpoint() { - return path+userinfo_endpoint; - } - public String getAuthorization_endpoint() { - return path+authorization_endpoint; - } - public String getToken_endpoint() { - return path+token_endpoint; - } - public String getJwks_uri() { - return path+jwks_uri; - } - public List getClaims_supported() { - return claims_supported; - } - public List getScopes_supported() { - return scopes_supported; - } - public List getSubject_types_supported() { - return subject_types_supported; - } - public List getResponse_types_supported() { - return response_types_supported; - } - public Boolean getClaims_parameter_supported() { - return claims_parameter_supported; - } - public List getToken_endpoint_auth_methods_supported() { - return token_endpoint_auth_methods_supported; - } - public List getGrant_types_supported() { - return grant_types_supported; - } - - /* example: - * "provider_info":{ - "token_endpoint_auth_signing_alg_values_supported":[ - "RS256" - ], - "userinfo_endpoint":"https://qa.orcid.org/oauth/userinfo", - "authorization_endpoint":"https://qa.orcid.org/oauth/authorize", - "claims_supported":[ - "family_name", - "given_name", - "name", - "auth_time", - "iss", - "sub" - ], - "scopes_supported":[ - "openid" - ], - "grant_types_supported":[ - "authorization_code" - ], - "token_endpoint":"https://qa.orcid.org/oauth/token", - "id_token_signing_alg_values_supported":[ - "RS256" - ], - "subject_types_supported":[ - "public" - ], - "response_types_supported":[ - "code" - ], - "jwks_uri":"https://qa.orcid.org/oauth/jwks", - "claims_parameter_supported":"false", - "token_endpoint_auth_methods_supported":[ - "client_secret_basic" - ], - "claim_types_supported":[ - "normal" - ], - "issuer":"https://orcid.org" - } - - */ - } - - public OpenIDConnectDiscoveryService(OpenIDConnectDiscoveryServiceConfig config){ - this.config = config; - } - - public OpenIDConnectDiscoveryServiceConfig getConfig(){ - return config; - } -} diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/openid/OpenIDConnectKeyService.java b/orcid-core/src/main/java/org/orcid/core/oauth/openid/OpenIDConnectKeyService.java deleted file mode 100644 index 28e46200f25..00000000000 --- a/orcid-core/src/main/java/org/orcid/core/oauth/openid/OpenIDConnectKeyService.java +++ /dev/null @@ -1,128 +0,0 @@ -package org.orcid.core.oauth.openid; - -import java.io.File; -import java.io.IOException; -import java.net.URISyntaxException; -import java.security.NoSuchAlgorithmException; -import java.text.ParseException; - -import com.nimbusds.jose.JOSEException; -import com.nimbusds.jose.JWSAlgorithm; -import com.nimbusds.jose.JWSHeader; -import com.nimbusds.jose.JWSSigner; -import com.nimbusds.jose.JWSVerifier; -import com.nimbusds.jose.crypto.RSASSASigner; -import com.nimbusds.jose.crypto.RSASSAVerifier; -import com.nimbusds.jose.jwk.JWKSet; -import com.nimbusds.jose.jwk.RSAKey; -import com.nimbusds.jwt.JWTClaimsSet; -import com.nimbusds.jwt.SignedJWT; - -public class OpenIDConnectKeyService { - - private final String keyID; - private final RSAKey publicJWK; - private final RSAKey privateJWK; - private final JWSAlgorithm defaultAlg = JWSAlgorithm.RS256; - - public static class OpenIDConnectKeyServiceConfig{ - String jwksLocation; - public String getJwksLocation() { - return jwksLocation; - } - public void setJwksLocation(String jwksLocation) { - this.jwksLocation = jwksLocation; - } - public String getJsonKey() { - return jsonKey; - } - public void setJsonKey(String jsonKey) { - this.jsonKey = jsonKey; - } - public String getKeyName() { - return keyName; - } - public void setKeyName(String keyName) { - this.keyName = keyName; - } - String jsonKey; - String keyName; - } - - /** Use a configured key ${org.orcid.openid.jwksLocation} or ${org.orcid.openid.jwksTestKey} + ${org.orcid.openid.jwksKeyName} - * - * New keys can be generated using this: https://mkjwk.org/ or a command line tool found here: https://connect2id.com/products/nimbus-jose-jwt/generator - * @throws NoSuchAlgorithmException - * @throws ParseException - * @throws IOException - * @throws URISyntaxException - */ - public OpenIDConnectKeyService(OpenIDConnectKeyServiceConfig config) throws NoSuchAlgorithmException, IOException, ParseException, URISyntaxException{ - if (config.jwksLocation !=null && !config.jwksLocation.isEmpty() && config.keyName!=null && !config.keyName.isEmpty()){ - //use a configured key. - this.keyID = config.keyName; - JWKSet keys = JWKSet.load(new File(config.jwksLocation)); - privateJWK = (RSAKey) keys.getKeyByKeyId(keyID); - publicJWK = privateJWK.toPublicJWK(); - }else if (config.jsonKey!=null){ - //use a key embedded in the properties file - this.keyID = config.keyName; - JWKSet keys = JWKSet.parse(config.jsonKey); - privateJWK = (RSAKey) keys.getKeyByKeyId(keyID); - publicJWK = privateJWK.toPublicJWK(); - }else - throw new RuntimeException("OpenID jwks not configured!"); - } - - /** Get the private key for signing - * - * @return - * @throws JOSEException - */ - public SignedJWT sign(JWTClaimsSet claims) throws JOSEException{ - JWSSigner signer = new RSASSASigner(privateJWK); - JWSHeader.Builder head = new JWSHeader.Builder(defaultAlg); - head.keyID(getDefaultKeyID()); - SignedJWT signedJWT = new SignedJWT(head.build(), claims); - signedJWT.sign(signer); - return signedJWT; - - /* For HMAC we could do the following. This may be useful for the implicit flow: - ClientDetailsEntity clientEntity = clientDetailsEntityCacheManager.retrieve(authentication.getOAuth2Request().getClientId()); - JWSSigner signer = new MACSigner(StringUtils.rightPad(clientEntity.getDecryptedClientSecret(), 32, "#").getBytes()); - signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.HS256), claims.build()); - signedJWT.sign(signer); - */ - } - - /** Get the key ID we'll be using - * - * @return - */ - public String getDefaultKeyID(){ - return keyID; - } - - /** get the Json Web Key representation of the public key - * - * @return a JWK. use .toString() to generate JSON representation. - */ - public JWKSet getPublicJWK(){ - return new JWKSet(publicJWK); - } - - /** verify an id_token was signed by us - * - * @param signed - * @return - */ - public boolean verify(SignedJWT signed) { - try { - JWSVerifier verifier = new RSASSAVerifier(publicJWK); - return signed.verify(verifier); - } catch (JOSEException e) { - throw new RuntimeException(e); - } - } - -} diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/openid/OpenIDConnectTokenEnhancer.java b/orcid-core/src/main/java/org/orcid/core/oauth/openid/OpenIDConnectTokenEnhancer.java deleted file mode 100644 index 3d6931442c4..00000000000 --- a/orcid-core/src/main/java/org/orcid/core/oauth/openid/OpenIDConnectTokenEnhancer.java +++ /dev/null @@ -1,154 +0,0 @@ -package org.orcid.core.oauth.openid; - -import java.util.Calendar; -import java.util.Date; -import java.util.Map; -import java.util.UUID; - -import javax.annotation.Resource; - -import org.apache.commons.lang.StringUtils; -import org.orcid.core.constants.OrcidOauth2Constants; -import org.orcid.core.manager.ClientDetailsEntityCacheManager; -import org.orcid.core.manager.ProfileEntityCacheManager; -import org.orcid.core.manager.read_only.PersonDetailsManagerReadOnly; -import org.orcid.jaxb.model.clientgroup.ClientType; -import org.orcid.jaxb.model.message.ScopePathType; -import org.orcid.jaxb.model.record_v2.Person; -import org.orcid.persistence.jpa.entities.ClientDetailsEntity; -import org.orcid.persistence.jpa.entities.ProfileEntity; -import org.orcid.pojo.ajaxForm.PojoUtil; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.security.oauth2.common.OAuth2AccessToken; -import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.security.oauth2.provider.token.TokenEnhancer; - -import com.nimbusds.jose.JOSEException; -import com.nimbusds.jose.JWSAlgorithm; -import com.nimbusds.jwt.JWTClaimsSet; -import com.nimbusds.jwt.JWTClaimsSet.Builder; -import com.nimbusds.jwt.SignedJWT; -import com.nimbusds.oauth2.sdk.token.BearerAccessToken; -import com.nimbusds.openid.connect.sdk.claims.AccessTokenHash; - -/** - * This class creates and appends JWT id_tokens to the response. - * - * @author tom - * - */ -public class OpenIDConnectTokenEnhancer implements TokenEnhancer { - - @Value("${org.orcid.core.baseUri}") - private String path; - - @Resource - private ProfileEntityCacheManager profileEntityCacheManager; - - @Resource - private PersonDetailsManagerReadOnly personDetailsManagerReadOnly; - - @Resource - private ClientDetailsEntityCacheManager clientDetailsEntityCacheManager; - - @Resource - private OpenIDConnectKeyService keyManager; - - @Value("${org.orcid.core.token.read_validity_seconds:631138519}") - private int readValiditySeconds; - - @Override - public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) { - // We check for a nonce and max_age which are added back into request by - // OrcidClientCredentialEndPointDelegatorImpl - Map params = authentication.getOAuth2Request().getRequestParameters(); - - // only add if we're using openid scope. - // only add in implicit flow if response_type id_token is present - String scopes = params.get(OrcidOauth2Constants.SCOPE_PARAM); - if (PojoUtil.isEmpty(scopes) || !ScopePathType.getScopesFromSpaceSeparatedString(scopes).contains(ScopePathType.OPENID)) { - return accessToken; - } - // inject the OpenID Connect "id_token" (authn). This is distinct from - // the access token (authz), so is for transporting info to the client - // only - // this means we do not have to support using them for authentication - // purposes. Some APIs support it, but it is not part of the spec. - try { - String idTok = buildIdToken(accessToken, authentication.getName(), params.get(OrcidOauth2Constants.CLIENT_ID_PARAM),params.get(OrcidOauth2Constants.NONCE) ); - accessToken.getAdditionalInformation().put(OrcidOauth2Constants.ID_TOKEN, idTok); - } catch (JOSEException e) { - e.printStackTrace(); - } catch (Exception e) { - e.printStackTrace(); - } - - return accessToken; - - } - - public String buildIdToken(OAuth2AccessToken accessToken, String orcid, String clientID, String nonce) throws JOSEException { - Builder claims = new JWTClaimsSet.Builder(); - claims.audience(clientID); - claims.subject(orcid); - claims.issuer(path); - claims.claim("at_hash", createAccessTokenHash(accessToken.getValue())); - - Date now = new Date(); - Calendar twentyFourHrs = Calendar.getInstance(); - twentyFourHrs.setTime(now); - twentyFourHrs.add(Calendar.DAY_OF_YEAR, 1); - - claims.issueTime(now); - claims.expirationTime(twentyFourHrs.getTime()); - - claims.jwtID(UUID.randomUUID().toString()); - if (nonce != null) - claims.claim(OrcidOauth2Constants.NONCE, nonce); - - ProfileEntity e = profileEntityCacheManager.retrieve(orcid); - - claims.claim(OrcidOauth2Constants.AUTH_TIME, e.getLastLogin()); - - // If it is a member, include AMR - ClientDetailsEntity c = clientDetailsEntityCacheManager.retrieve(clientID); - - if (StringUtils.isNotEmpty(c.getClientType()) && !ClientType.PUBLIC_CLIENT.equals(ClientType.valueOf(c.getClientType()))) { - claims.claim(OrcidOauth2Constants.AUTHENTICATION_METHODS_REFERENCES, (e.getUsing2FA() ? OrcidOauth2Constants.AMR_MFA : OrcidOauth2Constants.AMR_PWD)); - } - - Person person = personDetailsManagerReadOnly.getPublicPersonDetails(orcid); - if (person.getName() != null) { - if (person.getName().getCreditName() != null) { - claims.claim("name", person.getName().getCreditName().getContent()); - } - if (person.getName().getFamilyName() != null) { - claims.claim("family_name", person.getName().getFamilyName().getContent()); - } - if (person.getName().getGivenNames() != null) { - claims.claim("given_name", person.getName().getGivenNames().getContent()); - } - } - - SignedJWT signedJWT = keyManager.sign(claims.build()); - return signedJWT.serialize(); - } - - /** - * Access Token hash value. If the ID Token is issued with an access_token - * in an Implicit Flow, this is REQUIRED, which is the case for this subset - * of OpenID Connect. Its value is the base64url encoding of the left-most - * half of the hash of the octets of the ASCII representation of the - * access_token value, where the hash algorithm used is the hash algorithm - * used in the alg Header Parameter of the ID Token's JOSE Header. For - * instance, if the alg is RS256, hash the access_token value with SHA-256, - * then take the left-most 128 bits and base64url-encode them. The at_hash - * value is a case-sensitive string. - * - * @param accessToken - * @return - */ - private String createAccessTokenHash(String accessToken) { - return AccessTokenHash.compute(new BearerAccessToken(accessToken), JWSAlgorithm.RS256).toString(); - } -} diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidRandomValueTokenServicesImpl.java b/orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidRandomValueTokenServicesImpl.java index 7ea84be6b88..dd1755d9001 100644 --- a/orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidRandomValueTokenServicesImpl.java +++ b/orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidRandomValueTokenServicesImpl.java @@ -35,20 +35,6 @@ import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; -import org.springframework.security.oauth2.common.DefaultOAuth2RefreshToken; -import org.springframework.security.oauth2.common.OAuth2AccessToken; -import org.springframework.security.oauth2.common.OAuth2RefreshToken; -import org.springframework.security.oauth2.common.exceptions.InvalidGrantException; -import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; -import org.springframework.security.oauth2.common.util.OAuth2Utils; -import org.springframework.security.oauth2.provider.AuthorizationRequest; -import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.security.oauth2.provider.OAuth2Request; -import org.springframework.security.oauth2.provider.TokenRequest; -import org.springframework.security.oauth2.provider.token.AuthenticationKeyGenerator; -import org.springframework.security.oauth2.provider.token.DefaultTokenServices; -import org.springframework.security.oauth2.provider.token.TokenEnhancer; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.context.request.RequestContextHolder; diff --git a/orcid-core/src/main/resources/orcid-core-context.xml b/orcid-core/src/main/resources/orcid-core-context.xml index 78fbe4c6335..9b5db759fcc 100644 --- a/orcid-core/src/main/resources/orcid-core-context.xml +++ b/orcid-core/src/main/resources/orcid-core-context.xml @@ -50,17 +50,6 @@ - - - - - - - - - - - @@ -393,7 +382,7 @@ - + @@ -694,7 +683,6 @@ - @@ -782,8 +770,6 @@ - - @@ -791,8 +777,7 @@ - - + @@ -1227,8 +1212,7 @@ - - + diff --git a/orcid-web/src/main/java/org/orcid/frontend/web/controllers/OpenIDController.java b/orcid-web/src/main/java/org/orcid/frontend/web/controllers/OpenIDController.java index 7b589d15088..f1a45e11595 100644 --- a/orcid-web/src/main/java/org/orcid/frontend/web/controllers/OpenIDController.java +++ b/orcid-web/src/main/java/org/orcid/frontend/web/controllers/OpenIDController.java @@ -7,12 +7,9 @@ import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; -import org.orcid.core.manager.v3.read_only.PersonDetailsManagerReadOnly; -import org.orcid.core.oauth.openid.OpenIDConnectDiscoveryService; -import org.orcid.core.oauth.openid.OpenIDConnectKeyService; +import org.orcid.core.oauth.authorizationServer.AuthorizationServerUtil; import org.orcid.core.oauth.openid.OpenIDConnectUserInfo; import org.orcid.jaxb.model.message.ScopePathType; -import org.orcid.jaxb.model.v3.release.record.Person; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -21,26 +18,19 @@ import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonInclude.Include; -import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.ObjectWriter; import net.minidev.json.JSONObject; @Controller public class OpenIDController { - - @Resource - private OpenIDConnectKeyService openIDConnectKeyService; @Resource(name = "personDetailsManagerReadOnlyV3") private PersonDetailsManagerReadOnly personDetailsManagerReadOnly; - @Resource OpenIDConnectDiscoveryService openIDConnectDiscoveryService; - + @Resource + private AuthorizationServerUtil authorizationServerUtil; + @Value("${org.orcid.core.baseUri}") private String path; @@ -54,7 +44,8 @@ public class OpenIDController { */ @RequestMapping(value = "/oauth/jwks", method = RequestMethod.GET, produces = "application/json") public @ResponseBody JSONObject getJWKS(HttpServletRequest request) { - return openIDConnectKeyService.getPublicJWK().toJSONObject(); + //TODO: This should be a FW proxy to the auth server, maybe from nginx + throw new UnsupportedOperationException("Should be requested from the auth server"); } /** Manually checks bearer token in header, looks up user or throws 403. @@ -88,21 +79,28 @@ public class OpenIDController { private OpenIDConnectUserInfo getInfoFromToken(String tokenValue) { //TODO: Refactor this to get the information from the database // This is unexpected and should be done with token introspection form the oauth server - OAuth2AccessToken tok = tokenStore.readAccessToken(tokenValue); - if (tok != null && !tok.isExpired()){ - boolean hasScope = false; - Set requestedScopes = ScopePathType.getScopesFromStrings(tok.getScope()); - for (ScopePathType scope : requestedScopes) { - if (scope.hasScope(ScopePathType.AUTHENTICATE)) { - hasScope = true; + JSONObject tokenInfo = authorizationServerUtil.tokenIntrospection(tokenValue); + if(tokenInfo == null) { + return null; + } + + boolean isTokenActive = tokenInfo.getBoolean("active"); + + if(isTokenActive) { + // If the token is user revoked it might be used for DELETE requests + Set scopes = OAuth2Utils.parseParameterList(tokenInfo.getString("scope")); + OpenIDConnectUserInfo info = scopes.stream().forEach(s -> + { + ScopePathType scope = ScopePathType.fromValue(s); + if(scope.hasScope(ScopePathType.AUTHENTICATE)) { + OpenIDConnectUserInfo t = new OpenIDConnectUserInfo(); + //TODO set data to the token + //TODO the name doesn't come in the token introspection data, so, we should get it from the names cache + return t; } - } - if (hasScope){ - String orcid = tok.getAdditionalInformation().get("orcid").toString(); - Person person = personDetailsManagerReadOnly.getPublicPersonDetails(orcid); - return new OpenIDConnectUserInfo(orcid,person,path); - } - } + }); + return info; + } return null; } @@ -115,20 +113,8 @@ private OpenIDConnectUserInfo getInfoFromToken(String tokenValue) { */ @RequestMapping(value = "/.well-known/openid-configuration", method = RequestMethod.GET, produces = "application/json") public @ResponseBody String getOpenIDDiscovery(HttpServletRequest request) throws JsonProcessingException { - ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter(); - String json = ow.writeValueAsString(openIDConnectDiscoveryService.getConfig()); - return json; + //TODO: This should be a FW proxy to the auth server, maybe from nginx + throw new UnsupportedOperationException("Should be requested from the auth server"); } - - @JsonInclude(Include.NON_NULL) - public static class OpenIDConnectUserInfoAccessDenied extends OpenIDConnectUserInfo{ - @JsonProperty("error") - String error = "access_denied"; - @JsonProperty("error-description") - String errorDescription="access_token is invalid"; - OpenIDConnectUserInfoAccessDenied(){ - - } - } } diff --git a/orcid-web/src/main/resources/orcid-core-context-spam.xml b/orcid-web/src/main/resources/orcid-core-context-spam.xml index 5581f7b200d..08de0c879d9 100644 --- a/orcid-web/src/main/resources/orcid-core-context-spam.xml +++ b/orcid-web/src/main/resources/orcid-core-context-spam.xml @@ -25,10 +25,6 @@ - - - - @@ -40,17 +36,6 @@ - - - - - - - - - - - @@ -759,8 +744,6 @@ - - @@ -769,8 +752,7 @@ - - + From aa5d92c9e2b12c34f8415c9e5d2977a65d48bf69 Mon Sep 17 00:00:00 2001 From: amontenegro Date: Fri, 20 Mar 2026 14:19:54 -0600 Subject: [PATCH 03/16] Removing some dependencies --- .../api/common/jaxb/OrcidExceptionMapper.java | 10 - orcid-core/pom.xml | 5 + .../exception/OrcidCoreExceptionMapper.java | 2 - .../core/manager/ProfileEntityManager.java | 4 +- .../ClientDetailsEntityCacheManagerImpl.java | 6 +- .../impl/ProfileEntityManagerImpl.java | 9 - .../ClientDetailsManagerReadOnly.java | 3 +- .../ClientDetailsManagerReadOnlyImpl.java | 27 - .../ClientDetailsManagerReadOnly.java | 3 +- .../ClientDetailsManagerReadOnlyImpl.java | 27 - .../org/orcid/core/oauth/OAuthErrorUtils.java | 16 - .../core/utils/SecurityContextTestUtils.java | 2 - .../service/ClientDetailsManagerTest.java | 28 +- .../frontend/oauth2/OauthController.java | 488 ------------------ .../frontend/oauth2/RevokeController.java | 58 +-- .../web/controllers/LoginController.java | 35 +- 16 files changed, 24 insertions(+), 699 deletions(-) delete mode 100644 orcid-web/src/main/java/org/orcid/frontend/oauth2/OauthController.java diff --git a/orcid-api-common/src/main/java/org/orcid/api/common/jaxb/OrcidExceptionMapper.java b/orcid-api-common/src/main/java/org/orcid/api/common/jaxb/OrcidExceptionMapper.java index c6423c3e94b..ee9f3285b6e 100644 --- a/orcid-api-common/src/main/java/org/orcid/api/common/jaxb/OrcidExceptionMapper.java +++ b/orcid-api-common/src/main/java/org/orcid/api/common/jaxb/OrcidExceptionMapper.java @@ -41,9 +41,6 @@ import org.springframework.context.MessageSource; import org.springframework.security.authentication.DisabledException; import org.springframework.security.core.AuthenticationException; -import org.springframework.security.oauth2.common.exceptions.InvalidClientException; -import org.springframework.security.oauth2.common.exceptions.OAuth2Exception; -import org.springframework.security.oauth2.common.exceptions.RedirectMismatchException; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; @@ -112,8 +109,6 @@ public Response toResponse(Throwable t) { logShortError(t, clientId); } else if (t instanceof OrcidBadRequestException) { logShortError(t, clientId); - } else if (t instanceof RedirectMismatchException) { - logShortError(t, clientId); } else if (t instanceof DuplicatedGroupIdRecordException) { logShortError(t, clientId); } else if (t instanceof OrcidNotificationException) { @@ -138,8 +133,6 @@ public Response toResponse(Throwable t) { logShortError(t, clientId); } else if(t instanceof StartDateAfterEndDateException) { logShortError(t, clientId); - } else if(t instanceof InvalidClientException) { - logShortError(t, clientId); } else if (t instanceof SearchStartParameterLimitExceededException) { logShortError(t, clientId); } else if (t instanceof InvalidDisambiguatedOrgException) { @@ -208,9 +201,6 @@ private Response legacyErrorResponse(Throwable t) { } else if (AuthenticationException.class.isAssignableFrom(t.getClass())) { OrcidMessage entity = getLegacyOrcidEntity("Authentication problem : ", t); return Response.status(Response.Status.UNAUTHORIZED).entity(entity).build(); - } else if (OAuth2Exception.class.isAssignableFrom(t.getClass())) { - OrcidMessage entity = getLegacyOrcidEntity("OAuth2 problem : ", t); - return Response.status(Response.Status.UNAUTHORIZED).entity(entity).build(); } else if (OrcidInvalidScopeException.class.isAssignableFrom(t.getClass())) { OrcidMessage entity = getLegacyOrcidEntity("OAuth2 problem : ", t); return Response.status(Response.Status.UNAUTHORIZED).entity(entity).build(); diff --git a/orcid-core/pom.xml b/orcid-core/pom.xml index cbfbebdab72..85eb46b76fd 100644 --- a/orcid-core/pom.xml +++ b/orcid-core/pom.xml @@ -82,6 +82,11 @@ org.springframework spring-web + 5.3.20 + + + org.springframework.security + spring-security-web org.freemarker diff --git a/orcid-core/src/main/java/org/orcid/core/exception/OrcidCoreExceptionMapper.java b/orcid-core/src/main/java/org/orcid/core/exception/OrcidCoreExceptionMapper.java index d09e1f386e6..69b63df304d 100644 --- a/orcid-core/src/main/java/org/orcid/core/exception/OrcidCoreExceptionMapper.java +++ b/orcid-core/src/main/java/org/orcid/core/exception/OrcidCoreExceptionMapper.java @@ -20,7 +20,6 @@ import org.orcid.jaxb.model.v3.release.error.OrcidError; import org.springframework.context.MessageSource; import org.springframework.security.core.AuthenticationException; -import org.springframework.security.oauth2.common.exceptions.OAuth2Exception; import com.fasterxml.jackson.core.JsonParseException; @@ -83,7 +82,6 @@ public class OrcidCoreExceptionMapper { // 401 HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE.put(AuthenticationException.class, new ImmutablePair<>(Response.Status.UNAUTHORIZED, 9002)); - HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE.put(OAuth2Exception.class, new ImmutablePair<>(Response.Status.UNAUTHORIZED, 9003)); HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE.put(OrcidUnauthorizedException.class, new ImmutablePair<>(Response.Status.UNAUTHORIZED, 9017)); HTTP_STATUS_AND_ERROR_CODE_BY_THROWABLE_TYPE.put(OrcidInvalidScopeException.class, new ImmutablePair<>(Response.Status.UNAUTHORIZED, 9015)); diff --git a/orcid-core/src/main/java/org/orcid/core/manager/ProfileEntityManager.java b/orcid-core/src/main/java/org/orcid/core/manager/ProfileEntityManager.java index 5a2eb26c5c5..f048f069e74 100644 --- a/orcid-core/src/main/java/org/orcid/core/manager/ProfileEntityManager.java +++ b/orcid-core/src/main/java/org/orcid/core/manager/ProfileEntityManager.java @@ -19,7 +19,7 @@ public interface ProfileEntityManager extends ProfileEntityManagerReadOnly { boolean hasBeenGivenPermissionTo(String giverOrcid, String receiverOrcid); - boolean existsAndNotClaimedAndBelongsTo(String messageOrcid, String clientId); + boolean existsAndNotClaimedAndBelongsTo(String messageOrcid, String clientId); boolean isProfileDeprecated(String orcid); @@ -39,8 +39,6 @@ public interface ProfileEntityManager extends ProfileEntityManagerReadOnly { boolean reviewProfile(String orcid); - void disableApplication(Long tokenId, String userOrcid); - String getOrcidHash(String orcid) throws NoSuchAlgorithmException; String retrivePublicDisplayName(String orcid); diff --git a/orcid-core/src/main/java/org/orcid/core/manager/impl/ClientDetailsEntityCacheManagerImpl.java b/orcid-core/src/main/java/org/orcid/core/manager/impl/ClientDetailsEntityCacheManagerImpl.java index b1e331c8235..3ddaae1dbbd 100644 --- a/orcid-core/src/main/java/org/orcid/core/manager/impl/ClientDetailsEntityCacheManagerImpl.java +++ b/orcid-core/src/main/java/org/orcid/core/manager/impl/ClientDetailsEntityCacheManagerImpl.java @@ -11,7 +11,6 @@ import org.orcid.core.utils.ReleaseNameUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.security.oauth2.common.exceptions.InvalidClientException; public class ClientDetailsEntityCacheManagerImpl implements ClientDetailsEntityCacheManager { @@ -26,20 +25,19 @@ public class ClientDetailsEntityCacheManagerImpl implements ClientDetailsEntityC @Resource(name = "clientDetailsEntityIdPCache") private Cache clientDetailsIdPCache; - private String releaseName = ReleaseNameUtils.getReleaseName(); + private final String releaseName = ReleaseNameUtils.getReleaseName(); @Override public ClientDetailsEntity retrieve(String clientId) throws IllegalArgumentException { Object key = new ClientIdCacheKey(clientId, releaseName); Date dbDate = retrieveLastModifiedDate(clientId); - ; ClientDetailsEntity clientDetails = clientDetailsCache.get(key); if (needsFresh(dbDate, clientDetails)) { clientDetails = clientDetailsCache.get(key); if (needsFresh(dbDate, clientDetails)) { clientDetails = clientDetailsManager.findByClientId(clientId); if (clientDetails == null) - throw new InvalidClientException("Client not found: " + clientId); + throw new IllegalArgumentException("Client not found: " + clientId); clientDetailsCache.put(key, clientDetails); } } diff --git a/orcid-core/src/main/java/org/orcid/core/manager/impl/ProfileEntityManagerImpl.java b/orcid-core/src/main/java/org/orcid/core/manager/impl/ProfileEntityManagerImpl.java index e711732854a..e21af5b6f5d 100644 --- a/orcid-core/src/main/java/org/orcid/core/manager/impl/ProfileEntityManagerImpl.java +++ b/orcid-core/src/main/java/org/orcid/core/manager/impl/ProfileEntityManagerImpl.java @@ -13,7 +13,6 @@ import org.orcid.core.manager.read_only.RecordNameManagerReadOnly; import org.orcid.core.manager.read_only.impl.ProfileEntityManagerReadOnlyImpl; import org.orcid.core.manager.v3.EmailManager; -import org.orcid.core.oauth.OrcidOauth2TokenDetailService; import org.orcid.jaxb.model.clientgroup.MemberType; import org.orcid.jaxb.model.common_v2.Locale; import org.orcid.jaxb.model.record_v2.Name; @@ -33,9 +32,6 @@ public class ProfileEntityManagerImpl extends ProfileEntityManagerReadOnlyImpl i @Resource(name = "emailManagerV3") private EmailManager emailManagerV3; - @Resource - private OrcidOauth2TokenDetailService orcidOauth2TokenService; - @Resource private RecordNameManager recordNameManager; @@ -132,11 +128,6 @@ public boolean unreviewProfile(String orcid) { return profileDao.unreviewProfile(orcid); } - @Override - public void disableApplication(Long tokenId, String userOrcid) { - orcidOauth2TokenService.disableAccessToken(tokenId, userOrcid); - } - @Override public String getOrcidHash(String orcid) throws NoSuchAlgorithmException { if (PojoUtil.isEmpty(orcid)) { diff --git a/orcid-core/src/main/java/org/orcid/core/manager/read_only/ClientDetailsManagerReadOnly.java b/orcid-core/src/main/java/org/orcid/core/manager/read_only/ClientDetailsManagerReadOnly.java index 8cbdf99badd..af9d9e67fd3 100644 --- a/orcid-core/src/main/java/org/orcid/core/manager/read_only/ClientDetailsManagerReadOnly.java +++ b/orcid-core/src/main/java/org/orcid/core/manager/read_only/ClientDetailsManagerReadOnly.java @@ -4,9 +4,8 @@ import java.util.List; import org.orcid.persistence.jpa.entities.ClientDetailsEntity; -import org.springframework.security.oauth2.provider.ClientDetailsService; -public interface ClientDetailsManagerReadOnly extends ClientDetailsService { +public interface ClientDetailsManagerReadOnly { ClientDetailsEntity findByClientId(String orcid); List getAll(); diff --git a/orcid-core/src/main/java/org/orcid/core/manager/read_only/impl/ClientDetailsManagerReadOnlyImpl.java b/orcid-core/src/main/java/org/orcid/core/manager/read_only/impl/ClientDetailsManagerReadOnlyImpl.java index fed04a51b54..10029e48d82 100644 --- a/orcid-core/src/main/java/org/orcid/core/manager/read_only/impl/ClientDetailsManagerReadOnlyImpl.java +++ b/orcid-core/src/main/java/org/orcid/core/manager/read_only/impl/ClientDetailsManagerReadOnlyImpl.java @@ -17,8 +17,6 @@ import org.orcid.persistence.jpa.entities.ClientDetailsEntity; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.security.oauth2.common.exceptions.InvalidClientException; -import org.springframework.security.oauth2.common.exceptions.OAuth2Exception; public class ClientDetailsManagerReadOnlyImpl implements ClientDetailsManagerReadOnly { @@ -49,31 +47,6 @@ public void setClientSecretDao(ClientSecretDao clientSecretDao) { public void setClientRedirectDao(ClientRedirectDao clientRedirectDao) { this.clientRedirectDao = clientRedirectDao; } - - /** - * Load a client by the client id. This method must NOT return null. - * - * @param clientId - * The client id. - * @return The client details. - * @throws org.springframework.security.oauth2.common.exceptions.OAuth2Exception - * If the client account is locked, expired, disabled, or for - * any other reason. - */ - @Override - public ClientDetailsEntity loadClientByClientId(String clientId) throws OAuth2Exception { - ClientDetailsEntity clientDetails = findByClientId(clientId); - if (clientDetails != null) { - if (!clientDetails.getClientId().equals(clientId)) - LOGGER.error("Client getClientId doesn't match. Requested: " + clientId + " Returned: " + clientDetails.getClientId()); - if (!clientDetails.getId().equals(clientId)) - LOGGER.error("Client getId() doesn't match. Requested: " + clientId + " Returned: " + clientDetails.getId()); - clientDetails.setDecryptedClientSecret(encryptionManager.decryptForInternalUse(clientDetails.getClientSecretForJpa())); - return clientDetails; - } else { - throw new InvalidClientException("Client not found: " + clientId); - } - } @Override public ClientDetailsEntity findByClientId(String clientId) { diff --git a/orcid-core/src/main/java/org/orcid/core/manager/v3/read_only/ClientDetailsManagerReadOnly.java b/orcid-core/src/main/java/org/orcid/core/manager/v3/read_only/ClientDetailsManagerReadOnly.java index 74c2b38e6b5..ba6ded14b2d 100644 --- a/orcid-core/src/main/java/org/orcid/core/manager/v3/read_only/ClientDetailsManagerReadOnly.java +++ b/orcid-core/src/main/java/org/orcid/core/manager/v3/read_only/ClientDetailsManagerReadOnly.java @@ -5,9 +5,8 @@ import org.orcid.jaxb.model.v3.release.client.ClientSummary; import org.orcid.persistence.jpa.entities.ClientDetailsEntity; -import org.springframework.security.oauth2.provider.ClientDetailsService; -public interface ClientDetailsManagerReadOnly extends ClientDetailsService { +public interface ClientDetailsManagerReadOnly { ClientDetailsEntity findByClientId(String orcid); List getAll(); diff --git a/orcid-core/src/main/java/org/orcid/core/manager/v3/read_only/impl/ClientDetailsManagerReadOnlyImpl.java b/orcid-core/src/main/java/org/orcid/core/manager/v3/read_only/impl/ClientDetailsManagerReadOnlyImpl.java index 50819febf2b..2a03d5d22da 100644 --- a/orcid-core/src/main/java/org/orcid/core/manager/v3/read_only/impl/ClientDetailsManagerReadOnlyImpl.java +++ b/orcid-core/src/main/java/org/orcid/core/manager/v3/read_only/impl/ClientDetailsManagerReadOnlyImpl.java @@ -18,8 +18,6 @@ import org.orcid.persistence.jpa.entities.ClientDetailsEntity; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.security.oauth2.common.exceptions.InvalidClientException; -import org.springframework.security.oauth2.common.exceptions.OAuth2Exception; public class ClientDetailsManagerReadOnlyImpl implements ClientDetailsManagerReadOnly { @@ -51,31 +49,6 @@ public void setClientRedirectDao(ClientRedirectDao clientRedirectDao) { this.clientRedirectDao = clientRedirectDao; } - /** - * Load a client by the client id. This method must NOT return null. - * - * @param clientId - * The client id. - * @return The client details. - * @throws org.springframework.security.oauth2.common.exceptions.OAuth2Exception - * If the client account is locked, expired, disabled, or for - * any other reason. - */ - @Override - public ClientDetailsEntity loadClientByClientId(String clientId) throws OAuth2Exception { - ClientDetailsEntity clientDetails = findByClientId(clientId); - if (clientDetails != null) { - if (!clientDetails.getClientId().equals(clientId)) - LOGGER.error("Client getClientId doesn't match. Requested: " + clientId + " Returned: " + clientDetails.getClientId()); - if (!clientDetails.getId().equals(clientId)) - LOGGER.error("Client getId() doesn't match. Requested: " + clientId + " Returned: " + clientDetails.getId()); - clientDetails.setDecryptedClientSecret(encryptionManager.decryptForInternalUse(clientDetails.getClientSecretForJpa())); - return clientDetails; - } else { - throw new InvalidClientException("Client not found: " + clientId); - } - } - @Override public ClientDetailsEntity findByClientId(String clientId) { ClientDetailsEntity result = null; diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/OAuthErrorUtils.java b/orcid-core/src/main/java/org/orcid/core/oauth/OAuthErrorUtils.java index 83548f13281..a63b7f07347 100644 --- a/orcid-core/src/main/java/org/orcid/core/oauth/OAuthErrorUtils.java +++ b/orcid-core/src/main/java/org/orcid/core/oauth/OAuthErrorUtils.java @@ -9,10 +9,6 @@ import org.orcid.core.exception.OrcidDeprecatedException; import org.orcid.core.exception.OrcidInvalidScopeException; import org.springframework.security.authentication.InsufficientAuthenticationException; -import org.springframework.security.oauth2.common.exceptions.InvalidGrantException; -import org.springframework.security.oauth2.common.exceptions.InvalidScopeException; -import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; -import org.springframework.security.oauth2.common.exceptions.UnsupportedGrantTypeException; public class OAuthErrorUtils { @@ -25,33 +21,21 @@ public static OAuthError getOAuthError(Throwable t) { } else if (ClientDeactivatedException.class.isAssignableFrom(t.getClass())) { error.setError(OAuthError.UNAUTHORIZED_CLIENT); error.setResponseStatus(Status.BAD_REQUEST); - } else if (UnsupportedGrantTypeException.class.isAssignableFrom(t.getClass())) { - error.setError(OAuthError.UNSUPPORTED_GRANT_TYPE); - error.setResponseStatus(Status.BAD_REQUEST); } else if (OrcidInvalidScopeException.class.isAssignableFrom(t.getClass())) { error.setError(OAuthError.INVALID_SCOPE); error.setResponseStatus(Status.UNAUTHORIZED); - } else if (InvalidScopeException.class.isAssignableFrom(t.getClass())) { - error.setError(OAuthError.INVALID_SCOPE); - error.setResponseStatus(Status.BAD_REQUEST); } else if (InsufficientAuthenticationException.class.isAssignableFrom(t.getClass())) { error.setError(OAuthError.UNAUTHORIZED_CLIENT); error.setResponseStatus(Status.UNAUTHORIZED); } else if (IllegalArgumentException.class.isAssignableFrom(t.getClass())) { error.setError(OAuthError.INVALID_REQUEST); error.setResponseStatus(Status.BAD_REQUEST); - } else if (InvalidGrantException.class.isAssignableFrom(t.getClass())) { - error.setError(OAuthError.INVALID_GRANT); - error.setResponseStatus(Status.BAD_REQUEST); } else if(DeactivatedException.class.isAssignableFrom(t.getClass())) { error.setError(OAuthError.UNAUTHORIZED_CLIENT); error.setResponseStatus(Status.BAD_REQUEST); } else if(OrcidDeprecatedException.class.isAssignableFrom(t.getClass())) { error.setError(OAuthError.UNAUTHORIZED_CLIENT); error.setResponseStatus(Status.BAD_REQUEST); - } else if (InvalidTokenException.class.isAssignableFrom(t.getClass())) { - error.setError(OAuthError.UNAUTHORIZED_CLIENT); - error.setResponseStatus(Status.BAD_REQUEST); } else if(MissingArgumentException.class.isAssignableFrom(t.getClass())) { error.setError(OAuthError.UNAUTHORIZED_CLIENT); error.setResponseStatus(Status.BAD_REQUEST); diff --git a/orcid-core/src/main/java/org/orcid/core/utils/SecurityContextTestUtils.java b/orcid-core/src/main/java/org/orcid/core/utils/SecurityContextTestUtils.java index fc41cd175ea..8a6510a3387 100644 --- a/orcid-core/src/main/java/org/orcid/core/utils/SecurityContextTestUtils.java +++ b/orcid-core/src/main/java/org/orcid/core/utils/SecurityContextTestUtils.java @@ -18,8 +18,6 @@ import org.springframework.security.core.context.SecurityContextImpl; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.oauth2.provider.OAuth2Request; -import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails; public class SecurityContextTestUtils { diff --git a/orcid-core/src/test/java/org/orcid/core/oauth/service/ClientDetailsManagerTest.java b/orcid-core/src/test/java/org/orcid/core/oauth/service/ClientDetailsManagerTest.java index a31dc0cc51d..b80f0457509 100644 --- a/orcid-core/src/test/java/org/orcid/core/oauth/service/ClientDetailsManagerTest.java +++ b/orcid-core/src/test/java/org/orcid/core/oauth/service/ClientDetailsManagerTest.java @@ -25,7 +25,6 @@ import org.orcid.test.DBUnitTest; import org.orcid.test.OrcidJUnit4ClassRunner; import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.oauth2.provider.ClientDetails; import org.springframework.test.annotation.Rollback; import org.springframework.test.context.ContextConfiguration; import org.springframework.transaction.annotation.Transactional; @@ -58,24 +57,6 @@ public static void removeDBUnitData() throws Exception { removeDBUnitData(Arrays.asList("/data/ClientDetailsEntityData.xml", "/data/ProfileEntityData.xml")); } - @Test - @Rollback - @Transactional - public void testLoadClientByClientId() throws Exception { - List all = clientDetailsManager.getAll(); - String[] clientExceptionList = new String[] { "APP-5555555555555555", "APP-5555555555555556", "APP-5555555555555557", "APP-5555555555555558", - "APP-6666666666666666" }; - assertEquals(12, all.size()); - for (ClientDetailsEntity clientDetailsEntity : all) { - ClientDetails clientDetails = clientDetailsManager.loadClientByClientId(clientDetailsEntity.getId()); - assertNotNull(clientDetails); - boolean exceptionClients = Arrays.stream(clientExceptionList).anyMatch(x -> x.equals(clientDetailsEntity.getId())); - if (!exceptionClients) { - checkClientDetails(clientDetails); - } - } - } - @Test @Rollback @Transactional @@ -152,20 +133,15 @@ private void checkClientDetails(ClientDetailsEntity clientDetails) { assertNotNull(clientDetails); assertEquals(clientDetails.getClientDescription(), CLIENT_DESCRIPTION); assertEquals(clientDetails.getClientName(), CLIENT_NAME); - checkClientDetails((ClientDetails) clientDetails); - } - - private void checkClientDetails(ClientDetails clientDetails) { String clientId = clientDetails.getClientId(); assertNotNull(clientId); Set registeredRedirectUris = clientDetails.getRegisteredRedirectUri(); assertNotNull(registeredRedirectUris); - if (clientDetails.getClientId().equals("4444-4444-4444-4445") || clientDetails.getClientId().equals("4444-4444-4444-4498")) { + if (clientId.equals("4444-4444-4444-4445") || clientId.equals("4444-4444-4444-4498")) { assertEquals(2, registeredRedirectUris.size()); } else { assertEquals(1, registeredRedirectUris.size()); } - Collection authorities = clientDetails.getAuthorities(); assertNotNull(authorities); assertEquals(1, authorities.size()); @@ -176,7 +152,7 @@ private void checkClientDetails(ClientDetails clientDetails) { } else if (!clientDetails.getClientId().equals("APP-1234567898765432")) { assertEquals(3, authorizedGrantTypes.size()); } - + String clientSecret = clientDetails.getClientSecret(); assertNotNull(clientSecret); Set resourceIds = clientDetails.getResourceIds(); diff --git a/orcid-web/src/main/java/org/orcid/frontend/oauth2/OauthController.java b/orcid-web/src/main/java/org/orcid/frontend/oauth2/OauthController.java deleted file mode 100644 index 39d9c367553..00000000000 --- a/orcid-web/src/main/java/org/orcid/frontend/oauth2/OauthController.java +++ /dev/null @@ -1,488 +0,0 @@ -package org.orcid.frontend.oauth2; - -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.security.Principal; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.annotation.Resource; -import javax.servlet.http.HttpServletRequest; - -import org.apache.commons.lang3.StringUtils; -import org.orcid.core.common.manager.EventManager; -import org.orcid.core.constants.OrcidOauth2Constants; -import org.orcid.core.exception.ClientDeactivatedException; -import org.orcid.core.exception.LockedException; -import org.orcid.core.manager.ClientDetailsEntityCacheManager; -import org.orcid.core.manager.impl.OrcidUrlManager; -import org.orcid.core.manager.v3.ProfileEntityManager; -import org.orcid.core.oauth.OrcidRandomValueTokenServices; -import org.orcid.core.oauth.service.OrcidAuthorizationEndpoint; -import org.orcid.core.oauth.service.OrcidOAuth2RequestValidator; -import org.orcid.core.togglz.Features; -import org.orcid.frontend.util.AuthorizationRequestLocalCache; -import org.orcid.frontend.util.OriginalAuthorizationRequestLocalCache; -import org.orcid.frontend.util.RequestInfoFormLocalCache; -import org.orcid.frontend.web.controllers.BaseControllerUtil; -import org.orcid.frontend.web.controllers.helper.OauthHelper; -import org.orcid.frontend.web.exception.OauthInvalidRequestException; -import org.orcid.jaxb.model.message.ScopePathType; -import org.orcid.persistence.jpa.entities.ClientDetailsEntity; -import org.orcid.persistence.jpa.entities.ClientGrantedAuthorityEntity; -import org.orcid.persistence.jpa.entities.EventType; -import org.orcid.persistence.jpa.entities.keys.ClientGrantedAuthorityPk; -import org.orcid.pojo.ajaxForm.PojoUtil; -import org.orcid.pojo.ajaxForm.RequestInfoForm; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.oauth2.common.exceptions.InvalidClientException; -import org.springframework.security.oauth2.common.exceptions.InvalidRequestException; -import org.springframework.security.oauth2.common.exceptions.InvalidScopeException; -import org.springframework.security.oauth2.common.exceptions.RedirectMismatchException; -import org.springframework.security.oauth2.common.exceptions.UnsupportedResponseTypeException; -import org.springframework.security.oauth2.common.util.OAuth2Utils; -import org.springframework.security.oauth2.provider.AuthorizationRequest; -import org.springframework.stereotype.Controller; -import org.springframework.util.MultiValueMap; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.bind.support.SessionStatus; -import org.springframework.web.bind.support.SimpleSessionStatus; -import org.springframework.web.servlet.ModelAndView; -import org.springframework.web.servlet.view.RedirectView; -import org.springframework.web.util.UriComponentsBuilder; - -@Controller("oauthController") -public class OauthController { - - private BaseControllerUtil baseControllerUtil = new BaseControllerUtil(); - - @Resource - private OauthHelper oauthHelper; - - @Resource - private OrcidAuthorizationEndpoint authorizationEndpoint; - - @Resource - private OrcidOAuth2RequestValidator orcidOAuth2RequestValidator; - - @Resource - private ClientDetailsEntityCacheManager clientDetailsEntityCacheManager; - - @Resource - protected OrcidUrlManager orcidUrlManager; - - @Resource - protected OrcidRandomValueTokenServices tokenServices; - - @Resource(name = "profileEntityManagerV3") - private ProfileEntityManager profileEntityManager; - - @Resource - private EventManager eventManager; - - @Resource - private RequestInfoFormLocalCache requestInfoFormLocalCache; - - @Resource - private AuthorizationRequestLocalCache authorizationRequestLocalCache; - - @Resource - private OriginalAuthorizationRequestLocalCache originalAuthorizationRequestLocalCache; - - @RequestMapping(value = { "/oauth/custom/init.json" }, method = RequestMethod.POST) - public @ResponseBody RequestInfoForm loginGetHandler(HttpServletRequest request, Map model, @RequestParam Map requestParameters, - SessionStatus sessionStatus, Principal principal) throws UnsupportedEncodingException { - // Populate the request info form - RequestInfoForm requestInfoForm = generateRequestInfoForm(request, request.getQueryString(), model, requestParameters, sessionStatus, principal); - - // Store the request info form in the cache - requestInfoFormLocalCache.put(request.getSession().getId(), requestInfoForm); - - boolean isResponseSet = false; - - // Verify if we already have the response set in the URL - if (!PojoUtil.isEmpty(requestInfoForm.getResponseType())) { - MultiValueMap parameters = UriComponentsBuilder.fromUriString(requestInfoForm.getRedirectUrl()).build().getQueryParams(); - List responseParam = parameters.get(requestInfoForm.getResponseType()); - if (responseParam != null && !responseParam.isEmpty() && !PojoUtil.isEmpty(responseParam.get(0))) { - isResponseSet = true; - if (Features.EVENTS.isActive()) { - eventManager.createReauthorizeEvent(requestInfoForm.getClientId()); - } - } - } - - if (requestInfoForm.getError() != null || isResponseSet) { - return requestInfoForm; - } - - // validate client scopes - try { - ClientDetailsEntity clientDetails = clientDetailsEntityCacheManager.retrieve(requestInfoForm.getClientId()); - authorizationEndpoint.validateScope(requestInfoForm.getScopesAsString(), clientDetails, requestInfoForm.getResponseType()); - orcidOAuth2RequestValidator.validateClientIsEnabled(clientDetails); - } catch (InvalidScopeException | LockedException | ClientDeactivatedException | InvalidClientException e) { - if (e instanceof InvalidScopeException) { - requestInfoForm.setError("invalid_scope"); - requestInfoForm.setErrorDescription(e.getMessage()); - } else if (e instanceof InvalidClientException) { - requestInfoForm.setError("invalid_client"); - requestInfoForm.setErrorDescription(e.getMessage()); - } else if (e instanceof LockedException){ - requestInfoForm.setError("client_locked"); - requestInfoForm.setErrorDescription(e.getMessage()); - } else { - requestInfoForm.setError("client_deactivated"); - requestInfoForm.setErrorDescription(e.getMessage()); - } - return requestInfoForm; - } - - // Authorize the request if needed - return setAuthorizationRequest(request, model, requestParameters, sessionStatus, principal, requestInfoForm); - } - - @RequestMapping(value = { "/oauth/custom/authorize.json" }, method = RequestMethod.GET) - public @ResponseBody RequestInfoForm requestInfoForm(HttpServletRequest request, Map model, @RequestParam Map requestParameters, - SessionStatus sessionStatus, Principal principal) throws UnsupportedEncodingException { - RequestInfoForm requestInfoForm = requestInfoFormLocalCache.get(request.getSession().getId()); - oauthHelper.setUserName(requestInfoForm); - requestInfoFormLocalCache.put(request.getSession().getId(), requestInfoForm); - return setAuthorizationRequest(request, model, requestParameters, sessionStatus, principal, requestInfoForm); - } - - @RequestMapping(value = { "/oauth/custom/requestInfoForm.json" }, method = RequestMethod.GET) - public @ResponseBody RequestInfoForm customRequestInfoForm(HttpServletRequest request, Map model, @RequestParam Map requestParameters, - SessionStatus sessionStatus, Principal principal) throws UnsupportedEncodingException { - RequestInfoForm requestInfoForm = new RequestInfoForm(); - if(requestInfoFormLocalCache.containsKey(request.getSession().getId())) { - requestInfoForm = requestInfoFormLocalCache.get(request.getSession().getId()); - oauthHelper.setUserName(requestInfoForm); - if (requestParameters.isEmpty() && request.getSession().getAttribute(OrcidOauth2Constants.OAUTH_QUERY_STRING) != null) { - try { - String url = URLDecoder.decode((String) request.getSession().getAttribute(OrcidOauth2Constants.OAUTH_QUERY_STRING), "UTF-8").trim(); - if (url.startsWith("oauth=&")) { - url = url.replaceFirst("oauth=&", ""); - } - String[] pairs = url.split("&"); - for (int i = 0; i < pairs.length; i++) { - String pair = pairs[i]; - String[] keyValue = pair.split("="); - requestParameters.put(keyValue[0], keyValue[1]); - } - setAuthorizationRequest(request, model, requestParameters, sessionStatus, principal, requestInfoForm); - } catch (NullPointerException | ArrayIndexOutOfBoundsException e) { - requestInfoForm.setError("oauth_error"); - requestInfoForm.setErrorDescription("Invalid request"); - } - } - } - return requestInfoForm; - } - - private RequestInfoForm generateRequestInfoForm(HttpServletRequest request, String queryString, Map model, @RequestParam Map requestParameters, - SessionStatus sessionStatus, Principal principal) throws UnsupportedEncodingException { - // Generate the request info form - String url = request.getQueryString(); - RequestInfoForm requestInfoForm = new RequestInfoForm(); - try { - // Get and save the request information form - requestInfoForm = oauthHelper.generateRequestInfoForm(url); - } catch (InvalidClientException e) { - requestInfoForm.setError("invalid_client"); - requestInfoForm.setErrorDescription(e.getMessage()); - return requestInfoForm; - } catch (OauthInvalidRequestException e) { - requestInfoForm.setError("oauth_error"); - requestInfoForm.setErrorDescription(e.getMessage()); - return requestInfoForm; - } catch (InvalidRequestException e) { - requestInfoForm.setError("oauth_error"); - requestInfoForm.setErrorDescription(e.getMessage()); - return requestInfoForm; - } - - // Check that the client have the required permissions - // Get client name - String clientId = requestInfoForm.getClientId(); - if (PojoUtil.isEmpty(clientId)) { - requestInfoForm.setError("invalid_client"); - requestInfoForm.setErrorDescription("invalid client_id"); - return requestInfoForm; - } - - // Validate client details - ClientDetailsEntity clientDetails = clientDetailsEntityCacheManager.retrieve(clientId); - try { - orcidOAuth2RequestValidator.validateClientIsEnabled(clientDetails); - } catch (LockedException e) { - requestInfoForm.setError("client_locked"); - requestInfoForm.setErrorDescription(e.getMessage()); - return requestInfoForm; - } catch (ClientDeactivatedException e) { - requestInfoForm.setError("client_deactivated"); - requestInfoForm.setErrorDescription(e.getMessage()); - return requestInfoForm; - } - - // Populate the user session - populateSession(request, requestInfoForm); - - // handle openID behaviour - if (!PojoUtil.isEmpty(requestInfoForm.getScopesAsString()) - && ScopePathType.getScopesFromSpaceSeparatedString(requestInfoForm.getScopesAsString()).contains(ScopePathType.OPENID)) { - String prompt = request.getParameter(OrcidOauth2Constants.PROMPT); - if (prompt != null && prompt.equals(OrcidOauth2Constants.PROMPT_NONE)) { - SecurityContext sci = getSecurityContext(request); - if (baseControllerUtil.getCurrentUser(sci) != null) { - requestInfoForm.setError("interaction_required"); - } else { - requestInfoForm.setError("login_required"); - } - - return requestInfoForm; - } - } - - // force a login even if the user is already logged in if openid - // prompt=login param present - boolean forceLogin = false; - if (!PojoUtil.isEmpty(requestInfoForm.getScopesAsString()) - && ScopePathType.getScopesFromSpaceSeparatedString(requestInfoForm.getScopesAsString()).contains(ScopePathType.OPENID)) { - String prompt = request.getParameter(OrcidOauth2Constants.PROMPT); - if (prompt != null && prompt.equals(OrcidOauth2Constants.PROMPT_LOGIN)) { - requestInfoForm.setForceLogin(true); - return requestInfoForm; - } - } - - //implicit id_token requests must have nonce. - if (!PojoUtil.isEmpty(requestInfoForm.getScopesAsString()) - && ScopePathType.getScopesFromSpaceSeparatedString(requestInfoForm.getScopesAsString()).contains(ScopePathType.OPENID) - && request.getParameter(OAuth2Utils.RESPONSE_TYPE).contains("id_token") - && request.getParameter(OrcidOauth2Constants.NONCE) == null) { - requestInfoForm.setError("invalid_request"); - requestInfoForm.setErrorDescription("Implicit id_token requests must have nonce"); - return requestInfoForm; - } - - SecurityContext sci = getSecurityContext(request); - - //Check for prompt=login and max_age. This is a MUST in the openid spec. - //If found redirect back to the signin page. - //Add check for prompt=confirm here. This is a SHOULD in the openid spec. - //If found, force user to confirm permissions. - boolean forceConfirm = false; - if (!PojoUtil.isEmpty(requestInfoForm.getScopesAsString()) && ScopePathType.getScopesFromSpaceSeparatedString(requestInfoForm.getScopesAsString()).contains(ScopePathType.OPENID) ){ - String prompt = request.getParameter(OrcidOauth2Constants.PROMPT); - String maxAge = request.getParameter(OrcidOauth2Constants.MAX_AGE); - if (baseControllerUtil.getCurrentUser(sci) != null) { - String orcid = baseControllerUtil.getCurrentUser(sci).getUsername(); - if (maxAge != null) { - //if maxAge+lastlogin > now, force login. max_age is in seconds. - java.util.Date authTime = profileEntityManager.getLastLogin(orcid); //is also on the entity. - try { - long max = Long.parseLong(maxAge); - if (authTime == null || ((authTime.getTime() + (max * 1000)) < (new java.util.Date()).getTime())) { - requestInfoForm.setForceLogin(true); - return requestInfoForm; - } - } catch (NumberFormatException e) { - //ignore - } - } - if (prompt != null && prompt.equals(OrcidOauth2Constants.PROMPT_CONFIRM)) { - forceConfirm = true; - } else if (prompt != null && prompt.equals(OrcidOauth2Constants.PROMPT_LOGIN)) { - requestInfoForm.setForceLogin(true); - return requestInfoForm; - } - } - } - boolean usePersistentTokens = false; - - // Check if the client has persistent tokens enabled - if (clientDetails.isPersistentTokensEnabled()) { - usePersistentTokens = true; - } - - if (!forceConfirm && usePersistentTokens && baseControllerUtil.getCurrentUser(sci) != null) { - boolean tokenLongLifeAlreadyExists = tokenServices.longLifeTokenExist(requestInfoForm.getClientId(), baseControllerUtil.getCurrentUser(sci).getUsername(), OAuth2Utils.parseParameterList(requestInfoForm.getScopesAsString())); - if (tokenLongLifeAlreadyExists) { - setAuthorizationRequest(request, model, requestParameters, sessionStatus, principal, requestInfoForm); - AuthorizationRequest authorizationRequest = authorizationRequestLocalCache.get(request.getSession().getId()); - if (authorizationRequest != null) { - Map requestParams = new HashMap(); - copyRequestParameters(request, requestParams); - Map approvalParams = new HashMap(); - - requestParams.put(OAuth2Utils.USER_OAUTH_APPROVAL, "true"); - approvalParams.put(OAuth2Utils.USER_OAUTH_APPROVAL, "true"); - - requestParams.put(OrcidOauth2Constants.TOKEN_VERSION, OrcidOauth2Constants.PERSISTENT_TOKEN); - - boolean hasPersistent = hasPersistenTokensEnabled(requestInfoForm.getClientId(), requestInfoForm); - // Don't let non persistent clients persist - if (!hasPersistent && "true".equals(requestParams.get(OrcidOauth2Constants.GRANT_PERSISTENT_TOKEN))){ - requestParams.put(OrcidOauth2Constants.GRANT_PERSISTENT_TOKEN, "false"); - } - //default to client default if not set - if (requestParams.get(OrcidOauth2Constants.GRANT_PERSISTENT_TOKEN) == null) { - if (hasPersistent) - requestParams.put(OrcidOauth2Constants.GRANT_PERSISTENT_TOKEN, "true"); - else - requestParams.put(OrcidOauth2Constants.GRANT_PERSISTENT_TOKEN, "false"); - } - - // Session status - SimpleSessionStatus status = new SimpleSessionStatus(); - - authorizationRequest.setRequestParameters(requestParams); - // Authorization request model - Map modelAuth = new HashMap(); - modelAuth.put("authorizationRequest", authorizationRequest); - - Map originalRequest = originalAuthorizationRequestLocalCache.get(request.getSession().getId()); - if(originalRequest != null) { - modelAuth.put(OrcidOauth2Constants.ORIGINAL_AUTHORIZATION_REQUEST, originalRequest); - } - - // Approve using the spring authorization endpoint code. - //note this will also handle generating implicit tokens via getTokenGranter().grant("implicit",new ImplicitTokenRequest(tokenRequest, storedOAuth2Request)); - RedirectView view = (RedirectView) authorizationEndpoint.approveOrDeny(approvalParams, modelAuth, status, principal); - requestInfoForm.setRedirectUrl(view.getUrl()); - // Oauth has been approved, hence, remove the oauth flag from the session - requestInfoFormLocalCache.remove(request.getSession().getId()); - request.getSession().removeAttribute(OrcidOauth2Constants.OAUTH_2SCREENS); - } - } - } - - return requestInfoForm; - } - - private void populateSession(HttpServletRequest request, RequestInfoForm requestInfoForm) { - String url = request.getQueryString(); - - requestInfoFormLocalCache.put(request.getSession().getId(), requestInfoForm); - - // Save also the original query string - request.getSession().setAttribute(OrcidOauth2Constants.OAUTH_QUERY_STRING, url); - - // Save a flag to indicate this is a Oauth request - request.getSession().setAttribute(OrcidOauth2Constants.OAUTH_2SCREENS, true); - - //Required to be able to work with org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint to authorize the request - Map authorizationRequestMap = new HashMap(); - - authorizationRequestMap.put(OAuth2Utils.CLIENT_ID, requestInfoForm.getClientId()); - authorizationRequestMap.put(OAuth2Utils.REDIRECT_URI, requestInfoForm.getRedirectUrl()); - authorizationRequestMap.put(OrcidOauth2Constants.APPROVED, false); - authorizationRequestMap.put(OrcidOauth2Constants.RESOURCE_IDS, Set.of("orcid")); - - ClientDetailsEntity clientDetails = clientDetailsEntityCacheManager.retrieve(requestInfoForm.getClientId()); - ClientGrantedAuthorityEntity cgae = new ClientGrantedAuthorityEntity(); - cgae.setClientId(clientDetails.getClientId()); - cgae.setAuthority((clientDetails.getClientGrantedAuthorities().isEmpty()) ? "ROLE_CLIENT" : clientDetails.getClientGrantedAuthorities().get(0).getAuthority()); - authorizationRequestMap.put(OrcidOauth2Constants.AUTHORITIES, Set.of(cgae)); - - if(requestInfoForm.getStateParam() != null) { - authorizationRequestMap.put(OAuth2Utils.STATE, requestInfoForm.getStateParam()); - } - - if (requestInfoForm.getScopes() != null) { - Set scopes = new HashSet(); - requestInfoForm.getScopes().forEach(s -> {scopes.add(s.getValue());}); - authorizationRequestMap.put(OAuth2Utils.SCOPE, Set.copyOf(scopes)); - } - - if (requestInfoForm.getResponseType() != null) { - String scope = requestInfoForm.getScopesAsString(); - String responseType = requestInfoForm.getResponseType(); - if(authorizationEndpoint.isOpenIdWithTokenResponseType(scope, responseType)) { - authorizationRequestMap.put(OAuth2Utils.RESPONSE_TYPE, Set.of("id_token", "token")); - } else { - authorizationRequestMap.put(OAuth2Utils.RESPONSE_TYPE, Set.of(requestInfoForm.getResponseType())); - } - } - - Map originalAuthorizationRequest = Map.copyOf(authorizationRequestMap); - originalAuthorizationRequestLocalCache.put(request.getSession().getId(), originalAuthorizationRequest); - } - - private RequestInfoForm setAuthorizationRequest(HttpServletRequest request, Map model, @RequestParam Map requestParameters, - SessionStatus sessionStatus, Principal principal, RequestInfoForm requestInfoForm) { - SecurityContext sci = getSecurityContext(request); - authorizationRequestLocalCache.remove(request.getSession().getId()); - if (baseControllerUtil.getCurrentUser(sci) != null) { - // Authorize the request - try { - ModelAndView mav = authorizationEndpoint.authorize(model, requestParameters, sessionStatus, principal); - RedirectView rev = (RedirectView) mav.getView(); - if (rev != null) { - String url = rev.getUrl(); - String errorDescription = "error_description="; - if (url.contains("error")) { - requestInfoForm.setError("invalid_scope"); - requestInfoForm.setErrorDescription(url.substring(url.indexOf("error_description=") + errorDescription.length())); - } - } - - // If there are not errors, store the authorizationRequest in the local cache - if(StringUtils.isEmpty(requestInfoForm.getError())) { - AuthorizationRequest authRequest = (AuthorizationRequest) mav.getModel().get("authorizationRequest"); - authorizationRequestLocalCache.put(request.getSession().getId(), authRequest); - } - } catch (RedirectMismatchException e ) { - requestInfoForm.setError("invalid_grant"); - requestInfoForm.setErrorDescription("Redirect URI doesn't match your registered redirect URIs."); - } catch (UnsupportedResponseTypeException e) { - requestInfoForm.setError("unsupported_response_type"); - requestInfoForm.setErrorDescription("Unsupported response type."); - } - } - return requestInfoForm; - } - - private SecurityContext getSecurityContext(HttpServletRequest request) { - SecurityContext sci = null; - if (request.getSession() != null) { - sci = (SecurityContext) request.getSession().getAttribute("SPRING_SECURITY_CONTEXT"); - } - return sci; - } - - protected boolean hasPersistenTokensEnabled(String clientId, RequestInfoForm requestInfoForm) throws IllegalArgumentException { - ClientDetailsEntity clientDetails = clientDetailsEntityCacheManager.retrieve(clientId); - if (clientDetails == null) { - requestInfoForm.setError("invalid_client"); - requestInfoForm.setErrorDescription("invalid client_id"); - return false; - } else { - return clientDetails.isPersistentTokensEnabled(); - } - } - - private void copyRequestParameters(HttpServletRequest request, Map params) { - if (request != null && request.getParameterMap() != null) { - Map savedParams = request.getParameterMap(); - copy(savedParams, params); - } - } - - protected void copy(Map savedParams, Map params) { - if (savedParams != null && !savedParams.isEmpty()) { - for (String key : savedParams.keySet()) { - String[] values = savedParams.get(key); - if (values != null && values.length > 0) - params.put(key, values[0]); - } - } - } -} diff --git a/orcid-web/src/main/java/org/orcid/frontend/oauth2/RevokeController.java b/orcid-web/src/main/java/org/orcid/frontend/oauth2/RevokeController.java index c5943681dd8..d0ebd391458 100644 --- a/orcid-web/src/main/java/org/orcid/frontend/oauth2/RevokeController.java +++ b/orcid-web/src/main/java/org/orcid/frontend/oauth2/RevokeController.java @@ -28,59 +28,23 @@ public class RevokeController { private static final Logger LOGGER = LoggerFactory.getLogger(RevokeController.class); - @Resource - private OrcidOauth2TokenDetailService orcidOauth2TokenDetailService; - @Resource private AuthorizationServerUtil authorizationServerUtil; - public void setOrcidOauth2TokenDetailService(OrcidOauth2TokenDetailService orcidOauth2TokenDetailService) { - this.orcidOauth2TokenDetailService = orcidOauth2TokenDetailService; - } - @RequestMapping public ResponseEntity revoke(HttpServletRequest request) throws IOException, URISyntaxException, InterruptedException { - if(Features.OAUTH_TOKEN_VALIDATION.isActive()) { - // Forward the request to the authorization server - String clientId = SecurityContextHolder.getContext().getAuthentication().getName(); - String clientSecret = request.getParameter("client_secret"); - String tokenToRevoke = request.getParameter("token"); - if (PojoUtil.isEmpty(tokenToRevoke)) { - throw new IllegalArgumentException("Please provide the token to be param"); - } - Response r = authorizationServerUtil.forwardTokenRevocationRequest(clientId, clientSecret, tokenToRevoke); - HttpHeaders responseHeaders = new HttpHeaders(); - responseHeaders.set(Features.OAUTH_TOKEN_VALIDATION.name(), - "ON"); - return ResponseEntity.status(r.getStatus()).headers(responseHeaders).body(r.getEntity()); - } else { - String clientId = SecurityContextHolder.getContext().getAuthentication().getName(); - if (PojoUtil.isEmpty(clientId)) { - throw new IllegalArgumentException("Unable to validate client credentials"); - } - - String tokenToRevoke = request.getParameter("token"); - if (PojoUtil.isEmpty(tokenToRevoke)) { - throw new IllegalArgumentException("Please provide the token to be param"); - } - - OrcidOauth2TokenDetail token = orcidOauth2TokenDetailService.findIgnoringDisabledByTokenValue(tokenToRevoke); - if (token == null) { - // Try to find it by refresh token - token = orcidOauth2TokenDetailService.findByRefreshTokenValue(tokenToRevoke); - } - - if (token != null && (token.getTokenDisabled() == null || !token.getTokenDisabled())) { - String tokenOwner = token.getClientDetailsId(); - if (clientId.equals(tokenOwner)) { - orcidOauth2TokenDetailService.revokeAccessToken(token.getTokenValue()); - } else { - LOGGER.warn("Client {} is trying to revoke token that belongs to client {}", clientId, tokenOwner); - } - } + // Forward the request to the authorization server + String clientId = SecurityContextHolder.getContext().getAuthentication().getName(); + String clientSecret = request.getParameter("client_secret"); + String tokenToRevoke = request.getParameter("token"); + if (PojoUtil.isEmpty(tokenToRevoke)) { + throw new IllegalArgumentException("Please provide the token to be param"); } - - return ResponseEntity.ok().build(); + Response r = authorizationServerUtil.forwardTokenRevocationRequest(clientId, clientSecret, tokenToRevoke); + HttpHeaders responseHeaders = new HttpHeaders(); + responseHeaders.set(Features.OAUTH_TOKEN_VALIDATION.name(), + "ON"); + return ResponseEntity.status(r.getStatus()).headers(responseHeaders).body(r.getEntity()); } } diff --git a/orcid-web/src/main/java/org/orcid/frontend/web/controllers/LoginController.java b/orcid-web/src/main/java/org/orcid/frontend/web/controllers/LoginController.java index 4b26382d314..8269f518a03 100644 --- a/orcid-web/src/main/java/org/orcid/frontend/web/controllers/LoginController.java +++ b/orcid-web/src/main/java/org/orcid/frontend/web/controllers/LoginController.java @@ -14,13 +14,9 @@ import org.codehaus.jettison.json.JSONObject; import org.orcid.core.common.manager.EventManager; import org.orcid.core.constants.OrcidOauth2Constants; -import org.orcid.core.exception.ClientDeactivatedException; -import org.orcid.core.exception.LockedException; import org.orcid.core.manager.ClientDetailsEntityCacheManager; import org.orcid.core.manager.UserConnectionManager; import org.orcid.core.manager.v3.read_only.RecordNameManagerReadOnly; -import org.orcid.core.oauth.service.OrcidAuthorizationEndpoint; -import org.orcid.core.oauth.service.OrcidOAuth2RequestValidator; import org.orcid.core.security.OrcidUserDetailsService; import org.orcid.core.togglz.Features; import org.orcid.frontend.spring.web.social.config.SocialSignInUtils; @@ -31,7 +27,6 @@ import org.orcid.jaxb.model.message.ScopePathType; import org.orcid.jaxb.model.v3.release.common.Visibility; import org.orcid.jaxb.model.v3.release.record.Name; -import org.orcid.persistence.jpa.entities.ClientDetailsEntity; import org.orcid.persistence.jpa.entities.EventType; import org.orcid.persistence.jpa.entities.ProfileEntity; import org.orcid.persistence.jpa.entities.UserconnectionEntity; @@ -45,9 +40,6 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.oauth2.common.exceptions.InvalidClientException; -import org.springframework.security.oauth2.common.exceptions.InvalidRequestException; -import org.springframework.security.oauth2.common.exceptions.InvalidScopeException; import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; @@ -66,12 +58,6 @@ public class LoginController extends OauthControllerBase { @Resource protected ClientDetailsEntityCacheManager clientDetailsEntityCacheManager; - @Resource - protected OrcidOAuth2RequestValidator orcidOAuth2RequestValidator; - - @Resource - protected OrcidAuthorizationEndpoint authorizationEndpoint; - @Resource(name = "recordNameManagerV3") private RecordNameManagerReadOnly recordNameManager; @@ -162,7 +148,7 @@ private ModelAndView handleOauthSignIn(HttpServletRequest request, HttpServletRe RequestInfoForm requestInfoForm; try { requestInfoForm = oauthHelper.generateRequestInfoForm(queryString); - } catch (InvalidRequestException | InvalidClientException e) { + } catch (Exception e) { // convert to a 400 ModelAndView mav = new ModelAndView("oauth-error"); mav.setStatus(HttpStatus.BAD_REQUEST); @@ -202,25 +188,6 @@ private ModelAndView handleOauthSignIn(HttpServletRequest request, HttpServletRe String redirectUriWithParams = redirectUri + "?error=invalid_client&error_description=invalid client_id"; return new ModelAndView(new RedirectView(redirectUriWithParams)); } - // Validate client details - ClientDetailsEntity clientDetails = clientDetailsEntityCacheManager.retrieve(clientId); - try { - orcidOAuth2RequestValidator.validateClientIsEnabled(clientDetails); - } catch (LockedException e) { - String redirectUriWithParams = redirectUri + "?error=client_locked&error_description=" + e.getMessage(); - return new ModelAndView(new RedirectView(redirectUriWithParams)); - } catch (ClientDeactivatedException e) { - String redirectUriWithParams = redirectUri + "?error=client_deactivated&error_description=" + e.getMessage(); - return new ModelAndView(new RedirectView(redirectUriWithParams)); - } - - // validate client scopes - try { - authorizationEndpoint.validateScope(requestInfoForm.getScopesAsString(), clientDetails, requestInfoForm.getResponseType()); - } catch (InvalidScopeException e) { - String redirectUriWithParams = redirectUri + "?error=invalid_scope&error_description=" + e.getMessage(); - return new ModelAndView(new RedirectView(redirectUriWithParams)); - } // handle openID prompt and max_age behaviour // here we remove prompt=login if present From cb38c09caf89dfd769e48516ecde47ec4266d33d Mon Sep 17 00:00:00 2001 From: amontenegro Date: Mon, 23 Mar 2026 17:03:45 -0600 Subject: [PATCH 04/16] Removing code and refactoring the authentication objects, introducing OrcidBearerTokenAuthentication --- .../core/exception/InvalidTokenException.java | 9 + .../manager/v3/impl/SourceManagerImpl.java | 17 +- .../core/oauth/IETFTokenExchangeResponse.java | 108 ----- ...va => OrcidBearerTokenAuthentication.java} | 4 +- .../OrcidClientCredentialsTokenGranter.java | 37 -- .../core/oauth/OrcidImplicitTokenGranter.java | 57 --- ...rcidMultiSecretAuthenticationProvider.java | 53 --- .../core/oauth/OrcidOAuth2Authentication.java | 43 -- .../orcid/core/oauth/OrcidOauth2AuthInfo.java | 72 --- .../oauth/OrcidOboOAuth2Authentication.java | 41 -- .../oauth/OrcidRandomValueTokenServices.java | 34 -- .../core/oauth/OrcidRefreshTokenChecker.java | 106 ----- .../oauth/OrcidRefreshTokenTokenGranter.java | 36 -- .../orcid/core/oauth/OrcidTokenEnhancer.java | 101 ----- ...tion.java => OrcidUserAuthentication.java} | 4 +- .../oauth/openid/OpenIDConnectUserInfo.java | 67 --- .../security/OrcidOauthRedirectResolver.java | 96 ---- .../NamespacedRandomCodeGenerator.java | 53 --- .../OrcidAuthorizationCodeServiceImpl.java | 196 -------- .../OrcidRandomValueTokenServicesImpl.java | 428 +----------------- .../security/DefaultPermissionChecker.java | 365 --------------- .../core/security/PermissionChecker.java | 78 ---- .../OrcidApiAuthorizationSecurityAspect.java | 385 ---------------- .../visibility/filter/VisibilityFilter.java | 15 - .../filter/impl/VisibilityFilterImpl.java | 272 ----------- .../core/utils/SecurityContextTestUtils.java | 128 ------ .../OrcidRandomValueTokenServicesTest.java | 12 +- .../service/OrcidTokenStoreServiceTest.java | 6 +- .../DefaultOAuthClientVisibilityTest.java | 82 ---- .../DefaultPermissionCheckerTest.java | 170 ------- 30 files changed, 33 insertions(+), 3042 deletions(-) create mode 100644 orcid-core/src/main/java/org/orcid/core/exception/InvalidTokenException.java delete mode 100644 orcid-core/src/main/java/org/orcid/core/oauth/IETFTokenExchangeResponse.java rename orcid-core/src/main/java/org/orcid/core/oauth/{OrcidOauth2ClientAuthentication.java => OrcidBearerTokenAuthentication.java} (97%) delete mode 100644 orcid-core/src/main/java/org/orcid/core/oauth/OrcidClientCredentialsTokenGranter.java delete mode 100644 orcid-core/src/main/java/org/orcid/core/oauth/OrcidImplicitTokenGranter.java delete mode 100644 orcid-core/src/main/java/org/orcid/core/oauth/OrcidMultiSecretAuthenticationProvider.java delete mode 100644 orcid-core/src/main/java/org/orcid/core/oauth/OrcidOAuth2Authentication.java delete mode 100644 orcid-core/src/main/java/org/orcid/core/oauth/OrcidOauth2AuthInfo.java delete mode 100644 orcid-core/src/main/java/org/orcid/core/oauth/OrcidOboOAuth2Authentication.java delete mode 100644 orcid-core/src/main/java/org/orcid/core/oauth/OrcidRandomValueTokenServices.java delete mode 100644 orcid-core/src/main/java/org/orcid/core/oauth/OrcidRefreshTokenChecker.java delete mode 100644 orcid-core/src/main/java/org/orcid/core/oauth/OrcidRefreshTokenTokenGranter.java delete mode 100644 orcid-core/src/main/java/org/orcid/core/oauth/OrcidTokenEnhancer.java rename orcid-core/src/main/java/org/orcid/core/oauth/{OrcidOauth2UserAuthentication.java => OrcidUserAuthentication.java} (97%) delete mode 100644 orcid-core/src/main/java/org/orcid/core/oauth/openid/OpenIDConnectUserInfo.java delete mode 100644 orcid-core/src/main/java/org/orcid/core/oauth/security/OrcidOauthRedirectResolver.java delete mode 100644 orcid-core/src/main/java/org/orcid/core/oauth/service/NamespacedRandomCodeGenerator.java delete mode 100644 orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidAuthorizationCodeServiceImpl.java delete mode 100644 orcid-core/src/main/java/org/orcid/core/security/DefaultPermissionChecker.java delete mode 100644 orcid-core/src/main/java/org/orcid/core/security/PermissionChecker.java delete mode 100644 orcid-core/src/main/java/org/orcid/core/security/visibility/aop/OrcidApiAuthorizationSecurityAspect.java delete mode 100644 orcid-core/src/main/java/org/orcid/core/security/visibility/filter/VisibilityFilter.java delete mode 100644 orcid-core/src/main/java/org/orcid/core/security/visibility/filter/impl/VisibilityFilterImpl.java delete mode 100644 orcid-core/src/main/java/org/orcid/core/utils/SecurityContextTestUtils.java delete mode 100644 orcid-core/src/test/java/org/orcid/core/security/DefaultOAuthClientVisibilityTest.java delete mode 100644 orcid-core/src/test/java/org/orcid/core/security/DefaultPermissionCheckerTest.java diff --git a/orcid-core/src/main/java/org/orcid/core/exception/InvalidTokenException.java b/orcid-core/src/main/java/org/orcid/core/exception/InvalidTokenException.java new file mode 100644 index 00000000000..796439f9cb6 --- /dev/null +++ b/orcid-core/src/main/java/org/orcid/core/exception/InvalidTokenException.java @@ -0,0 +1,9 @@ +package org.orcid.core.exception; + +public class InvalidTokenException extends ApplicationException { + private static final long serialVersionUID = 1L; + + public InvalidTokenException(String s) { + super(s); + } +} diff --git a/orcid-core/src/main/java/org/orcid/core/manager/v3/impl/SourceManagerImpl.java b/orcid-core/src/main/java/org/orcid/core/manager/v3/impl/SourceManagerImpl.java index aab12c28ebf..3fed5bbc201 100644 --- a/orcid-core/src/main/java/org/orcid/core/manager/v3/impl/SourceManagerImpl.java +++ b/orcid-core/src/main/java/org/orcid/core/manager/v3/impl/SourceManagerImpl.java @@ -1,7 +1,5 @@ package org.orcid.core.manager.v3.impl; -import java.util.Collection; - import javax.annotation.Resource; import org.apache.commons.lang3.StringUtils; @@ -10,9 +8,8 @@ import org.orcid.core.manager.SourceNameCacheManager; import org.orcid.core.manager.v3.SourceManager; import org.orcid.core.oauth.OrcidOAuth2Authentication; -import org.orcid.core.oauth.OrcidOauth2UserAuthentication; +import org.orcid.core.oauth.OrcidUserAuthentication; import org.orcid.core.oauth.OrcidOboOAuth2Authentication; -import org.orcid.core.security.OrcidRoles; import org.orcid.core.togglz.Features; import org.orcid.jaxb.model.v3.release.common.Source; import org.orcid.jaxb.model.v3.release.common.SourceClientId; @@ -24,15 +21,11 @@ import org.orcid.persistence.jpa.entities.OrcidOauth2TokenDetail; import org.orcid.persistence.jpa.entities.ProfileEntity; import org.orcid.persistence.jpa.entities.SourceEntity; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.OAuth2Request; import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails; -import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; -import org.springframework.security.web.authentication.switchuser.SwitchUserGrantedAuthority; /** * @@ -90,8 +83,8 @@ public Source retrieveActiveSource() { source.setAssertionOriginClientId(new SourceClientId(oboClientDetails.getClientId())); source.setAssertionOriginName(new SourceName(oboClientDetails.getClientName())); } else { - if(clientDetails.isUserOBOEnabled() && authDetails.getUserAuthentication() != null && OrcidOauth2UserAuthentication.class.isAssignableFrom(authDetails.getUserAuthentication().getClass())) { - OrcidOauth2UserAuthentication userAuth = (OrcidOauth2UserAuthentication) authDetails.getUserAuthentication(); + if(clientDetails.isUserOBOEnabled() && authDetails.getUserAuthentication() != null && OrcidUserAuthentication.class.isAssignableFrom(authDetails.getUserAuthentication().getClass())) { + OrcidUserAuthentication userAuth = (OrcidUserAuthentication) authDetails.getUserAuthentication(); ProfileEntity profile = (ProfileEntity) userAuth.getPrincipal(); source.setAssertionOriginOrcid(new SourceOrcid(profile.getId())); source.setAssertionOriginName(new SourceName(sourceNameCacheManager.retrieve(profile.getId()))); @@ -99,8 +92,8 @@ public Source retrieveActiveSource() { } } else { OrcidOAuth2Authentication authDetails = (OrcidOAuth2Authentication) authentication; - if(clientDetails.isUserOBOEnabled() && authDetails.getUserAuthentication() != null && OrcidOauth2UserAuthentication.class.isAssignableFrom(authDetails.getUserAuthentication().getClass())) { - OrcidOauth2UserAuthentication userAuth = (OrcidOauth2UserAuthentication) authDetails.getUserAuthentication(); + if(clientDetails.isUserOBOEnabled() && authDetails.getUserAuthentication() != null && OrcidUserAuthentication.class.isAssignableFrom(authDetails.getUserAuthentication().getClass())) { + OrcidUserAuthentication userAuth = (OrcidUserAuthentication) authDetails.getUserAuthentication(); ProfileEntity profile = (ProfileEntity) userAuth.getPrincipal(); source.setAssertionOriginOrcid(new SourceOrcid(profile.getId())); source.setAssertionOriginName(new SourceName(sourceNameCacheManager.retrieve(profile.getId()))); diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/IETFTokenExchangeResponse.java b/orcid-core/src/main/java/org/orcid/core/oauth/IETFTokenExchangeResponse.java deleted file mode 100644 index 9c81fe501f6..00000000000 --- a/orcid-core/src/main/java/org/orcid/core/oauth/IETFTokenExchangeResponse.java +++ /dev/null @@ -1,108 +0,0 @@ -package org.orcid.core.oauth; - -import java.text.ParseException; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import org.orcid.core.constants.OrcidOauth2Constants; -import org.springframework.security.oauth2.common.OAuth2AccessToken; -import org.springframework.security.oauth2.common.OAuth2RefreshToken; - -import com.nimbusds.jwt.SignedJWT; - -import static org.orcid.core.constants.OrcidOauth2Constants.TOKEN_DISABLED; -import static org.orcid.core.constants.OrcidOauth2Constants.IS_OBO_TOKEN; - -public class IETFTokenExchangeResponse implements OAuth2AccessToken { - - private Map additionalInformation = new HashMap(); - private Set scope = new HashSet(); - private OAuth2RefreshToken refreshToken = null; - private String tokenType; - private Date expiration; - private String value; - private int expiresIn; - - public static IETFTokenExchangeResponse idToken(String idToken) throws ParseException { - IETFTokenExchangeResponse token = new IETFTokenExchangeResponse(); - token.additionalInformation.put("issued_token_type", OrcidOauth2Constants.IETF_EXCHANGE_ID_TOKEN ); - token.value = idToken; - token.tokenType = "N_A"; - SignedJWT claims = SignedJWT.parse(idToken); - token.expiration = claims.getJWTClaimsSet().getExpirationTime(); - return token; - } - - public static IETFTokenExchangeResponse accessToken(OAuth2AccessToken accessToken) { - IETFTokenExchangeResponse token = new IETFTokenExchangeResponse(); - token.additionalInformation.put("issued_token_type", OrcidOauth2Constants.IETF_EXCHANGE_ACCESS_TOKEN ); - token.value = accessToken.getValue(); - token.tokenType="bearer"; - token.expiration = accessToken.getExpiration(); - token.expiresIn = accessToken.getExpiresIn(); - token.scope = accessToken.getScope(); - if (accessToken.getAdditionalInformation().containsKey("orcid")) { - token.additionalInformation.put("orcid",accessToken.getAdditionalInformation().get("orcid")); - } - if (accessToken.getAdditionalInformation().containsKey("name")) { - token.additionalInformation.put("name",accessToken.getAdditionalInformation().get("name")); - } - if(accessToken.getAdditionalInformation().containsKey(TOKEN_DISABLED)) { - token.additionalInformation.put(TOKEN_DISABLED, "true"); - } - if(accessToken.getAdditionalInformation().containsKey(IS_OBO_TOKEN)) { - token.additionalInformation.put(IS_OBO_TOKEN, "true"); - } - - return token; - } - - private IETFTokenExchangeResponse() { - - } - - - @Override - public Map getAdditionalInformation() { - return additionalInformation; - } - - @Override - public Set getScope() { - return scope; - } - - @Override - public OAuth2RefreshToken getRefreshToken() { - return refreshToken; - } - - @Override - public String getTokenType() { - return tokenType; - } - - @Override - public boolean isExpired() { - return false; - } - - @Override - public Date getExpiration() { - return expiration; - } - - @Override - public int getExpiresIn() { - return expiresIn; - } - - @Override - public String getValue() { - return value; - } - -} diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/OrcidOauth2ClientAuthentication.java b/orcid-core/src/main/java/org/orcid/core/oauth/OrcidBearerTokenAuthentication.java similarity index 97% rename from orcid-core/src/main/java/org/orcid/core/oauth/OrcidOauth2ClientAuthentication.java rename to orcid-core/src/main/java/org/orcid/core/oauth/OrcidBearerTokenAuthentication.java index 657b889f468..c8a034d599b 100644 --- a/orcid-core/src/main/java/org/orcid/core/oauth/OrcidOauth2ClientAuthentication.java +++ b/orcid-core/src/main/java/org/orcid/core/oauth/OrcidBearerTokenAuthentication.java @@ -9,7 +9,7 @@ /** * @author Declan Newman (declan) Date: 17/04/2012 */ -public class OrcidOauth2ClientAuthentication implements Authentication { +public class OrcidBearerTokenAuthentication implements Authentication { /** * @@ -18,7 +18,7 @@ public class OrcidOauth2ClientAuthentication implements Authentication { private ClientDetailsEntity clientDetails; private boolean authenticated = false; - public OrcidOauth2ClientAuthentication(ClientDetailsEntity clientDetails) { + public OrcidBearerTokenAuthentication(ClientDetailsEntity clientDetails) { this.clientDetails = clientDetails; } diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/OrcidClientCredentialsTokenGranter.java b/orcid-core/src/main/java/org/orcid/core/oauth/OrcidClientCredentialsTokenGranter.java deleted file mode 100644 index 8b0460e8305..00000000000 --- a/orcid-core/src/main/java/org/orcid/core/oauth/OrcidClientCredentialsTokenGranter.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.orcid.core.oauth; - -import org.springframework.security.oauth2.common.OAuth2AccessToken; -import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.security.oauth2.provider.OAuth2Request; -import org.springframework.security.oauth2.provider.TokenGranter; -import org.springframework.security.oauth2.provider.TokenRequest; -import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; - -/** - * @author Declan Newman (declan) Date: 10/05/2012 - */ -public class OrcidClientCredentialsTokenGranter implements TokenGranter { - - private static final String CLIENT_CREDENTIALS = "client_credentials"; - - private final OrcidClientCredentialsChecker clientCredentialsChecker; - - private final AuthorizationServerTokenServices tokenServices; - - public OrcidClientCredentialsTokenGranter(OrcidClientCredentialsChecker clientCredentialsChecker, AuthorizationServerTokenServices tokenServices) { - this.clientCredentialsChecker = clientCredentialsChecker; - this.tokenServices = tokenServices; - } - - @Override - public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) { - if (!CLIENT_CREDENTIALS.equals(grantType)) { - return null; - } - - OAuth2Request authorizationRequest = clientCredentialsChecker.validateCredentials(grantType, tokenRequest); - - return tokenServices.createAccessToken(new OAuth2Authentication(authorizationRequest, null)); - } - -} diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/OrcidImplicitTokenGranter.java b/orcid-core/src/main/java/org/orcid/core/oauth/OrcidImplicitTokenGranter.java deleted file mode 100644 index 00aedf8e939..00000000000 --- a/orcid-core/src/main/java/org/orcid/core/oauth/OrcidImplicitTokenGranter.java +++ /dev/null @@ -1,57 +0,0 @@ -package org.orcid.core.oauth; - -import java.util.List; - -import javax.annotation.Resource; - -import org.orcid.core.manager.ProfileEntityManager; -import org.orcid.persistence.dao.ProfileDao; -import org.orcid.persistence.jpa.entities.OrcidGrantedAuthority; -import org.orcid.persistence.jpa.entities.ProfileEntity; -import org.springframework.security.authentication.InsufficientAuthenticationException; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.oauth2.provider.ClientDetails; -import org.springframework.security.oauth2.provider.ClientDetailsService; -import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.security.oauth2.provider.OAuth2Request; -import org.springframework.security.oauth2.provider.OAuth2RequestFactory; -import org.springframework.security.oauth2.provider.TokenRequest; -import org.springframework.security.oauth2.provider.implicit.ImplicitTokenGranter; -import org.springframework.security.oauth2.provider.implicit.ImplicitTokenRequest; -import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; - -public class OrcidImplicitTokenGranter extends ImplicitTokenGranter { - @Resource - private ProfileEntityManager profileEntityManager; - @Resource - private ProfileDao profileDao; - - protected OrcidImplicitTokenGranter(AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) { - super(tokenServices, clientDetailsService, requestFactory); - } - - /** - * Note, client must have implicit scope in client_authorized_grant_type - * table to get this far. Otherwise request will be rejected by - * OrcidClientCredentialsChecker - * - */ - @Override - protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest clientToken) { - Authentication userAuthSpring = SecurityContextHolder.getContext().getAuthentication(); - if (userAuthSpring == null || !userAuthSpring.isAuthenticated()) { - throw new InsufficientAuthenticationException("There is no currently logged in user"); - } - OAuth2Request request = ((ImplicitTokenRequest) clientToken).getOAuth2Request(); - ProfileEntity profileEntity = profileEntityManager.findByOrcid(userAuthSpring.getName()); - List authorities = profileDao.getGrantedAuthoritiesForProfile(profileEntity.getId()); - profileEntity.setAuthorities(authorities); - - OrcidOauth2UserAuthentication userAuth = new OrcidOauth2UserAuthentication(profileEntity, - userAuthSpring.isAuthenticated()); - OAuth2Authentication result = new OAuth2Authentication(request, userAuth); - return result; - } - -} diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/OrcidMultiSecretAuthenticationProvider.java b/orcid-core/src/main/java/org/orcid/core/oauth/OrcidMultiSecretAuthenticationProvider.java deleted file mode 100644 index 99da7f732de..00000000000 --- a/orcid-core/src/main/java/org/orcid/core/oauth/OrcidMultiSecretAuthenticationProvider.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.orcid.core.oauth; - -import javax.annotation.Resource; - -import org.orcid.core.manager.ClientDetailsManager; -import org.orcid.core.manager.EncryptionManager; -import org.orcid.persistence.jpa.entities.ClientDetailsEntity; -import org.orcid.persistence.jpa.entities.ClientSecretEntity; -import org.springframework.security.authentication.BadCredentialsException; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.authentication.dao.DaoAuthenticationProvider; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.crypto.password.NoOpPasswordEncoder; - -/** - * - * @author Will Simpson - * - */ -public class OrcidMultiSecretAuthenticationProvider extends DaoAuthenticationProvider { - - @Resource - private ClientDetailsManager clientDetailsManager; - - @Resource - private EncryptionManager encryptionManager; - - @SuppressWarnings("deprecation") - @Override - protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { - - if (authentication.getCredentials() == null) { - logger.debug("Authentication failed: no credentials provided"); - throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); - } - - String presentedPassword = authentication.getCredentials().toString(); - - ClientDetailsEntity clientDetailsEntity = clientDetailsManager.findByClientId(userDetails.getUsername()); - for (ClientSecretEntity clientSecretEntity : clientDetailsEntity.getClientSecrets()) { - if(!presentedPassword.startsWith("{noop}")){ - presentedPassword = "{noop}" + presentedPassword; - } - if (getPasswordEncoder().matches(encryptionManager.decryptForInternalUse(clientSecretEntity.getClientSecret()), presentedPassword)) { - return; - } - } - logger.debug("Authentication failed: password does not match any value"); - throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); - } - -} diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/OrcidOAuth2Authentication.java b/orcid-core/src/main/java/org/orcid/core/oauth/OrcidOAuth2Authentication.java deleted file mode 100644 index 4d65aced1f7..00000000000 --- a/orcid-core/src/main/java/org/orcid/core/oauth/OrcidOAuth2Authentication.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.orcid.core.oauth; - -import org.springframework.security.core.Authentication; -import org.springframework.security.oauth2.provider.AuthorizationRequest; -import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.util.Assert; - -/** - * @author Declan Newman (declan) Date: 10/05/2012 - */ -public class OrcidOAuth2Authentication extends OAuth2Authentication { - - /** - * - */ - private static final long serialVersionUID = 1L; - private String activeToken; - - /** - * Construct an OAuth 2 authentication. Since some grant types don't require - * user authentication, the user authentication may be null. The active - * token cannot be null as this is needed for subsequent changes to the - * scopes when writing. - * - * @param authorizationRequest - * The authorization request (must not be null). - * @param userAuthentication - * The user authentication (possibly null). - * @param activeToken - * The token that has been used to authenticate the client - */ - - public OrcidOAuth2Authentication(AuthorizationRequest authorizationRequest, Authentication userAuthentication, String activeToken) { - super(authorizationRequest.createOAuth2Request(), userAuthentication); - Assert.hasText(activeToken, "The active token must have a value."); - this.activeToken = activeToken; - } - - public String getActiveToken() { - return activeToken; - } - -} diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/OrcidOauth2AuthInfo.java b/orcid-core/src/main/java/org/orcid/core/oauth/OrcidOauth2AuthInfo.java deleted file mode 100644 index 9f617402109..00000000000 --- a/orcid-core/src/main/java/org/orcid/core/oauth/OrcidOauth2AuthInfo.java +++ /dev/null @@ -1,72 +0,0 @@ -package org.orcid.core.oauth; - -import java.util.Set; - -import org.orcid.persistence.jpa.entities.ProfileEntity; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.security.oauth2.provider.OAuth2Request; - -public class OrcidOauth2AuthInfo { - - private String clientId; - - private Set scopes; - - private String userOrcid; - - public OrcidOauth2AuthInfo(String clientId, Set scopes, String userOrcid) { - this.clientId = clientId; - this.scopes = scopes; - this.userOrcid = userOrcid; - } - - public OrcidOauth2AuthInfo(OAuth2Authentication oauth2Authentication) { - if (oauth2Authentication != null) { - init(oauth2Authentication.getOAuth2Request(), oauth2Authentication.getUserAuthentication()); - } - } - - private void init(OAuth2Request authRequest, Authentication userAuthentication) { - if (authRequest != null) { - clientId = authRequest.getClientId(); - scopes = authRequest.getScope(); - if (userAuthentication != null) { - Object principal = userAuthentication.getPrincipal(); - if (principal != null) { - if (ProfileEntity.class.isAssignableFrom(principal.getClass())) { - userOrcid = ((ProfileEntity) principal).getId(); - } else if (UserDetails.class.isAssignableFrom(principal.getClass())) { - userOrcid = ((UserDetails) principal).getUsername(); - } - } - } - } - } - - public String getClientId() { - return clientId; - } - - public void setClientId(String clientId) { - this.clientId = clientId; - } - - public Set getScopes() { - return scopes; - } - - public void setScopes(Set scopes) { - this.scopes = scopes; - } - - public String getUserOrcid() { - return userOrcid; - } - - public void setUserOrcid(String userOrcid) { - this.userOrcid = userOrcid; - } - -} diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/OrcidOboOAuth2Authentication.java b/orcid-core/src/main/java/org/orcid/core/oauth/OrcidOboOAuth2Authentication.java deleted file mode 100644 index 0ddd50b8c7f..00000000000 --- a/orcid-core/src/main/java/org/orcid/core/oauth/OrcidOboOAuth2Authentication.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.orcid.core.oauth; - -import org.springframework.security.core.Authentication; -import org.springframework.security.oauth2.provider.AuthorizationRequest; -import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.util.Assert; - -/** - * @author Declan Newman (declan) Date: 10/05/2012 - */ -public class OrcidOboOAuth2Authentication extends OrcidOAuth2Authentication { - private static final long serialVersionUID = 4582917463028491537L; - private String oboClientId; - - /** - * Construct an OAuth 2 authentication. Since some grant types don't require - * user authentication, the user authentication may be null. The active - * token cannot be null as this is needed for subsequent changes to the - * scopes when writing. - * - * @param oboClientId - * The client id of the OBO client - * @param authorizationRequest - * The authorization request (must not be null). - * @param userAuthentication - * The user authentication (possibly null). - * @param activeToken - * The token that has been used to authenticate the client - */ - - public OrcidOboOAuth2Authentication(String oboClientId, AuthorizationRequest authorizationRequest, Authentication userAuthentication, String activeToken) { - super(authorizationRequest, userAuthentication, activeToken); - Assert.hasText(oboClientId, "The oboClientId must have a value."); - this.oboClientId = oboClientId; - } - - public String getOboClientId() { - return oboClientId; - } - -} diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/OrcidRandomValueTokenServices.java b/orcid-core/src/main/java/org/orcid/core/oauth/OrcidRandomValueTokenServices.java deleted file mode 100644 index ff97d162734..00000000000 --- a/orcid-core/src/main/java/org/orcid/core/oauth/OrcidRandomValueTokenServices.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.orcid.core.oauth; - -import java.util.Collection; - -import org.orcid.core.constants.RevokeReason; -import org.orcid.core.oauth.service.OrcidTokenStore; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.oauth2.common.OAuth2AccessToken; -import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; -import org.springframework.security.oauth2.provider.token.TokenEnhancer; - -/** - * @author Angel Montenegro - * */ -public interface OrcidRandomValueTokenServices extends AuthorizationServerTokenServices { - - OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException; - - OAuth2Authentication loadAuthentication(String accessTokenValue) throws AuthenticationException; - - int getWriteValiditySeconds(); - - int getReadValiditySeconds(); - - void setOrcidtokenStore(OrcidTokenStore orcidtokenStore); - - void setCustomTokenEnhancer(TokenEnhancer customTokenEnhancer); - - boolean longLifeTokenExist(String clientId, String userId, Collection scopes); - - OAuth2AccessToken createRevokedAccessToken(OAuth2Authentication authentication, RevokeReason revokeReason) throws AuthenticationException; - -} diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/OrcidRefreshTokenChecker.java b/orcid-core/src/main/java/org/orcid/core/oauth/OrcidRefreshTokenChecker.java deleted file mode 100644 index bf845fd6a9a..00000000000 --- a/orcid-core/src/main/java/org/orcid/core/oauth/OrcidRefreshTokenChecker.java +++ /dev/null @@ -1,106 +0,0 @@ -package org.orcid.core.oauth; - -import java.util.Date; -import java.util.HashSet; -import java.util.Set; - -import javax.annotation.Resource; -import javax.persistence.NoResultException; - -import org.orcid.core.constants.OrcidOauth2Constants; -import org.orcid.core.manager.ClientDetailsEntityCacheManager; -import org.orcid.core.oauth.service.OrcidOAuth2RequestValidator; -import org.orcid.jaxb.model.message.ScopePathType; -import org.orcid.persistence.dao.OrcidOauth2TokenDetailDao; -import org.orcid.persistence.jpa.entities.ClientDetailsEntity; -import org.orcid.persistence.jpa.entities.OrcidOauth2TokenDetail; -import org.orcid.pojo.ajaxForm.PojoUtil; -import org.springframework.security.oauth2.common.exceptions.InvalidScopeException; -import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; -import org.springframework.security.oauth2.common.util.OAuth2Utils; -import org.springframework.security.oauth2.provider.TokenRequest; - -/** - * @author Angel Montenegro - */ -public class OrcidRefreshTokenChecker { - @Resource - private ClientDetailsEntityCacheManager clientDetailsEntityCacheManager; - - @Resource - private OrcidOAuth2RequestValidator orcidOAuth2RequestValidator; - - @Resource - private OrcidOauth2TokenDetailDao orcidOauth2TokenDetailDao; - - public void validateRequest(String grantType, TokenRequest tokenRequest, Long requestTimeInMillis) { - String authorization = tokenRequest.getRequestParameters().get(OrcidOauth2Constants.AUTHORIZATION); - String clientId = tokenRequest.getClientId(); - String scopes = tokenRequest.getRequestParameters().get(OAuth2Utils.SCOPE); - Long expireIn = tokenRequest.getRequestParameters().containsKey(OrcidOauth2Constants.EXPIRES_IN) - ? Long.valueOf(tokenRequest.getRequestParameters().get(OrcidOauth2Constants.EXPIRES_IN)) : 0L; - String refreshToken = tokenRequest.getRequestParameters().get(OrcidOauth2Constants.REFRESH_TOKEN); - - OrcidOauth2TokenDetail token = null; - try { - token = orcidOauth2TokenDetailDao.findByRefreshTokenValue(refreshToken); - } catch (NoResultException e) { - throw new InvalidTokenException("Unable to find refresh token", e); - } - - // Verify the token belongs to this client - if (!clientId.equals(token.getClientDetailsId())) { - throw new IllegalArgumentException("This token does not belong to the given client"); - } - - // Verify client is enabled - ClientDetailsEntity clientDetails = clientDetailsEntityCacheManager.retrieve(clientId); - orcidOAuth2RequestValidator.validateClientIsEnabled(clientDetails); - - // Verify the token is not expired - if (token.getTokenExpiration() != null) { - if (token.getTokenExpiration().before(new Date())) { - throw new InvalidTokenException("Access token expired: " + authorization); - } - } - - // Verify access token and refresh token are linked - if (!refreshToken.equals(token.getRefreshTokenValue())) { - throw new InvalidTokenException("Token and refresh token does not match"); - } - - // Verify the token is not disabled - if (token.getTokenDisabled() != null && token.getTokenDisabled()) { - throw new InvalidTokenException("Parent token is disabled"); - } - - // Verify scopes are not wider than the token scopes - if (PojoUtil.isEmpty(scopes)) { - scopes = token.getScope(); - } else { - Set requiredScopes = ScopePathType.getScopesFromSpaceSeparatedString(scopes); - Set simpleTokenScopes = ScopePathType.getScopesFromSpaceSeparatedString(token.getScope()); - // This collection contains all tokens that should be allowed given - // the scopes that the parent token contains - Set combinedTokenScopes = new HashSet(); - for (ScopePathType scope : simpleTokenScopes) { - combinedTokenScopes.addAll(scope.combined()); - } - - // Check that all requiredScopes are included in the list of - // combinedTokenScopes - for (ScopePathType scope : requiredScopes) { - if (!combinedTokenScopes.contains(scope)) { - throw new InvalidScopeException("The given scope '" + scope.value() + "' is not allowed for the parent token"); - } - } - } - - // Validate the expiration for the new token is no later than the parent - // token expiration. - long parentTokenExpiration = token.getTokenExpiration() == null ? System.currentTimeMillis() : token.getTokenExpiration().getTime(); - if (expireIn > parentTokenExpiration) { - throw new IllegalArgumentException("Token expiration can't be after " + token.getTokenExpiration()); - } - } -} diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/OrcidRefreshTokenTokenGranter.java b/orcid-core/src/main/java/org/orcid/core/oauth/OrcidRefreshTokenTokenGranter.java deleted file mode 100644 index 38c79595a67..00000000000 --- a/orcid-core/src/main/java/org/orcid/core/oauth/OrcidRefreshTokenTokenGranter.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.orcid.core.oauth; - -import org.orcid.core.constants.OrcidOauth2Constants; -import org.springframework.security.oauth2.common.OAuth2AccessToken; -import org.springframework.security.oauth2.provider.TokenGranter; -import org.springframework.security.oauth2.provider.TokenRequest; -import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; - -/** - * @author Angel Montenegro - */ -public class OrcidRefreshTokenTokenGranter implements TokenGranter { - private final OrcidRefreshTokenChecker orcidRefreshTokenChecker; - - private final AuthorizationServerTokenServices tokenServices; - - public OrcidRefreshTokenTokenGranter(OrcidRefreshTokenChecker orcidRefreshTokenChecker, AuthorizationServerTokenServices tokenServices) { - this.orcidRefreshTokenChecker = orcidRefreshTokenChecker; - this.tokenServices = tokenServices; - } - - @Override - public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) { - if (!OrcidOauth2Constants.REFRESH_TOKEN.equals(grantType)) { - return null; - } - - Long requestTimeInMillis = System.currentTimeMillis(); - orcidRefreshTokenChecker.validateRequest(grantType, tokenRequest, requestTimeInMillis); - - //If no exception is thrown we are ready to create the new token - String refreshToken = tokenRequest.getRequestParameters().get(OrcidOauth2Constants.REFRESH_TOKEN); - - return tokenServices.refreshAccessToken(refreshToken, tokenRequest); - } -} diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/OrcidTokenEnhancer.java b/orcid-core/src/main/java/org/orcid/core/oauth/OrcidTokenEnhancer.java deleted file mode 100644 index d725577f6b1..00000000000 --- a/orcid-core/src/main/java/org/orcid/core/oauth/OrcidTokenEnhancer.java +++ /dev/null @@ -1,101 +0,0 @@ -package org.orcid.core.oauth; - -import java.util.HashMap; -import java.util.Map; - -import javax.annotation.Resource; - -import org.orcid.core.constants.OrcidOauth2Constants; -import org.orcid.core.manager.ProfileEntityManager; -import org.orcid.persistence.dao.OrcidOauth2AuthoriziationCodeDetailDao; -import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; -import org.springframework.security.oauth2.common.OAuth2AccessToken; -import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.security.oauth2.provider.OAuth2Request; -import org.springframework.security.oauth2.provider.token.TokenEnhancer; - -public class OrcidTokenEnhancer implements TokenEnhancer { - @Resource - private ProfileEntityManager profileEntityManager; - - @Resource - private OrcidOauth2AuthoriziationCodeDetailDao orcidOauth2AuthoriziationCodeDetailDao; - - @Override - public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) { - if (!(accessToken instanceof DefaultOAuth2AccessToken)) - throw new UnsupportedOperationException("At this time we can handle only tokens of type DefaultOauth2AccessToken"); - DefaultOAuth2AccessToken result = (DefaultOAuth2AccessToken) accessToken; - OrcidOauth2AuthInfo authInfo = new OrcidOauth2AuthInfo(authentication); - String userOrcid = authInfo.getUserOrcid(); - - Map additionalInfo = new HashMap(); - if (result.getAdditionalInformation() != null && !result.getAdditionalInformation().isEmpty()) { - additionalInfo.putAll(result.getAdditionalInformation()); - } - - if (!OrcidOauth2Constants.IMPLICIT_GRANT_TYPE.equals(authentication.getOAuth2Request().getGrantType())){ - // If the additional info object already contains the orcid info, leave - // it - if (!additionalInfo.containsKey("orcid")) { - additionalInfo.put("orcid", userOrcid); - } - - // If the additional info object already contains the name info, leave - // it - if (!additionalInfo.containsKey("name")) { - if (userOrcid != null) { - String name = profileEntityManager.retrivePublicDisplayName(userOrcid); - additionalInfo.put("name", name); - } - } - } - - // Overwrite token version - additionalInfo.put(OrcidOauth2Constants.TOKEN_VERSION, OrcidOauth2Constants.PERSISTENT_TOKEN); - - // Overwrite persistent flag - if (isPersistentTokenEnabled(authentication.getOAuth2Request())) { - additionalInfo.put(OrcidOauth2Constants.PERSISTENT, true); - } else { - additionalInfo.put(OrcidOauth2Constants.PERSISTENT, false); - } - - // Put the updated additional info object in the result - result.setAdditionalInformation(additionalInfo); - - return result; - } - - /** - * Checks the authorization code to verify if the user enable the persistent - * token or not - * */ - private boolean isPersistentTokenEnabled(OAuth2Request authorizationRequest) { - if (authorizationRequest != null) { - Map params = authorizationRequest.getRequestParameters(); - if (params != null) { - if (params.containsKey(OrcidOauth2Constants.IS_PERSISTENT)) { - String isPersistent = params.get(OrcidOauth2Constants.IS_PERSISTENT); - if (Boolean.valueOf(isPersistent)) { - return true; - } - } else if (params.containsKey("code")) { - String code = params.get("code"); - if (orcidOauth2AuthoriziationCodeDetailDao.find(code) != null) { - if (orcidOauth2AuthoriziationCodeDetailDao.isPersistentToken(code)) { - return true; - } - } - } else if (params.get(OrcidOauth2Constants.RESPONSE_TYPE_PARAM) != null - && params.get(OrcidOauth2Constants.RESPONSE_TYPE_PARAM).contains(OrcidOauth2Constants.IMPLICIT_TOKEN_RESPONSE_TYPE) - && "true".equals(params.get(OrcidOauth2Constants.GRANT_PERSISTENT_TOKEN))){ - //for implicit generation, use the query string parameter. - return true; - } - } - } - - return false; - } -} diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/OrcidOauth2UserAuthentication.java b/orcid-core/src/main/java/org/orcid/core/oauth/OrcidUserAuthentication.java similarity index 97% rename from orcid-core/src/main/java/org/orcid/core/oauth/OrcidOauth2UserAuthentication.java rename to orcid-core/src/main/java/org/orcid/core/oauth/OrcidUserAuthentication.java index 77d1fdd28b4..6fec889b48d 100644 --- a/orcid-core/src/main/java/org/orcid/core/oauth/OrcidOauth2UserAuthentication.java +++ b/orcid-core/src/main/java/org/orcid/core/oauth/OrcidUserAuthentication.java @@ -15,7 +15,7 @@ /** * @author Declan Newman (declan) Date: 17/04/2012 */ -public class OrcidOauth2UserAuthentication implements Authentication { +public class OrcidUserAuthentication implements Authentication { /** * @@ -27,7 +27,7 @@ public class OrcidOauth2UserAuthentication implements Authentication { @Resource(name = "recordNameManagerReadOnlyV3") private RecordNameManagerReadOnly recordNameManagerReadOnlyV3; - public OrcidOauth2UserAuthentication(ProfileEntity profileEntity, boolean authenticated) { + public OrcidUserAuthentication(ProfileEntity profileEntity, boolean authenticated) { Assert.notNull(profileEntity, "Cannot instantiate an OrcidOauth2UserAuthentication will a null profile"); this.profileEntity = profileEntity; this.authenticated = authenticated; diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/openid/OpenIDConnectUserInfo.java b/orcid-core/src/main/java/org/orcid/core/oauth/openid/OpenIDConnectUserInfo.java deleted file mode 100644 index 6db95f308e5..00000000000 --- a/orcid-core/src/main/java/org/orcid/core/oauth/openid/OpenIDConnectUserInfo.java +++ /dev/null @@ -1,67 +0,0 @@ -package org.orcid.core.oauth.openid; - -import org.orcid.jaxb.model.v3.release.record.Person; -import org.orcid.jaxb.model.v3.release.record.PersonalDetails; - -public class OpenIDConnectUserInfo { - - private String id; - private String sub; - private String name; - private String family_name; - private String given_name; - - public OpenIDConnectUserInfo(){ - - } - - public OpenIDConnectUserInfo(String orcid, Person person, String path) { - this.id = path+"/"+orcid; - this.sub = orcid; - if (person.getName() != null){ - if (person.getName().getCreditName() != null){ - this.name = person.getName().getCreditName().getContent(); - } - if (person.getName().getFamilyName() != null){ - this.family_name = person.getName().getFamilyName().getContent(); - } - if (person.getName().getGivenNames() != null){ - this.given_name = person.getName().getGivenNames().getContent(); - } - } - } - public OpenIDConnectUserInfo(String orcid, PersonalDetails person, String path) { - this.id = path+"/"+orcid; - this.sub = orcid; - if (person.getName() != null){ - if (person.getName().getCreditName() != null){ - this.name = person.getName().getCreditName().getContent(); - } - if (person.getName().getFamilyName() != null){ - this.family_name = person.getName().getFamilyName().getContent(); - } - if (person.getName().getGivenNames() != null){ - this.given_name = person.getName().getGivenNames().getContent(); - } - } - } - public String getId() { - return id; - } - public void setId(String id) { - this.id = id; - } - public String getName() { - return name; - } - public String getSub() { - return sub; - } - public String getFamily_name() { - return family_name; - } - public String getGiven_name() { - return given_name; - } - -} diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/security/OrcidOauthRedirectResolver.java b/orcid-core/src/main/java/org/orcid/core/oauth/security/OrcidOauthRedirectResolver.java deleted file mode 100644 index 4c790a3ad92..00000000000 --- a/orcid-core/src/main/java/org/orcid/core/oauth/security/OrcidOauthRedirectResolver.java +++ /dev/null @@ -1,96 +0,0 @@ -package org.orcid.core.oauth.security; - -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; - -import org.springframework.security.oauth2.common.exceptions.InvalidGrantException; -import org.springframework.security.oauth2.common.exceptions.InvalidRequestException; -import org.springframework.security.oauth2.common.exceptions.OAuth2Exception; -import org.springframework.security.oauth2.common.exceptions.RedirectMismatchException; -import org.springframework.security.oauth2.provider.ClientDetails; -import org.springframework.security.oauth2.provider.endpoint.DefaultRedirectResolver; -import org.springframework.util.StringUtils; -import org.springframework.web.util.UriComponents; -import org.springframework.web.util.UriComponentsBuilder; - -public class OrcidOauthRedirectResolver extends DefaultRedirectResolver { - - private Collection redirectGrantTypes = Arrays.asList("implicit", "authorization_code"); - - public OrcidOauthRedirectResolver() { - - } - - @Override - public void setRedirectGrantTypes(Collection redirectGrantTypes) { - this.redirectGrantTypes = new HashSet(redirectGrantTypes); - } - - @Override - public String resolveRedirect(String requestedRedirect, ClientDetails client) throws OAuth2Exception { - Set authorizedGrantTypes = client.getAuthorizedGrantTypes(); - if (authorizedGrantTypes.isEmpty()) { - throw new InvalidGrantException("A client must have at least one authorized grant type."); - } - if (!containsRedirectGrantType(authorizedGrantTypes)) { - throw new InvalidGrantException("A redirect_uri can only be used by implicit or authorization_code grant types."); - } - - Set registeredRedirectUris = client.getRegisteredRedirectUri(); - if (registeredRedirectUris == null || registeredRedirectUris.isEmpty()) { - throw new InvalidRequestException("At least one redirect_uri must be registered with the client."); - } - - // There must be at least one redirect uri that is the root of the requested redirect uri - for(String registeredRedirectUri : registeredRedirectUris) { - if(requestedRedirect != null && redirectMatches(requestedRedirect, registeredRedirectUri.trim())) { - return requestedRedirect; - } - } - throw new RedirectMismatchException("Unable to find a matching redirect_uri for the client."); - } - - /** - * @param grantTypes - * some grant types - * @return true if the supplied grant types includes one or more of the - * redirect types - */ - private boolean containsRedirectGrantType(Set grantTypes) { - for (String type : grantTypes) { - if (redirectGrantTypes.contains(type)) { - return true; - } - } - return false; - } - - @Override - protected boolean redirectMatches(String requestedRedirect, String redirectUri) { - UriComponents requestedRedirectUri = UriComponentsBuilder.fromUriString(requestedRedirect).build(); - UriComponents registeredRedirectUri = UriComponentsBuilder.fromUriString(redirectUri).build(); - - String requestedRedirectUriPath = (requestedRedirectUri.getPath() != null ? requestedRedirectUri.getPath() : ""); - String registeredRedirectUriPath = (registeredRedirectUri.getPath() != null ? registeredRedirectUri.getPath() : ""); - - boolean portMatch = registeredRedirectUri.getPort() == requestedRedirectUri.getPort(); - boolean hostMatch = hostMatches(registeredRedirectUri.getHost(), requestedRedirectUri.getHost()); - boolean schemeMatch = isEqual(registeredRedirectUri.getScheme(), requestedRedirectUri.getScheme()); - boolean userInfoMatch = isEqual(registeredRedirectUri.getUserInfo(), requestedRedirectUri.getUserInfo()); - boolean pathMatch = StringUtils.cleanPath(requestedRedirectUriPath).startsWith(StringUtils.cleanPath(registeredRedirectUriPath)); - - return schemeMatch && userInfoMatch && hostMatch && portMatch && pathMatch; - } - - private boolean isEqual(String str1, String str2) { - return Objects.equals(str1, str2); - } - - @Override - protected boolean hostMatches(String registered, String requested) { - return isEqual(registered, requested); - } -} diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/service/NamespacedRandomCodeGenerator.java b/orcid-core/src/main/java/org/orcid/core/oauth/service/NamespacedRandomCodeGenerator.java deleted file mode 100644 index 75de0827790..00000000000 --- a/orcid-core/src/main/java/org/orcid/core/oauth/service/NamespacedRandomCodeGenerator.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.orcid.core.oauth.service; - -import java.util.concurrent.ThreadLocalRandom; -import java.util.concurrent.TimeUnit; - -import org.springframework.security.oauth2.common.util.RandomValueStringGenerator; - -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; - -public class NamespacedRandomCodeGenerator { - - private static final Cache codeCache = CacheBuilder.newBuilder().maximumSize(100000).expireAfterWrite(10, TimeUnit.MINUTES).build(); - private RandomValueStringGenerator generator = new RandomValueStringGenerator(5); - private static final String base62 = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; - - private int start; - private int end; - - /** Each node needs a local cache of authentication codes. - * When creating a new code the cache is checked to ensure it's not already been issued. - * If it has, regenerate until unique code is used. - * - * Each node requires its own code namespace in order to prevent clashes across nodes. - * This will be the first character of the authorisation code. - * Note this must be a range of prefixes, not a single char. - * Using a single char would reduce the amount of randomness drastically. - * - * Split the base62 namespace into 3 chunks, last chuck may be bigger than the others. - * Use a namespaced random char + 5 random chars to create code. - */ - public NamespacedRandomCodeGenerator(int node, int numberOfNodes){ - int range = 62 / numberOfNodes; - start = (node - 1) * range; - end = start + range - 1; - if (node == numberOfNodes) - end = 61; - } - - /** - * @return a 6 char base62 code specific to this node. - */ - public synchronized String nextRandomCode(){ - String suffix = generator.generate(); - String code; - do { - int position = ThreadLocalRandom.current().nextInt(end - start + 1);//excludes last number - code = base62.charAt(position + start) + suffix; - } while (codeCache.getIfPresent(code) != null); - codeCache.put(code, code); - return code; - }; -} diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidAuthorizationCodeServiceImpl.java b/orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidAuthorizationCodeServiceImpl.java deleted file mode 100644 index 4e29910b401..00000000000 --- a/orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidAuthorizationCodeServiceImpl.java +++ /dev/null @@ -1,196 +0,0 @@ -package org.orcid.core.oauth.service; - -import java.io.Serializable; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import javax.annotation.Resource; -import javax.persistence.NoResultException; - -import org.apache.commons.lang.StringUtils; -import org.orcid.core.constants.OrcidOauth2Constants; -import org.orcid.core.manager.ClientDetailsEntityCacheManager; -import org.orcid.core.manager.ProfileEntityCacheManager; -import org.orcid.core.oauth.OrcidOauth2AuthInfo; -import org.orcid.core.oauth.OrcidOauth2UserAuthentication; -import org.orcid.persistence.dao.OrcidOauth2AuthoriziationCodeDetailDao; -import org.orcid.persistence.jpa.entities.ClientDetailsEntity; -import org.orcid.persistence.jpa.entities.OrcidOauth2AuthoriziationCodeDetail; -import org.orcid.persistence.jpa.entities.ProfileEntity; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.oauth2.common.util.OAuth2Utils; -import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.security.oauth2.provider.OAuth2Request; -import org.springframework.security.oauth2.provider.code.RandomValueAuthorizationCodeServices; -import org.springframework.security.web.authentication.WebAuthenticationDetails; -import org.springframework.stereotype.Service; - -/** - * @author Declan Newman (declan) Date: 23/04/2012 - */ -@Service("orcidAuthorizationCodeService") -public class OrcidAuthorizationCodeServiceImpl extends RandomValueAuthorizationCodeServices { - - private static final String CLIENT_ID = "client_id"; - - private static final String STATE = "state"; - - private static final String SCOPE = "scope"; - - private static final String REDIRECT_URI = "redirect_uri"; - - private static final String RESPONSE_TYPE = "response_type"; - - @Resource(name = "orcidOauth2AuthoriziationCodeDetailDao") - private OrcidOauth2AuthoriziationCodeDetailDao orcidOauth2AuthoriziationCodeDetailDao; - - @Resource(name = "profileEntityCacheManager") - private ProfileEntityCacheManager profileEntityCacheManager; - - @Resource - private ClientDetailsEntityCacheManager clientDetailsEntityCacheManager; - - private static final Logger LOGGER = LoggerFactory.getLogger(OrcidAuthorizationCodeServiceImpl.class); - - @Resource - private NamespacedRandomCodeGenerator generator; - - @Override - public String createAuthorizationCode(OAuth2Authentication authentication) { - String code = generator.nextRandomCode(); - store(code, authentication); - return code; - } - - @Override - protected void store(String code, OAuth2Authentication authentication) { - OrcidOauth2AuthoriziationCodeDetail detail = getDetailFromAuthorization(code, authentication); - if (detail == null) { - throw new IllegalArgumentException("Cannot persist the authorisation code as the user and/or client " + "cannot be found"); - } - orcidOauth2AuthoriziationCodeDetailDao.persist(detail); - OrcidOauth2AuthInfo authInfo = new OrcidOauth2AuthInfo(authentication); - LOGGER.info("Storing authorization code: code={}, clientId={}, scopes={}, userOrcid={}", new Object[] { code, authInfo.getClientId(), authInfo.getScopes(), - authInfo.getUserOrcid() }); - } - - @Override - protected OAuth2Authentication remove(String code) { - OrcidOauth2AuthoriziationCodeDetail detail = orcidOauth2AuthoriziationCodeDetailDao.removeAndReturn(code); - if (detail == null) { - LOGGER.info("No such authorization code to remove: code={}", - new Object[] { code }); - return null; - } - - //////// - // TODO: The name should change to `scopes` once the authorization server generates all authorization codes - //////// - Set newScopes = StringUtils.isNotBlank(detail.getNewScopes()) ? Stream.of(detail.getNewScopes().split(",")).map(String::trim).collect(Collectors.toSet()) : Set.of(); - Set scopes = detail.getScopes(); - scopes.addAll(newScopes); - String clientId = detail.getClientDetailsEntity().getId(); - - LOGGER.info("Removed authorization code: code={}, clientId={}, scopes={}, userOrcid={}", new Object[] { code, clientId, scopes, - detail.getOrcid() }); - - - OAuth2Request oAuth2Request = new OAuth2Request(Collections. emptyMap(), clientId, Collections. emptyList(), true, scopes, detail.getResourceIds(), detail.getRedirectUri(), new HashSet(Arrays.asList(detail.getResponseType())), Collections. emptyMap()); - Authentication userAuth = getUserAuthentication(detail); - return new OAuth2Authentication(oAuth2Request, userAuth); - } - - private OrcidOauth2UserAuthentication getUserAuthentication(OrcidOauth2AuthoriziationCodeDetail detail) { - ProfileEntity profile = profileEntityCacheManager.retrieve(detail.getOrcid()); - return new OrcidOauth2UserAuthentication(profile, detail.getAuthenticated()); - } - - private OrcidOauth2AuthoriziationCodeDetail getDetailFromAuthorization(String code, OAuth2Authentication authentication) { - OAuth2Request oAuth2Request = authentication.getOAuth2Request(); - OrcidOauth2AuthoriziationCodeDetail detail = new OrcidOauth2AuthoriziationCodeDetail(); - Map requestParameters = oAuth2Request.getRequestParameters(); - if (requestParameters != null && !requestParameters.isEmpty()) { - String clientId = (String) requestParameters.get(CLIENT_ID); - ClientDetailsEntity clientDetails = getClientDetails(clientId); - - if (clientDetails == null) { - return null; - } - - detail.setScopes(OAuth2Utils.parseParameterList((String)requestParameters.get(SCOPE))); - detail.setState((String)requestParameters.get(STATE)); - detail.setRedirectUri((String)requestParameters.get(REDIRECT_URI)); - detail.setResponseType((String)requestParameters.get(RESPONSE_TYPE)); - detail.setClientDetailsEntity(clientDetails); - - //persist the openID params if present - if (requestParameters.get(OrcidOauth2Constants.NONCE) != null) - detail.setNonce((String)requestParameters.get(OrcidOauth2Constants.NONCE)); - } - - detail.setId(code); - detail.setApproved(authentication.getOAuth2Request().isApproved()); - Authentication userAuthentication = authentication.getUserAuthentication(); - Object principal = userAuthentication.getDetails(); - - String orcid = userAuthentication.getName(); - - if (orcid == null) { - return null; - } - - detail.setOrcid(orcid); - detail.setAuthenticated(userAuthentication.isAuthenticated()); - Set authorities = getStringSetFromGrantedAuthorities(authentication.getAuthorities()); - detail.setAuthorities(authorities); - Object authenticationDetails = userAuthentication.getDetails(); - if (authenticationDetails instanceof WebAuthenticationDetails) { - detail.setSessionId(((WebAuthenticationDetails) authenticationDetails).getSessionId()); - } - - boolean isPersistentTokenEnabledByUser = false; - //Set token version to persistent token - //TODO: As of Jan 2015 all tokens will be new tokens, so, we will have to remove the token version code and - //treat all tokens as new tokens - detail.setVersion(Long.valueOf(OrcidOauth2Constants.PERSISTENT_TOKEN)); - if(requestParameters.containsKey(OrcidOauth2Constants.GRANT_PERSISTENT_TOKEN)) { - String grantPersitentToken = (String)requestParameters.get(OrcidOauth2Constants.GRANT_PERSISTENT_TOKEN); - if(Boolean.parseBoolean(grantPersitentToken)) { - isPersistentTokenEnabledByUser = true; - } - } - - detail.setPersistent(isPersistentTokenEnabledByUser); - - return detail; - } - - private ClientDetailsEntity getClientDetails(String clientId) { - try { - return clientDetailsEntityCacheManager.retrieve(clientId); - } catch (NoResultException e) { - return null; - } - } - - private Set getStringSetFromGrantedAuthorities(Collection authorities) { - Set stringSet = new HashSet(); - if (authorities != null && !authorities.isEmpty()) { - for (GrantedAuthority authority : authorities) { - stringSet.add(authority.getAuthority()); - } - } - return stringSet; - } - -} diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidRandomValueTokenServicesImpl.java b/orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidRandomValueTokenServicesImpl.java index dd1755d9001..6fee9a4f801 100644 --- a/orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidRandomValueTokenServicesImpl.java +++ b/orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidRandomValueTokenServicesImpl.java @@ -39,13 +39,16 @@ import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; +import org.orcid.core.exception.InvalidTokenException import com.google.common.collect.Sets; /** * @author Declan Newman (declan) Date: 11/05/2012 */ -public class OrcidRandomValueTokenServicesImpl extends DefaultTokenServices implements OrcidRandomValueTokenServices { +@Deprecated +//TODO: This have to be removed!!!! +public class OrcidRandomValueTokenServicesImpl { private static final Logger LOGGER = LoggerFactory.getLogger(OrcidRandomValueTokenServicesImpl.class); @Value("${org.orcid.core.token.write_validity_seconds:3600}") @@ -57,207 +60,11 @@ public class OrcidRandomValueTokenServicesImpl extends DefaultTokenServices impl @Value("${org.orcid.core.token.write_validity_seconds:3600}") private int ietfTokenExchangeValiditySeconds; - private OrcidTokenStore orcidTokenStore; - - private TokenEnhancer customTokenEnhancer; - - @Resource(name="orcidOauth2AuthoriziationCodeDetailDaoReadOnly") - private OrcidOauth2AuthoriziationCodeDetailDao orcidOauth2AuthoriziationCodeDetailDaoReadOnly; - - @Resource - private OrcidOAuth2RequestValidator orcidOAuth2RequestValidator; - @Resource - private ClientDetailsEntityCacheManager clientDetailsEntityCacheManager; - - private boolean customSupportRefreshToken; - - @Resource(name="orcidOauth2TokenDetailDao") - private OrcidOauth2TokenDetailDao orcidOauth2TokenDetailDao; - - @Resource - private ProfileEntityManager profileEntityManager; - - @Resource - private AuthenticationKeyGenerator authenticationKeyGenerator; - - @Resource - private RedisClient redisClient; - - @Value("${org.orcid.core.utils.cache.redis.enabled:true}") - private boolean isTokenCacheEnabled; - @Resource private AuthorizationServerUtil authorizationServerUtil; - public void setCustomSupportRefreshToken(boolean customSupportRefreshToken) { - this.customSupportRefreshToken = customSupportRefreshToken; - } - - @Override - public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException { - - //Do we have an implicit request with update permissions? - if (OrcidOauth2Constants.IMPLICIT_GRANT_TYPE.equals(authentication.getOAuth2Request().getGrantType())) { - // authentication.getOAuth2Request().getResponseTypes().contains("token")) { - Collection combinedStrings = ScopePathType.getCombinedScopesFromStringsAsStrings(authentication.getOAuth2Request().getScope()); - Set requestedScopes = ScopePathType.getScopesFromStrings(combinedStrings); - Set allowedScopes = Sets.newHashSet(); - for (ScopePathType s: requestedScopes) { - if (ScopePathType.AUTHENTICATE.equals(s) || ScopePathType.READ_PUBLIC.equals(s) || ScopePathType.OPENID.equals(s)) { - allowedScopes.add(s.value()); - } - } - //If so, create a long life update token in the background - if (requestedScopes.size() != allowedScopes.size()) { - //create token behind the scenes. - DefaultOAuth2AccessToken accessToken = new DefaultOAuth2AccessToken(UUID.randomUUID().toString()); - accessToken.setExpiration(new Date(System.currentTimeMillis() + (readValiditySeconds * 1000L))); - accessToken.setScope(authentication.getOAuth2Request().getScope()); - accessToken = new DefaultOAuth2AccessToken(customTokenEnhancer.enhance(accessToken, authentication)); - //accessToken.setAdditionalInformation(Maps.newHashMap()); - orcidTokenStore.storeAccessToken(accessToken, authentication); - - //create a read only token request - OAuth2Request r = authentication.getOAuth2Request().narrowScope(allowedScopes); - authentication = new OAuth2Authentication(r, authentication.getUserAuthentication()); - } - } - - //create the regular token - DefaultOAuth2AccessToken accessToken = generateAccessToken(authentication); - try { - orcidTokenStore.storeAccessToken(accessToken, authentication); - } catch(PersistenceException e) { - // In the unlikely case that there is a constraint violation, lets try to generate the token one more time - if(e.getCause() instanceof ConstraintViolationException) { - accessToken = generateAccessToken(authentication); - try { - orcidTokenStore.storeAccessToken(accessToken, authentication); - return accessToken; - } catch(Exception e2) { - // Just throw the first exception - } - - } - OrcidOauth2AuthInfo authInfo = new OrcidOauth2AuthInfo(authentication); - LOGGER.error("Exception creating access token for client {}, scopes {} and user {}", new Object[] {authInfo.getClientId(), authInfo.getScopes(), authInfo.getUserOrcid()}); - LOGGER.error("Error info", e); - throw e; - } - return accessToken; - } - - private DefaultOAuth2AccessToken generateAccessToken(OAuth2Authentication authentication) { - DefaultOAuth2AccessToken accessToken = new DefaultOAuth2AccessToken(UUID.randomUUID().toString()); - int validitySeconds = getAccessTokenValiditySeconds(authentication.getOAuth2Request()); - if (validitySeconds > 0) { - accessToken.setExpiration(new Date(System.currentTimeMillis() + (validitySeconds * 1000L))); - } - accessToken.setScope(authentication.getOAuth2Request().getScope()); - - if(customTokenEnhancer != null) { - accessToken = new DefaultOAuth2AccessToken(customTokenEnhancer.enhance(accessToken, authentication)); - } - - if(!OrcidOauth2Constants.IETF_EXCHANGE_GRANT_TYPE.equals(authentication.getOAuth2Request().getGrantType()) - && this.isSupportRefreshToken(authentication.getOAuth2Request())) { - OAuth2RefreshToken refreshToken = new DefaultOAuth2RefreshToken(UUID.randomUUID().toString()); - accessToken.setRefreshToken(refreshToken); - } - return accessToken; - } - - /** - * The access token validity period in seconds - * - * @param authorizationRequest - * the current authorization request - * @return the access token validity period in seconds - */ @Override - protected int getAccessTokenValiditySeconds(OAuth2Request authorizationRequest) { - Set requestedScopes = ScopePathType.getScopesFromStrings(authorizationRequest.getScope()); - if (isClientCredentialsGrantType(authorizationRequest)) { - boolean allAreClientCredentialsScopes = true; - - if (requestedScopes.size() == 1) { - if (requestedScopes.stream().findFirst().get().isInternalScope()) { - return readValiditySeconds; - } - } - - for (ScopePathType scope : requestedScopes) { - if (!scope.isClientCreditalScope()) { - allAreClientCredentialsScopes = false; - break; - } - } - - if (allAreClientCredentialsScopes) { - return readValiditySeconds; - } - } else if(OrcidOauth2Constants.IMPLICIT_GRANT_TYPE.equals(authorizationRequest.getGrantType())){ - return implicitValiditySeconds; - } else if(OrcidOauth2Constants.IETF_EXCHANGE_GRANT_TYPE.equals(authorizationRequest.getGrantType())) { - return ietfTokenExchangeValiditySeconds; - } else if (isPersistentTokenEnabled(authorizationRequest)) { - return readValiditySeconds; - } - - return writeValiditySeconds; - } - - public int getWriteValiditySeconds() { - return writeValiditySeconds; - } - - public int getReadValiditySeconds() { - return readValiditySeconds; - } - - /** - * Checks the authorization code to verify if the user enable the persistent - * token or not - * */ - private boolean isPersistentTokenEnabled(OAuth2Request authorizationRequest) { - if (authorizationRequest != null) { - Map params = authorizationRequest.getRequestParameters(); - if (params != null) { - if (params.containsKey(OrcidOauth2Constants.IS_PERSISTENT)) { - String isPersistent = params.get(OrcidOauth2Constants.IS_PERSISTENT); - if (Boolean.valueOf(isPersistent)) { - return true; - } - } else if (params.containsKey("code")) { - String code = params.get("code"); - if (orcidOauth2AuthoriziationCodeDetailDaoReadOnly.find(code) != null) { - if (orcidOauth2AuthoriziationCodeDetailDaoReadOnly.isPersistentToken(code)) { - return true; - } - } - } - } - } - - return false; - } - - /** - * Checks if the authorization request grant type is client_credentials - * */ - private boolean isClientCredentialsGrantType(OAuth2Request authorizationRequest) { - Map params = authorizationRequest.getRequestParameters(); - if (params != null) { - if (params.containsKey(OrcidOauth2Constants.GRANT_TYPE)) { - String grantType = params.get(OrcidOauth2Constants.GRANT_TYPE); - if (OrcidOauth2Constants.GRANT_TYPE_CLIENT_CREDENTIALS.equals(grantType)) - return true; - } - } - return false; - } - - private OAuth2Authentication loadAuthenticationFromAuthorizationServer(String accessTokenValue) { + public OAuth2Authentication loadAuthentication(String accessTokenValue) throws AuthenticationException { try { JSONObject tokenInfo = authorizationServerUtil.tokenIntrospection(accessTokenValue); if(tokenInfo == null) { @@ -288,7 +95,7 @@ private OAuth2Authentication loadAuthenticationFromAuthorizationServer(String ac } } - private OrcidOAuth2Authentication buildAuthentication(String accessTokenValue, JSONObject tokenInfo) throws JSONException { + private OrcidBearerTokenAuthentication buildAuthentication(String accessTokenValue, JSONObject tokenInfo) throws JSONException { // What will we get when the token does not exists? String clientId = tokenInfo.getString("client_id"); Authentication authentication = null; @@ -316,227 +123,4 @@ private OrcidOAuth2Authentication buildAuthentication(String accessTokenValue, J return new OrcidOAuth2Authentication(request, authentication, accessTokenValue); } - - @Override - public OAuth2Authentication loadAuthentication(String accessTokenValue) throws AuthenticationException { - if(Features.OAUTH_TOKEN_VALIDATION.isActive()) { - if(LOGGER.isTraceEnabled()) { - LOGGER.trace("Using the auth server to validate token"); - } - return loadAuthenticationFromAuthorizationServer(accessTokenValue); - } else { - ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); - // Feature flag: If the request is to delete an element, allow - if (RequestMethod.DELETE.name().equals(attr.getRequest().getMethod())) { - OrcidOauth2TokenDetail orcidAccessToken = orcidTokenStore.readOrcidOauth2TokenDetail(accessTokenValue); - if (orcidAccessToken == null) { - // Access token not found - throw new InvalidTokenException("Invalid access token: " + accessTokenValue); - } - String revokeReason = orcidAccessToken.getRevokeReason(); - if (PojoUtil.isEmpty(revokeReason) || RevokeReason.USER_REVOKED.equals(RevokeReason.valueOf(revokeReason))) { - OAuth2AccessToken accessToken = orcidTokenStore.readEvenDisabledAccessToken(accessTokenValue); - validateTokenExpirationAndClientStatus(accessToken, accessTokenValue); - return orcidTokenStore.readAuthenticationEvenOnDisabledTokens(accessTokenValue); - } else { - throw new InvalidTokenException("Invalid access token: " + accessTokenValue + ", revoke reason: " + revokeReason); - } - } else { - // Get the token from the cache - Map cachedAccessToken = getTokenFromCache(accessTokenValue); - if (cachedAccessToken != null) { - return orcidTokenStore.readAuthenticationFromCachedToken(cachedAccessToken); - } else { - // Fallback to database if it is not in the cache - OAuth2AccessToken accessToken = orcidTokenStore.readAccessToken(accessTokenValue); - validateTokenExpirationAndClientStatus(accessToken, accessTokenValue); - return orcidTokenStore.readAuthentication(accessTokenValue); - } - } - } - } - - private Map getTokenFromCache(String accessTokenValue) { - if(isTokenCacheEnabled) { - String tokenJsonInfo = redisClient.get(accessTokenValue); - if(StringUtils.isNotBlank(tokenJsonInfo)) { - return JsonUtils.readObjectFromJsonString(tokenJsonInfo, HashMap.class); - } - } - return null; - } - - private void validateTokenExpirationAndClientStatus(OAuth2AccessToken accessToken, String tokenValue) { - // Throw an exception if access token is not found - if (accessToken != null) { - // Throw an exception if the token is expired - if (accessToken.isExpired()) { - orcidTokenStore.removeAccessToken(accessToken); - throw new InvalidTokenException("Access token expired: " + tokenValue); - } - Map additionalInfo = accessToken.getAdditionalInformation(); - if (additionalInfo != null) { - String clientId = (String) additionalInfo.get(OrcidOauth2Constants.CLIENT_ID); - orcidTokenStore.isClientEnabled(clientId); - } - } else { - // Access token not found - throw new InvalidTokenException("Invalid access token: " + tokenValue); - } - } - - public void setOrcidtokenStore(OrcidTokenStore orcidTokenStore) { - super.setTokenStore(orcidTokenStore); - this.orcidTokenStore = orcidTokenStore; - } - - public void setCustomTokenEnhancer(TokenEnhancer customTokenEnhancer) { - super.setTokenEnhancer(customTokenEnhancer); - this.customTokenEnhancer = customTokenEnhancer; - } - - public boolean longLifeTokenExist(String clientId, String userId, Collection scopes) { - Collection existingTokens = orcidTokenStore.findTokensByClientIdAndUserName(clientId, userId); - - if(existingTokens == null || existingTokens.isEmpty()) { - return false; - } - - for(OAuth2AccessToken token : existingTokens) { - if (token.getAdditionalInformation().get(OrcidOauth2Constants.PERSISTENT) != null && Boolean.valueOf((token.getAdditionalInformation().get("persistent").toString()))){ - if(token.getScope().containsAll(scopes) && scopes.containsAll(token.getScope())) { - return true; - } - } - } - - return false; - } - - //see https://github.com/spring-projects/spring-security-oauth/pull/957#issuecomment-304729865 - @Override - @Transactional - public OAuth2AccessToken refreshAccessToken(String refreshTokenValue, TokenRequest tokenRequest) throws AuthenticationException { - String parentTokenValue = tokenRequest.getRequestParameters().get(OrcidOauth2Constants.AUTHORIZATION); - String clientId = tokenRequest.getClientId(); - String scopes = tokenRequest.getRequestParameters().get(OAuth2Utils.SCOPE); - Long expiresIn = tokenRequest.getRequestParameters().containsKey(OrcidOauth2Constants.EXPIRES_IN) - ? Long.valueOf(tokenRequest.getRequestParameters().get(OrcidOauth2Constants.EXPIRES_IN)) : 0L; - Boolean revokeOld = tokenRequest.getRequestParameters().containsKey(OrcidOauth2Constants.REVOKE_OLD) ? Boolean.valueOf(tokenRequest.getRequestParameters().get(OrcidOauth2Constants.REVOKE_OLD)) : true; - - // Check if the refresh token is enabled - if (!customSupportRefreshToken) { - throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue); - } - // Check if the client support refresh token - ClientDetailsEntity clientDetails = clientDetailsEntityCacheManager.retrieve(clientId); - if (!clientDetails.getAuthorizedGrantTypes().contains(OrcidOauth2Constants.REFRESH_TOKEN)) { - throw new InvalidGrantException("Client " + clientId + " doesnt have refresh token enabled"); - } - - OrcidOauth2TokenDetail parentToken = orcidOauth2TokenDetailDao.findByRefreshTokenValue(refreshTokenValue); - - ProfileEntity profileEntity = new ProfileEntity(parentToken.getOrcid()); - OrcidOauth2TokenDetail newToken = new OrcidOauth2TokenDetail(); - - newToken.setApproved(true); - newToken.setClientDetailsId(clientId); - newToken.setPersistent(parentToken.isPersistent()); - newToken.setOrcid(parentToken.getOrcid()); - newToken.setRedirectUri(parentToken.getRedirectUri()); - newToken.setRefreshTokenValue(UUID.randomUUID().toString()); - newToken.setResourceId(parentToken.getResourceId()); - newToken.setResponseType(parentToken.getResponseType()); - newToken.setState(parentToken.getState()); - newToken.setTokenDisabled(false); - if(expiresIn <= 0) { - //If expiresIn is 0 or less, set the parent token - newToken.setTokenExpiration(parentToken.getTokenExpiration()); - } else { - //Assumes expireIn already contains the real expired time expressed in millis - newToken.setTokenExpiration(new Date(expiresIn)); - } - newToken.setTokenType(parentToken.getTokenType()); - newToken.setTokenValue(UUID.randomUUID().toString()); - newToken.setVersion(parentToken.getVersion()); - - if (PojoUtil.isEmpty(scopes)) { - newToken.setScope(parentToken.getScope()); - } else { - newToken.setScope(scopes); - } - - //Generate an authentication object to be able to generate the authentication key - Set scopesSet = OAuth2Utils.parseParameterList(newToken.getScope()); - AuthorizationRequest request = new AuthorizationRequest(clientId, scopesSet); - request.setApproved(true); - Authentication authentication = new OrcidOauth2UserAuthentication(profileEntity, true); - OrcidOAuth2Authentication orcidAuthentication = new OrcidOAuth2Authentication(request, authentication, newToken.getTokenValue()); - newToken.setAuthenticationKey(authenticationKeyGenerator.extractKey(orcidAuthentication)); - - // Store the new token and return it - orcidOauth2TokenDetailDao.persist(newToken); - - // Revoke the old token when required - if (revokeOld) { - orcidOauth2TokenDetailDao.disableAccessToken(parentTokenValue); - } - - // Save the changes - orcidOauth2TokenDetailDao.flush(); - - // Transform the OrcidOauth2TokenDetail into a OAuth2AccessToken - // and return it - return toOAuth2AccessToken(newToken); - } - - private OAuth2AccessToken toOAuth2AccessToken(OrcidOauth2TokenDetail token) { - DefaultOAuth2AccessToken result = new DefaultOAuth2AccessToken(token.getTokenValue()); - result.setExpiration(token.getTokenExpiration()); - result.setRefreshToken(new DefaultOAuth2RefreshToken(token.getRefreshTokenValue())); - result.setScope(OAuth2Utils.parseParameterList(token.getScope())); - result.setTokenType(token.getTokenType()); - result.setValue(token.getTokenValue()); - - Map additionalInfo = new HashMap(); - if(token.getOrcid() != null) { - additionalInfo.put(OrcidOauth2Constants.ORCID, token.getOrcid()); - additionalInfo.put(OrcidOauth2Constants.NAME, profileEntityManager.retrivePublicDisplayName(token.getOrcid())); - } - - result.setAdditionalInformation(additionalInfo); - return result; - } - - @Override - public OAuth2AccessToken createRevokedAccessToken(OAuth2Authentication authentication, RevokeReason revokeReason) throws AuthenticationException { - // create the regular token - DefaultOAuth2AccessToken accessToken = generateAccessToken(authentication); - try { - if(accessToken.getAdditionalInformation() == null) { - accessToken.setAdditionalInformation(Collections.emptyMap()); - } - accessToken.getAdditionalInformation().put(OrcidOauth2Constants.TOKEN_DISABLED, true); - orcidTokenStore.storeRevokedAccessToken(accessToken, authentication, revokeReason); - } catch (PersistenceException e) { - // In the unlikely case that there is a constraint violation, lets - // try to generate the token one more time - if (e.getCause() instanceof ConstraintViolationException) { - accessToken = generateAccessToken(authentication); - try { - orcidTokenStore.storeRevokedAccessToken(accessToken, authentication, revokeReason); - return accessToken; - } catch (Exception e2) { - // Just throw the first exception - } - - } - OrcidOauth2AuthInfo authInfo = new OrcidOauth2AuthInfo(authentication); - LOGGER.error("Exception creating revoked access token for client {}, scopes {} and user {}", - new Object[] { authInfo.getClientId(), authInfo.getScopes(), authInfo.getUserOrcid() }); - LOGGER.error("Error info", e); - throw e; - } - return accessToken; - } } diff --git a/orcid-core/src/main/java/org/orcid/core/security/DefaultPermissionChecker.java b/orcid-core/src/main/java/org/orcid/core/security/DefaultPermissionChecker.java deleted file mode 100644 index a338652ad67..00000000000 --- a/orcid-core/src/main/java/org/orcid/core/security/DefaultPermissionChecker.java +++ /dev/null @@ -1,365 +0,0 @@ -package org.orcid.core.security; - -import java.security.AccessControlException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.annotation.Resource; -import javax.xml.datatype.XMLGregorianCalendar; - -import org.apache.commons.lang.StringUtils; -import org.orcid.core.exception.OrcidDeprecatedException; -import org.orcid.core.manager.ProfileEntityCacheManager; -import org.orcid.core.manager.ProfileEntityManager; -import org.orcid.core.oauth.OrcidOAuth2Authentication; -import org.orcid.core.oauth.OrcidOauth2TokenDetailService; -import org.orcid.utils.DateUtils; -import org.orcid.jaxb.model.message.OrcidIdentifier; -import org.orcid.jaxb.model.message.OrcidMessage; -import org.orcid.jaxb.model.message.ScopePathType; -import org.orcid.jaxb.model.message.Visibility; -import org.orcid.persistence.dao.ProfileDao; -import org.orcid.persistence.jpa.entities.OrcidOauth2TokenDetail; -import org.orcid.persistence.jpa.entities.ProfileEntity; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.oauth2.common.util.OAuth2Utils; -import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.security.oauth2.provider.OAuth2Request; -import org.springframework.stereotype.Component; - -/** - * @author Declan Newman (declan) Date: 27/04/2012 - */ -@Component("defaultPermissionChecker") -public class DefaultPermissionChecker implements PermissionChecker { - - private static final Logger LOGGER = LoggerFactory.getLogger(DefaultPermissionChecker.class); - - @Value("${org.orcid.core.token.write_validity_seconds:3600}") - private int writeValiditySeconds; - - @Resource(name = "profileEntityManager") - private ProfileEntityManager profileEntityManager; - - @Resource - private ProfileDao profileDao; - - @Value("${org.orcid.core.baseUri}") - private String baseUrl; - - @Resource - private OrcidOauth2TokenDetailService orcidOauthTokenDetailService; - - @Resource(name = "profileEntityCacheManager") - ProfileEntityCacheManager profileEntityCacheManager; - - /** - * Check the permissions for the given - * {@link org.springframework.security.core.Authentication} object and the - * scopes defined in the required scopes - * - * @param authentication - * The authentication object associated with this session - * @param requiredScope - * the scopes required to perform the requested operation - * @param orcid - * the orcid passed into the request. This is for requests, such - * as a GET /1234-1234-1234-1234/orcid-bio - * @param orcidMessage - * the {@link org.orcid.jaxb.model.message.OrcidMessage} that has - * been sent as part of this request. This will only apply to - * PUTs and POSTs - */ - @Override - public void checkPermissions(Authentication authentication, ScopePathType requiredScope, String orcid, OrcidMessage orcidMessage) { - if (StringUtils.isNotBlank(orcid) && orcidMessage != null && orcidMessage.getOrcidProfile() != null - && (orcidMessage.getOrcidProfile().getOrcidIdentifier() == null || StringUtils.isBlank(orcidMessage.getOrcidProfile().getOrcidIdentifier().getPath()))) { - orcidMessage.getOrcidProfile().setOrcidIdentifier(orcid); - } - performPermissionChecks(authentication, requiredScope, orcid, orcidMessage); - } - - /** - * Check the permissions for the given - * {@link org.springframework.security.core.Authentication} object and the - * scopes defined in the required scopes - * - * @param authentication - * The authentication object associated with this session - * @param requiredScope - * the scopes required to perform the requested operation - * @param orcidMessage - * the {@link org.orcid.jaxb.model.message.OrcidMessage} that has - * been sent as part of this request. This will only apply to - * PUTs and POSTs - */ - @Override - public void checkPermissions(Authentication authentication, ScopePathType requiredScope, OrcidMessage orcidMessage) { - performPermissionChecks(authentication, requiredScope, null, orcidMessage); - } - - /** - * Check the permissions for the given - * {@link org.springframework.security.core.Authentication} object and the - * scopes defined in the required scopes - * - * @param authentication - * The authentication object associated with this session - * @param requiredScope - * the scopes required to perform the requested operation - * @param orcid - * the orcid passed into the request. This is for requests, such - * as a GET /1234-1234-1234-1234/orcid-bio - */ - @Override - public void checkPermissions(Authentication authentication, ScopePathType requiredScope, String orcid) { - LOGGER.debug("Checking permissions on token"); - performPermissionChecks(authentication, requiredScope, orcid, null); - } - - /** - * Obtain the current users' permission and return the - * {@link org.orcid.jaxb.model.message.Visibility} array containing those - * - * @param authentication - * the object containing the user's security information - * @return the {@alink Visibility} array of the current user - */ - @Override - public Set obtainVisibilitiesForAuthentication(Authentication authentication, ScopePathType requiredScope, OrcidMessage orcidMessage) { - Collection authorities = authentication.getAuthorities(); - if (authoritiesHasRole(authorities, "ROLE_SYSTEM")) { - return new HashSet(Arrays.asList(Visibility.SYSTEM)); - } else if (OrcidOAuth2Authentication.class.isAssignableFrom(authentication.getClass())) { - OrcidOAuth2Authentication auth2Authentication = (OrcidOAuth2Authentication) authentication; - Set visibilities = getVisibilitiesForOauth2Authentication(auth2Authentication, orcidMessage, requiredScope); - return visibilities; - } else { - throw new IllegalArgumentException("Cannot obtain authentication details from " + authentication); - } - } - - private Set getVisibilitiesForOauth2Authentication(OAuth2Authentication oAuth2Authentication, OrcidMessage orcidMessage, ScopePathType requiredScope) { - - Set visibilities = new HashSet(); - visibilities.add(Visibility.PUBLIC); - - String orcid = orcidMessage.getOrcidProfile().getOrcidIdentifier().getPath(); - - // Check the scopes and it will throw an an AccessControlException if - // the correct scope is not found. This - // effectively means that the user can only see the public data - try { - checkScopes(oAuth2Authentication, requiredScope); - } catch (AccessControlException e) { - return visibilities; - } - // If the user has granted permission to the client and the orcid that - // has been requested is that of the user - // we can allow for access of protected data - if (!oAuth2Authentication.isClientOnly() && oAuth2Authentication.getPrincipal() != null - && ProfileEntity.class.isAssignableFrom(oAuth2Authentication.getPrincipal().getClass())) { - ProfileEntity principal = (ProfileEntity) oAuth2Authentication.getPrincipal(); - visibilities.add(Visibility.REGISTERED_ONLY); - if (principal != null && principal.getId().equals(orcid)) { - Set requestedScopes = oAuth2Authentication.getOAuth2Request().getScope(); - for (String scope : requestedScopes) { - if (ScopePathType.hasStringScope(scope, requiredScope)) { - visibilities.add(Visibility.LIMITED); - break; - } - } - } - // This is a client credential authenticated client. If the profile - // was created using this client and it - // hasn't been claimed, it's theirs to read - } else if (oAuth2Authentication.isClientOnly()) { - OAuth2Request authorizationRequest = oAuth2Authentication.getOAuth2Request(); - String clientId = authorizationRequest.getClientId(); - String sponsorOrcid = getSponsorOrcid(orcidMessage); - if (StringUtils.isNotBlank(sponsorOrcid) && clientId.equals(sponsorOrcid) && !orcidMessage.getOrcidProfile().getOrcidHistory().isClaimed()) { - visibilities.add(Visibility.LIMITED); - visibilities.add(Visibility.PRIVATE); - } - } - - return visibilities; - } - - private String getSponsorOrcid(OrcidMessage orcidMessage) { - if (orcidMessage != null && orcidMessage.getOrcidProfile() != null && orcidMessage.getOrcidProfile().getOrcidHistory() != null - && orcidMessage.getOrcidProfile().getOrcidHistory().getSource() != null) { - return orcidMessage.getOrcidProfile().getOrcidHistory().getSource().retrieveSourcePath(); - } else { - return null; - } - } - - private void performPermissionChecks(Authentication authentication, ScopePathType requiredScope, String orcid, OrcidMessage orcidMessage) { - // We can trust that this will return a not-null Authentication object - Collection authorities = authentication.getAuthorities(); - if (authoritiesHasRole(authorities, "ROLE_SYSTEM")) { - return; - } else if (OAuth2Authentication.class.isAssignableFrom(authentication.getClass())) { - OAuth2Authentication oAuth2Authentication = (OAuth2Authentication) authentication; - checkScopes(oAuth2Authentication, requiredScope); - performSecurityChecks(oAuth2Authentication, requiredScope, orcidMessage, orcid); - } else { - throw new AccessControlException("Cannot access method with authentication type " + authentication != null ? authentication.toString() : ", as it's null!"); - } - } - - private void checkScopes(OAuth2Authentication oAuth2Authentication, ScopePathType requiredScope) { - OAuth2Request authorizationRequest = oAuth2Authentication.getOAuth2Request(); - Set requestedScopes = authorizationRequest.getScope(); - if (requiredScope.isUserGrantWriteScope()) { - OrcidOAuth2Authentication orcidOauth2Authentication = (OrcidOAuth2Authentication) oAuth2Authentication; - String activeToken = orcidOauth2Authentication.getActiveToken(); - if (activeToken != null) { - OrcidOauth2TokenDetail tokenDetail = orcidOauthTokenDetailService.findNonDisabledByTokenValue(activeToken); - if (removeUserGrantWriteScopePastValitity(tokenDetail)) { - throw new AccessControlException("Write scopes for this token have expired "); - } - } - } - if (!hasRequiredScope(requestedScopes, requiredScope)) { - throw new AccessControlException("Insufficient or wrong scope " + requestedScopes); - } - } - - /* - * Remove UserGrantWriteScope past the specified validity, returns true if - * modified false otherwise - */ - public boolean removeUserGrantWriteScopePastValitity(OrcidOauth2TokenDetail tokenDetail) { - boolean scopeRemoved = false; - if (tokenDetail != null && tokenDetail.getScope() != null) { - // Clean the scope if it is not a persistent token - if (!tokenDetail.isPersistent()) { - Set scopes = OAuth2Utils.parseParameterList(tokenDetail.getScope()); - List removeScopes = new ArrayList(); - for (String scope : scopes) { - if (scope != null && !scope.isEmpty()) { - ScopePathType scopePathType = ScopePathType.fromValue(scope); - if (scopePathType.isUserGrantWriteScope()) { - Date now = new Date(); - if (now.getTime() > tokenDetail.getDateCreated().getTime() + (writeValiditySeconds * 1000)) { - removeScopes.add(scope); - scopeRemoved = true; - } - } - } - } - if (scopeRemoved) { - for (String scope : removeScopes) - scopes.remove(scope); - orcidOauthTokenDetailService.updateScopes(tokenDetail.getTokenValue(), scopes); - return true; - } - } - } - return false; - } - - private void performSecurityChecks(OAuth2Authentication oAuth2Authentication, ScopePathType requiredScope, OrcidMessage orcidMessage, String orcid) { - if (oAuth2Authentication.isClientOnly()) { - performClientChecks(oAuth2Authentication, requiredScope, orcidMessage, orcid); - } else { - performUserChecks(oAuth2Authentication, requiredScope, orcidMessage, orcid); - } - } - - private void performUserChecks(OAuth2Authentication oAuth2Authentication, ScopePathType requiredScope, OrcidMessage orcidMessage, String orcid) { - ProfileEntity principal = (ProfileEntity) oAuth2Authentication.getPrincipal(); - String userOrcid = principal.getId(); - if (orcidMessage != null && orcidMessage.getOrcidProfile() != null && orcidMessage.getOrcidProfile().getOrcidIdentifier() != null - && StringUtils.isNotBlank(orcid)) { - String messageOrcid = orcidMessage.getOrcidProfile().getOrcidIdentifier().getPath(); - // First check that this is a valid call. If these don't match then - // the request is invalid - if (!messageOrcid.equals(orcid)) { - throw new IllegalArgumentException("The ORCID in the body and the URI do not match. Body ORCID: " + messageOrcid + " URI ORCID: " + orcid - + " do NOT match."); - } - } - // Is this the owner making the call? If it is, then let 'em on - // through - if (userOrcid.equals(orcid)) { - return; - } else { - if(profileDao.isProfileDeprecated(orcid)) { - ProfileEntity entity = profileEntityCacheManager.retrieve(orcid); - Map params = new HashMap(); - StringBuffer primary = new StringBuffer(baseUrl).append("/").append(entity.getPrimaryRecord().getId()); - params.put(OrcidDeprecatedException.ORCID, primary.toString()); - if(entity.getDeprecatedDate() != null) { - XMLGregorianCalendar calendar = DateUtils.convertToXMLGregorianCalendar(entity.getDeprecatedDate()); - params.put(OrcidDeprecatedException.DEPRECATED_DATE, calendar.toString()); - } - throw new OrcidDeprecatedException(params); - } - } - throw new AccessControlException("You do not have the required permissions."); - } - - private void performClientChecks(OAuth2Authentication oAuth2Authentication, ScopePathType requiredScope, OrcidMessage orcidMessage, String orcid) { - OAuth2Request authorizationRequest = oAuth2Authentication.getOAuth2Request(); - // If we have an ORCID in the request, we assume that this is intended - // as an update - if (orcidMessage != null && orcidMessage.getOrcidProfile() != null && StringUtils.isNotBlank(orcid)) { - - OrcidIdentifier orcidOb = orcidMessage.getOrcidProfile().getOrcidIdentifier(); - String messageOrcid = orcidOb != null ? orcidOb.getPath() : orcid; - if (StringUtils.isNotBlank(messageOrcid) && !orcid.equals(messageOrcid)) { - throw new IllegalArgumentException("The ORCID in the body and the URI do NOT match. Body ORCID: " + messageOrcid + " URI ORCID: " + orcid - + " do NOT match."); - } - - profileEntityCacheManager.retrieve(messageOrcid); - if (!profileEntityManager.existsAndNotClaimedAndBelongsTo(messageOrcid, authorizationRequest.getClientId())) { - throw new AccessControlException("You cannot update this profile as it has been claimed, or you are not the owner."); - } - } - } - - private boolean authoritiesHasRole(Collection authorities, String role) { - for (GrantedAuthority authority : authorities) { - if (authority.getAuthority().equalsIgnoreCase(role)) { - return true; - } - } - return false; - } - - private boolean hasRequiredScope(Set requestedScopes, ScopePathType requiredScope) { - for (String requestedScope : requestedScopes) { - if (ScopePathType.hasStringScope(requestedScope, requiredScope)) { - return true; - } - if (requiredScope.isReadOnlyScope()) { - // If read only (limited or otherwise) then let it through it - // the user has /read-public, and let the visibility filter take - // care of it. - if (ScopePathType.hasStringScope(requestedScope, ScopePathType.READ_PUBLIC)) { - return true; - } - } - } - return false; - } - - public void setOrcidOauthTokenDetailService(OrcidOauth2TokenDetailService orcidOauthTokenDetailService) { - this.orcidOauthTokenDetailService = orcidOauthTokenDetailService; - } -} diff --git a/orcid-core/src/main/java/org/orcid/core/security/PermissionChecker.java b/orcid-core/src/main/java/org/orcid/core/security/PermissionChecker.java deleted file mode 100644 index ec9a9763d4d..00000000000 --- a/orcid-core/src/main/java/org/orcid/core/security/PermissionChecker.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.orcid.core.security; - -import org.orcid.jaxb.model.message.OrcidMessage; -import org.orcid.jaxb.model.message.ScopePathType; -import org.orcid.jaxb.model.message.Visibility; -import org.springframework.security.core.Authentication; - -import java.util.Set; - -/** - *

- * Checks permissions for the user current registered as the - * {@link java.security.Principal} in this session. - *

- * This was designed to be used in conjunction with AOP giving access to the - * {@link org.orcid.core.security .visibility.aop.AccessControl#requiredScopes()} - * properties. However, to make this more versitile it takes the array of - * {@link ScopePathType} available when using the annotation. - * - * @author Declan Newman (declan) Date: 27/04/2012 - */ -public interface PermissionChecker { - - /** - * Check the permissions for the given {@link Authentication} object and the - * scopes defined in the required scopes - * - * @param authentication - * The authentication object associated with this session - * @param requiredScope - * the scope required to perform the requested operation - * @param orcid - * the orcid passed into the request. This is for requests, such - * as a GET /1234-1234-1234-1234/orcid-bio - * @param orcidMessage - * the {@link OrcidMessage} that has been sent as part of this - * request. This will only apply to PUTs and POSTs - */ - void checkPermissions(Authentication authentication, ScopePathType requiredScope, String orcid, OrcidMessage orcidMessage); - - /** - * Check the permissions for the given {@link Authentication} object and the - * scopes defined in the required scopes - * - * @param authentication - * The authentication object associated with this session - * @param requiredScope - * the scope required to perform the requested operation - * @param orcidMessage - * the {@link OrcidMessage} that has been sent as part of this - * request. This will only apply to PUTs and POSTs - */ - void checkPermissions(Authentication authentication, ScopePathType requiredScope, OrcidMessage orcidMessage); - - /** - * Check the permissions for the given {@link Authentication} object and the - * scopes defined in the required scopes - * - * @param authentication - * The authentication object associated with this session - * @param requiredScope - * the scope required to perform the requested operation - * @param orcid - * the orcid passed into the request. This is for requests, such - * as a GET /1234-1234-1234-1234/orcid-bio - */ - void checkPermissions(Authentication authentication, ScopePathType requiredScope, String orcid); - - /** - * Obtain the current users' permission and return the {@link Visibility} - * array containing those - * - * @param authentication - * the object containing the user's security information - * @return the {@alink Visibility} array of the current user - */ - Set obtainVisibilitiesForAuthentication(Authentication authentication, ScopePathType requiredScope, OrcidMessage orcidMessage); -} diff --git a/orcid-core/src/main/java/org/orcid/core/security/visibility/aop/OrcidApiAuthorizationSecurityAspect.java b/orcid-core/src/main/java/org/orcid/core/security/visibility/aop/OrcidApiAuthorizationSecurityAspect.java deleted file mode 100644 index c7f7f65d43f..00000000000 --- a/orcid-core/src/main/java/org/orcid/core/security/visibility/aop/OrcidApiAuthorizationSecurityAspect.java +++ /dev/null @@ -1,385 +0,0 @@ -package org.orcid.core.security.visibility.aop; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import javax.annotation.Resource; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; - -import org.apache.commons.lang.StringUtils; -import org.aspectj.lang.annotation.AfterReturning; -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Before; -import org.orcid.core.oauth.OrcidOAuth2Authentication; -import org.orcid.core.security.PermissionChecker; -import org.orcid.core.security.visibility.filter.VisibilityFilter; -import org.orcid.jaxb.model.message.ExternalIdentifiers; -import org.orcid.jaxb.model.message.GivenNames; -import org.orcid.jaxb.model.message.Keywords; -import org.orcid.jaxb.model.message.OrcidBio; -import org.orcid.jaxb.model.message.OrcidMessage; -import org.orcid.jaxb.model.message.OrcidProfile; -import org.orcid.jaxb.model.message.OtherNames; -import org.orcid.jaxb.model.message.PersonalDetails; -import org.orcid.jaxb.model.message.ResearcherUrls; -import org.orcid.jaxb.model.message.ScopePathType; -import org.orcid.jaxb.model.message.Visibility; -import org.orcid.jaxb.model.message.VisibilityType; -import org.orcid.jaxb.model.notification_v2.Notification; -import org.orcid.jaxb.model.record_v2.Activity; -import org.orcid.persistence.dao.OrcidOauth2TokenDetailDao; -import org.orcid.pojo.ajaxForm.PojoUtil; -import org.springframework.core.annotation.Order; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.oauth2.provider.OAuth2Request; -import org.springframework.stereotype.Component; - -/** - * @author Declan Newman (declan) Date: 16/03/2012 - */ -@Deprecated -@Aspect -@Component -@Order(100) -public class OrcidApiAuthorizationSecurityAspect { - - public static final String CLIENT_ID = "client_id"; - - @Resource - private OrcidOauth2TokenDetailDao orcidOauth2TokenDetailDao; - - @Resource(name = "visibilityFilter") - private VisibilityFilter visibilityFilter; - - @Resource(name = "defaultPermissionChecker") - private PermissionChecker permissionChecker; - - public void setOrcidOauth2TokenDetailDao(OrcidOauth2TokenDetailDao orcidOauth2TokenDetailDao) { - this.orcidOauth2TokenDetailDao = orcidOauth2TokenDetailDao; - } - - @Before("@annotation(accessControl) && (args(uriInfo ,orcid, orcidMessage))") - public void checkPermissionsWithAll(AccessControl accessControl, UriInfo uriInfo, String orcid, OrcidMessage orcidMessage) { - permissionChecker.checkPermissions(getAuthentication(), accessControl.requiredScope(), orcid, orcidMessage); - } - - @Before("@annotation(accessControl) && (args(uriInfo, orcidMessage))") - public void checkPermissionsWithOrcidMessage(AccessControl accessControl, UriInfo uriInfo, OrcidMessage orcidMessage) { - permissionChecker.checkPermissions(getAuthentication(), accessControl.requiredScope(), orcidMessage); - - } - - @Before("@annotation(accessControl) && args(orcid)") - public void checkPermissionsWithOrcid(AccessControl accessControl, String orcid) { - Authentication auth = getAuthentication(); - boolean allowAnonymousCall = allowAnonymousAccess(auth, accessControl); - if(!allowAnonymousCall) { - permissionChecker.checkPermissions(auth, accessControl.requiredScope(), orcid); - } - - } - - @Before("@annotation(accessControl) && args(orcid, id)") - public void checkPermissionsWithLongId(AccessControl accessControl, String orcid, Long id) { - Authentication auth = getAuthentication(); - boolean allowAnonymousCall = allowAnonymousAccess(auth, accessControl); - if(!allowAnonymousCall) { - permissionChecker.checkPermissions(auth, accessControl.requiredScope(), orcid); - } - } - - @Before("@annotation(accessControl) && args(orcid, id)") - public void checkPermissionsWithId(AccessControl accessControl, String orcid, String id) { - Authentication auth = getAuthentication(); - boolean allowAnonymousCall = allowAnonymousAccess(auth, accessControl); - if(!allowAnonymousCall) { - permissionChecker.checkPermissions(getAuthentication(), accessControl.requiredScope(), orcid); - } - } - - @Before("@annotation(accessControl) && args(uriInfo, orcid, notification)") - public void checkPermissionsWithNotification(AccessControl accessControl, UriInfo uriInfo, String orcid, Notification notification) { - permissionChecker.checkPermissions(getAuthentication(), accessControl.requiredScope(), orcid); - } - - @Before("@annotation(accessControl) && args(orcid, activity)") - public void checkPermissionsWithWork(AccessControl accessControl, String orcid, Activity activity) { - permissionChecker.checkPermissions(getAuthentication(), accessControl.requiredScope(), orcid); - } - - @Before("@annotation(accessControl) && args(orcid, putCode, activity)") - public void checkPermissionsWithWork(AccessControl accessControl, String orcid, String putCode, Activity activity) { - permissionChecker.checkPermissions(getAuthentication(), accessControl.requiredScope(), orcid); - } - - @Before("@annotation(accessControl) && args(uriInfo, orcid, webhookUri)") - public void checkPermissionsWithOrcidAndWebhookUri(AccessControl accessControl, UriInfo uriInfo, String orcid, String webhookUri) { - permissionChecker.checkPermissions(getAuthentication(), accessControl.requiredScope(), orcid); - } - - @AfterReturning(pointcut = "@annotation(accessControl)", returning = "response") - public void visibilityResponseFilter(Response response, AccessControl accessControl) { - if(accessControl.requestComesFromInternalApi()) { - return; - } - Object entity = response.getEntity(); - if (entity != null && OrcidMessage.class.isAssignableFrom(entity.getClass())) { - OrcidMessage orcidMessage = (OrcidMessage) entity; - - //If it is search results, don't filter them, just return them - if(orcidMessage.getOrcidSearchResults() != null) { - return; - } - - // get the client id - Object authentication = getAuthentication(); - - Set visibilities = new HashSet(); - - if(allowAnonymousAccess((Authentication)authentication, accessControl)) { - visibilities.add(Visibility.PUBLIC); - } else { - visibilities = permissionChecker.obtainVisibilitiesForAuthentication(getAuthentication(), accessControl.requiredScope(), orcidMessage); - } - //If the message contains a bio, and the given name is filtered, restore it as an empty space - boolean setEmptyGivenNameIfFiltered = false; - if(orcidMessage.getOrcidProfile() != null) { - if(orcidMessage.getOrcidProfile() != null && orcidMessage.getOrcidProfile().getOrcidBio() != null) { - setEmptyGivenNameIfFiltered = true; - } - } - - ScopePathType requiredScope = accessControl.requiredScope(); - // If the required scope is */read-limited or */update - if (isUpdateOrReadScope(requiredScope)) { - // If the authentication contains a client_id, use it to check - // if it should be able to - if (OrcidOAuth2Authentication.class.isAssignableFrom(authentication.getClass())){ - OrcidOAuth2Authentication orcidAuth = (OrcidOAuth2Authentication) getAuthentication(); - - OAuth2Request authorization = orcidAuth.getOAuth2Request(); - String clientId = authorization.getClientId(); - - // #1: Get the user orcid - String userOrcid = getUserOrcidFromOrcidMessage(orcidMessage); - // #2: Evaluate the scope to know which field to filter - boolean allowWorks = false; - boolean allowFunding = false; - boolean allowAffiliations = false; - - // Get the update equivalent scope, if it is reading, but, - // doesnt have the read permissions, check if it have the - // update permissions - ScopePathType equivalentUpdateScope = getEquivalentUpdateScope(requiredScope); - if (requiredScope.equals(ScopePathType.READ_LIMITED)) { - if (hasScopeEnabled(clientId, userOrcid, ScopePathType.ORCID_WORKS_READ_LIMITED.getContent(), ScopePathType.ORCID_WORKS_UPDATE.getContent())) - allowWorks = true; - if (hasScopeEnabled(clientId, userOrcid, ScopePathType.FUNDING_READ_LIMITED.getContent(), ScopePathType.FUNDING_UPDATE.getContent())) - allowFunding = true; - if (hasScopeEnabled(clientId, userOrcid, ScopePathType.AFFILIATIONS_READ_LIMITED.getContent(), ScopePathType.AFFILIATIONS_UPDATE.getContent())) - allowAffiliations = true; - } else if (requiredScope.equals(ScopePathType.ORCID_WORKS_UPDATE) || requiredScope.equals(ScopePathType.ORCID_WORKS_READ_LIMITED)) { - // Check if the member have the update or read scope on - // works - if (hasScopeEnabled(clientId, userOrcid, requiredScope.getContent(), equivalentUpdateScope == null ? null : equivalentUpdateScope.getContent())) - // If so, allow him to see private works - allowWorks = true; - } else if (requiredScope.equals(ScopePathType.FUNDING_UPDATE) || requiredScope.equals(ScopePathType.FUNDING_READ_LIMITED)) { - // Check if the member have the update or read scope on - // funding - if (hasScopeEnabled(clientId, userOrcid, requiredScope.getContent(), equivalentUpdateScope == null ? null : equivalentUpdateScope.getContent())) - // If so, allow him to see private funding - allowFunding = true; - } else if (requiredScope.equals(ScopePathType.AFFILIATIONS_UPDATE) || requiredScope.equals(ScopePathType.AFFILIATIONS_READ_LIMITED)) { - // Check if the member have the update or read scope on - // affiliations - if (hasScopeEnabled(clientId, userOrcid, requiredScope.getContent(), equivalentUpdateScope == null ? null : equivalentUpdateScope.getContent())) - // If so, allow him to see private affiliations - allowAffiliations = true; - } - - visibilityFilter.filter(orcidMessage, clientId, allowWorks, allowFunding, allowAffiliations, - visibilities.toArray(new Visibility[visibilities.size()])); - } else { - visibilityFilter.filter(orcidMessage, null, false, false, false, visibilities.toArray(new Visibility[visibilities.size()])); - } - - } else { - visibilityFilter.filter(orcidMessage, null, false, false, false, visibilities.toArray(new Visibility[visibilities.size()])); - } - - //This applies for given names that were filtered because of the new visibility field applied on them - //If the given name was set at the beginning and now is filtered, it means we should restore it as an empty field - if(setEmptyGivenNameIfFiltered) { - if(orcidMessage.getOrcidProfile() != null) { - if(orcidMessage.getOrcidProfile().getOrcidBio() == null) { - orcidMessage.getOrcidProfile().setOrcidBio(new OrcidBio()); - } - - if(orcidMessage.getOrcidProfile().getOrcidBio().getPersonalDetails() == null) { - orcidMessage.getOrcidProfile().getOrcidBio().setPersonalDetails(new PersonalDetails()); - } - } - } - - //Filter given or family names visibility - if(orcidMessage.getOrcidProfile() != null) { - if(orcidMessage.getOrcidProfile().getOrcidBio() != null) { - if(orcidMessage.getOrcidProfile().getOrcidBio().getPersonalDetails() != null) { - if(orcidMessage.getOrcidProfile().getOrcidBio().getPersonalDetails().getGivenNames() != null) { - orcidMessage.getOrcidProfile().getOrcidBio().getPersonalDetails().getGivenNames().setVisibility(null); - } else { - //Null given names could break client integrations, so, lets return an empty string - GivenNames empty = new GivenNames(); - empty.setContent(StringUtils.EMPTY); - orcidMessage.getOrcidProfile().getOrcidBio().getPersonalDetails().setGivenNames(empty); - } - - if(orcidMessage.getOrcidProfile().getOrcidBio().getPersonalDetails().getFamilyName() != null) { - orcidMessage.getOrcidProfile().getOrcidBio().getPersonalDetails().getFamilyName().setVisibility(null); - } - } - } - } - - //replace section visibilities now we may have filtered items - if(orcidMessage.getOrcidProfile() != null) { - if(orcidMessage.getOrcidProfile().getOrcidBio() != null) { - if(orcidMessage.getOrcidProfile().getOrcidBio().getPersonalDetails() != null) { - OtherNames n = orcidMessage.getOrcidProfile().getOrcidBio().getPersonalDetails().getOtherNames(); - if(n != null) { - n.setVisibility(getMostFromCollection(n.getOtherName())); - } - } - ExternalIdentifiers ids = orcidMessage.getOrcidProfile().getOrcidBio().getExternalIdentifiers(); - if (ids != null){ - ids.setVisibility(getMostFromCollection(ids.getExternalIdentifier())); - } - Keywords kws = orcidMessage.getOrcidProfile().getOrcidBio().getKeywords(); - if (kws != null){ - kws.setVisibility(getMostFromCollection(kws.getKeyword())); - } - ResearcherUrls urls = orcidMessage.getOrcidProfile().getOrcidBio().getResearcherUrls(); - if (urls != null){ - urls.setVisibility(getMostFromCollection(urls.getResearcherUrl())); - } - } - } - } - } - - private Visibility getMostFromCollection(List c){ - Visibility most = Visibility.PUBLIC; - for (VisibilityType x : c){ - if (x.getVisibility().isMoreRestrictiveThan(most)) - most = x.getVisibility(); - } - return most; - } - private String getUserOrcidFromOrcidMessage(OrcidMessage message) { - OrcidProfile profile = message.getOrcidProfile(); - return profile.getOrcidIdentifier().getPath(); - } - - private boolean isUpdateOrReadScope(ScopePathType requiredScope) { - switch (requiredScope) { - case AFFILIATIONS_READ_LIMITED: - case AFFILIATIONS_UPDATE: - case FUNDING_READ_LIMITED: - case FUNDING_UPDATE: - case ORCID_BIO_READ_LIMITED: - case ORCID_BIO_UPDATE: - case ORCID_PATENTS_READ_LIMITED: - case ORCID_PATENTS_UPDATE: - case ORCID_PROFILE_READ_LIMITED: - case READ_LIMITED: - case ORCID_WORKS_READ_LIMITED: - case ORCID_WORKS_UPDATE: - return true; - default: - return false; - } - } - - @Deprecated - public boolean hasScopeEnabled(String clientId, String userName, String scope, String equivalentScope) { - List scopes = new ArrayList(); - scopes.add(scope); - if (equivalentScope != null) - scopes.add(equivalentScope); - return checkIfScopeIsAvailableForMember(clientId, userName, scopes); - } - - private Authentication getAuthentication() { - SecurityContext context = SecurityContextHolder.getContext(); - if (context != null && context.getAuthentication() != null) { - return context.getAuthentication(); - } else { - throw new IllegalStateException("No security context found. This is bad!"); - } - } - - private ScopePathType getEquivalentUpdateScope(ScopePathType readScope) { - if (readScope != null) - switch (readScope) { - case AFFILIATIONS_READ_LIMITED: - return ScopePathType.AFFILIATIONS_UPDATE; - case FUNDING_READ_LIMITED: - return ScopePathType.FUNDING_UPDATE; - case ORCID_WORKS_READ_LIMITED: - return ScopePathType.ORCID_WORKS_UPDATE; - default: - return null; - } - return null; - } - - private boolean allowAnonymousAccess(Authentication auth, AccessControl accessControl) { - boolean allowAnonymousAccess = false; - if(auth != null) { - for(GrantedAuthority grantedAuth : auth.getAuthorities()) { - if("ROLE_ANONYMOUS".equals(grantedAuth.getAuthority())) { - if(!accessControl.enableAnonymousAccess()) { - break; - } - allowAnonymousAccess = true; - break; - } - } - } - return allowAnonymousAccess; - } - - /** - * Check if a member have a specific scope over a client - * - * @param clientId - * @param userName - * @param scopes - * @return true if the member have access to any of the specified scope on the specified user - * */ - @Deprecated - private boolean checkIfScopeIsAvailableForMember(String clientId, String userName, List requiredScopes) { - List availableScopes = orcidOauth2TokenDetailDao.findAvailableScopesByUserAndClientId(clientId, userName); - for(String availableScope : availableScopes) { - String [] simpleScopes = availableScope.split(" "); - for(String simpleScope : simpleScopes) { - if(!PojoUtil.isEmpty(simpleScope)) { - ScopePathType scopePathType = ScopePathType.fromValue(simpleScope); - for(String requiredScope: requiredScopes) { - if(scopePathType.hasScope(requiredScope)) - return true; - } - } - } - } - return false; - } - -} diff --git a/orcid-core/src/main/java/org/orcid/core/security/visibility/filter/VisibilityFilter.java b/orcid-core/src/main/java/org/orcid/core/security/visibility/filter/VisibilityFilter.java deleted file mode 100644 index b480ea778a1..00000000000 --- a/orcid-core/src/main/java/org/orcid/core/security/visibility/filter/VisibilityFilter.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.orcid.core.security.visibility.filter; - -import org.orcid.jaxb.model.message.OrcidMessage; -import org.orcid.jaxb.model.message.Visibility; - -/** - * @author Declan Newman (declan) Date: 16/03/2012 - */ -public interface VisibilityFilter { - - OrcidMessage filter(OrcidMessage messageToBeFiltered, Visibility... visibilities); - - OrcidMessage filter(OrcidMessage messageToBeFiltered, String sourceId, boolean allowPrivateWorks, boolean allowPrivateFunding, boolean allowPrivateAffiliations, Visibility... visibilities); - -} diff --git a/orcid-core/src/main/java/org/orcid/core/security/visibility/filter/impl/VisibilityFilterImpl.java b/orcid-core/src/main/java/org/orcid/core/security/visibility/filter/impl/VisibilityFilterImpl.java deleted file mode 100644 index b242c52ccfb..00000000000 --- a/orcid-core/src/main/java/org/orcid/core/security/visibility/filter/impl/VisibilityFilterImpl.java +++ /dev/null @@ -1,272 +0,0 @@ -package org.orcid.core.security.visibility.filter.impl; - -import java.security.AccessControlException; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -import javax.annotation.Resource; - -import org.orcid.core.security.PermissionChecker; -import org.orcid.core.security.visibility.filter.VisibilityFilter; -import org.orcid.core.tree.TreeCleaner; -import org.orcid.core.tree.TreeCleaningDecision; -import org.orcid.core.tree.TreeCleaningStrategy; -import org.orcid.jaxb.model.message.Address; -import org.orcid.jaxb.model.message.Affiliation; -import org.orcid.jaxb.model.message.Country; -import org.orcid.jaxb.model.message.Email; -import org.orcid.jaxb.model.message.Funding; -import org.orcid.jaxb.model.message.Orcid; -import org.orcid.jaxb.model.message.OrcidIdentifier; -import org.orcid.jaxb.model.message.OrcidMessage; -import org.orcid.jaxb.model.message.OrcidProfile; -import org.orcid.jaxb.model.message.OrcidSearchResults; -import org.orcid.jaxb.model.message.OrcidWork; -import org.orcid.jaxb.model.message.PrivateVisibleToSource; -import org.orcid.jaxb.model.message.ScopePathType; -import org.orcid.jaxb.model.message.Source; -import org.orcid.jaxb.model.message.Visibility; -import org.orcid.jaxb.model.message.VisibilityType; -import org.orcid.jaxb.model.message.WorkContributors; -import org.orcid.pojo.ajaxForm.PojoUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.stereotype.Component; - -/** - * I would imagine the first time you see this class, it may be a bit confusing. - *

- * The goal of this class is to insure that no elements that are marked with a - * {@link org.orcid.jaxb.model.message .Visibility} are displayed to the caller - * unless they have sufficient permissions. - *

- * In addition, we need to make sure that the actual visibility attributes are - * removed (if requested with the removeAttribute argument) are nulled in order - * for callers to be unaware of elements that have been requested to be made - * unavailable. - *

- * This is a very important step in the security model and thus, reflection was - * chosen to scan the JaxB objects and locate any classes implementing the - * {@link org.orcid.jaxb.model.message.VisibilityType} interface and check on - * the visibility level requested. - *

- * - * @author Declan Newman (declan) Date: 16/03/2012 - */ -@Component("visibilityFilter") -public class VisibilityFilterImpl implements VisibilityFilter { - - private final static Logger LOGGER = LoggerFactory.getLogger(VisibilityFilterImpl.class); - - @Resource - private PermissionChecker permissionChecker; - - /** - * Remove the elements that are not present in the list of set of - * {@link org.orcid.jaxb.model.message .Visibility}s present in the array - * passed in By default the remaining visibility elements will not be - * removed from the object. - * - * @param messageToBeFiltered - * the {@link org.orcid.jaxb.model.message.OrcidMessage} that - * will be traversed looking for - * {@link org .orcid.jaxb.model.message.VisibilityType} - * @param visibilities - * What {@link org.orcid.jaxb.model.message.Visibility} elements - * should be allowed. - * @return the cleansed {@link org.orcid.jaxb.model.message.OrcidMessage} - */ - @Override - public OrcidMessage filter(OrcidMessage messageToBeFiltered, Visibility... visibilities) { - return filter(messageToBeFiltered, null, false, false, false, visibilities); - } - - /** - * Remove the elements that are not present in the list of set of - * {@link org.orcid.jaxb.model.message .Visibility}s present in the array - * passed in. - * - * @param messageToBeFiltered - * the {@link org.orcid.jaxb.model.message.OrcidMessage} that - * will be traversed looking for - * {@link org .orcid.jaxb.model.message.VisibilityType} elements. - * @param source - * The orcid source that is executing the request - * @param removeAttribute - * should all {@link org.orcid.jaxb.model.message.Visibility} - * elements be removed from the object graph. This has the effect - * that they will not be present in the resulting JAXB - * serialisation. - * @param visibilities - * What {@link org.orcid.jaxb.model.message.Visibility} elements - * should be allowed. - * @return the cleansed {@link org.orcid.jaxb.model.message.OrcidMessage} - */ - @Override - public OrcidMessage filter(OrcidMessage messageToBeFiltered, final String sourceId, final boolean allowPrivateWorks, final boolean allowPrivateFunding, - final boolean allowPrivateAffiliations, Visibility... visibilities) { - if (messageToBeFiltered == null || visibilities == null || visibilities.length == 0) { - return null; - } - String messageIdForLog = getMessageIdForLog(messageToBeFiltered); - LOGGER.debug("About to filter message: " + messageIdForLog); - final Set visibilitySet = new HashSet(Arrays.asList(visibilities)); - if (visibilitySet.contains(Visibility.SYSTEM)) { - return messageToBeFiltered; - } else { - TreeCleaner treeCleaner = new TreeCleaner(); - treeCleaner.clean(messageToBeFiltered, new TreeCleaningStrategy() { - public TreeCleaningDecision needsStripping(Object obj) { - TreeCleaningDecision decision = TreeCleaningDecision.DEFAULT; - if (obj != null) { - Class clazz = obj.getClass(); - - if (!PojoUtil.isEmpty(sourceId)) { - if (allowPrivateAffiliations && Affiliation.class.isAssignableFrom(clazz)) { - Affiliation affiliation = (Affiliation) obj; - Source source = affiliation.getSource(); - if (source != null) { - String sourcePath = source.retrieveSourcePath(); - if (sourcePath != null) { - if (sourceId.equals(sourcePath)) { - decision = TreeCleaningDecision.IGNORE; - } - } - } - } else if (allowPrivateFunding && Funding.class.isAssignableFrom(clazz)) { - Funding funding = (Funding) obj; - Source source = funding.getSource(); - if (source != null) { - String sourcePath = source.retrieveSourcePath(); - if (sourcePath != null) { - if (sourceId.equals(sourcePath)) { - decision = TreeCleaningDecision.IGNORE; - } - } - } - } else if (allowPrivateWorks && OrcidWork.class.isAssignableFrom(clazz)) { - OrcidWork work = (OrcidWork) obj; - Source source = work.getSource(); - if (source != null) { - if (sourceId.equals(source.retrieveSourcePath())) { - decision = TreeCleaningDecision.IGNORE; - } - } - } - } - - // If it is the address field, the visibility and source - // fields are inside the country element - if (Address.class.isAssignableFrom(clazz)) { - Address address = (Address) obj; - // Remove empty addresses - if (address.getCountry() == null) { - decision = TreeCleaningDecision.CLEANING_REQUIRED; - } else { - Country country = address.getCountry(); - // Allow public addresses - if (Visibility.PUBLIC.equals(country.getVisibility())) { - decision = TreeCleaningDecision.IGNORE; - } else if (visibilitySet.contains(Visibility.LIMITED)) { - // Allow limited visibility when possible - if (Visibility.LIMITED.equals(country.getVisibility())) { - decision = TreeCleaningDecision.IGNORE; - } else { - // As last resource, check the source - Source source = country.getSource(); - if (source != null && sourceId != null && sourceId.equals(source.retrieveSourcePath())) { - decision = TreeCleaningDecision.IGNORE; - } else { - decision = TreeCleaningDecision.CLEANING_REQUIRED; - } - } - } - } - } - - if (Email.class.isAssignableFrom(clazz)) { - // check for /email/read-private permissions, - // include all emails if present - try { - Authentication authentication = getAuthentication(); - if (authentication != null && messageToBeFiltered.getOrcidProfile() != null) { - permissionChecker.checkPermissions(getAuthentication(), ScopePathType.EMAIL_READ_PRIVATE, messageToBeFiltered.getOrcidProfile() - .retrieveOrcidPath()); - decision = TreeCleaningDecision.IGNORE; - } - } catch (AccessControlException e) { - // private email can't be read, do nothing here - } - } - - // if we have a source, and that source can read - // limited, also return the private things they own - // Applies to ExternalIdentifier, Keyword, - // ResearcherUrl, OtherName and anything in the future - // that implements PrivateVisibleToSource - if (sourceId != null) - if (PrivateVisibleToSource.class.isAssignableFrom(clazz) && visibilitySet.contains(Visibility.LIMITED)) { - Source source = ((PrivateVisibleToSource) obj).getSource(); - if (source != null) { - if (sourceId.equals(source.retrieveSourcePath())) { - decision = TreeCleaningDecision.IGNORE; - } - } - } - - if (TreeCleaningDecision.DEFAULT.equals(decision)) { - if (WorkContributors.class.isAssignableFrom(clazz)) { - decision = TreeCleaningDecision.IGNORE; - } else if (VisibilityType.class.isAssignableFrom(clazz)) { - VisibilityType visibilityType = (VisibilityType) obj; - if ((visibilityType.getVisibility() == null || !visibilitySet.contains(visibilityType.getVisibility()))) { - decision = TreeCleaningDecision.CLEANING_REQUIRED; - } - } - } - - } - return decision; - } - }); - OrcidProfile orcidProfile = messageToBeFiltered.getOrcidProfile(); - if (orcidProfile != null) { - orcidProfile.setOrcidInternal(null); - } - LOGGER.debug("Finished filtering message: " + messageIdForLog); - return messageToBeFiltered; - } - } - - private Authentication getAuthentication() { - SecurityContext context = SecurityContextHolder.getContext(); - if (context != null && context.getAuthentication() != null) { - return context.getAuthentication(); - } - return null; - } - - private String getMessageIdForLog(OrcidMessage messageToBeFiltered) { - String messageIdForLog = "unknown"; - OrcidSearchResults orcidSearchResults = messageToBeFiltered.getOrcidSearchResults(); - OrcidProfile orcidProfile = messageToBeFiltered.getOrcidProfile(); - if (orcidSearchResults != null) { - messageIdForLog = "orcid-search-results"; - } else if (orcidProfile != null) { - OrcidIdentifier orcidIdentifier = orcidProfile.getOrcidIdentifier(); - if (orcidIdentifier != null) { - messageIdForLog = orcidIdentifier.getPath(); - } - Orcid orcid = orcidProfile.getOrcid(); - if (orcid != null) { - messageIdForLog = orcid.getValue(); - } - } - return messageIdForLog; - } - -} diff --git a/orcid-core/src/main/java/org/orcid/core/utils/SecurityContextTestUtils.java b/orcid-core/src/main/java/org/orcid/core/utils/SecurityContextTestUtils.java deleted file mode 100644 index 8a6510a3387..00000000000 --- a/orcid-core/src/main/java/org/orcid/core/utils/SecurityContextTestUtils.java +++ /dev/null @@ -1,128 +0,0 @@ -package org.orcid.core.utils; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.io.Serializable; -import java.util.*; - -import org.orcid.core.oauth.OrcidOAuth2Authentication; -import org.orcid.jaxb.model.message.ScopePathType; -import org.orcid.persistence.jpa.entities.ProfileEntity; -import org.springframework.security.authentication.AnonymousAuthenticationToken; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.core.context.SecurityContextImpl; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.core.userdetails.UserDetails; - -public class SecurityContextTestUtils { - - public final static String DEFAULT_CLIENT_ID = "APP-5555555555555555"; - - static public void setUpSecurityContext() { - setUpSecurityContext(ScopePathType.ORCID_WORKS_CREATE); - } - - static public void setUpSecurityContext(ScopePathType... scopePathTypes) { - setUpSecurityContext("4444-4444-4444-4441", scopePathTypes); - } - - static public void setUpSecurityContext(String userOrcid, ScopePathType... scopePathTypes) { - setUpSecurityContext(userOrcid, DEFAULT_CLIENT_ID, scopePathTypes); - } - - static public void setUpSecurityContext(String userOrcid, String clientId, ScopePathType... scopePathTypes) { - SecurityContextImpl securityContext = new SecurityContextImpl(); - OrcidOAuth2Authentication mockedAuthentication = mock(OrcidOAuth2Authentication.class); - securityContext.setAuthentication(mockedAuthentication); - SecurityContextHolder.setContext(securityContext); - ProfileEntity userProfileEntity = new ProfileEntity(userOrcid); - when(mockedAuthentication.getPrincipal()).thenReturn(userProfileEntity); - Authentication userAuthentication = mock(Authentication.class); - when(userAuthentication.getPrincipal()).thenReturn(userProfileEntity); - when(mockedAuthentication.getUserAuthentication()).thenReturn(userAuthentication); - Set scopes = new HashSet(); - if (scopePathTypes != null) { - for (ScopePathType scopePathType : scopePathTypes) { - scopes.add(scopePathType.value()); - } - } - OAuth2Request authorizationRequest = new OAuth2Request(Collections. emptyMap(), clientId, - Collections. emptyList(), true, scopes, Collections. emptySet(), null, Collections. emptySet(), - Collections. emptyMap()); - when(mockedAuthentication.getOAuth2Request()).thenReturn(authorizationRequest); - when(mockedAuthentication.isAuthenticated()).thenReturn(true); - //for obo - OAuth2AuthenticationDetails authDetails = mock(OAuth2AuthenticationDetails.class); - //to use, set token in detail to be a token with OBO. -> when(authDetails.getTokenValue()).thenReturn("xxx"); - when(mockedAuthentication.getDetails()).thenReturn(authDetails); - } - - static public void setUpSecurityContextForClientOnly() { - setUpSecurityContextForClientOnly("APP-5555555555555555"); - } - - static public void setUpSecurityContextForGroupIdClientOnly() { - Set scopes = new HashSet(); - scopes.add(ScopePathType.GROUP_ID_RECORD_READ.value()); - scopes.add(ScopePathType.GROUP_ID_RECORD_UPDATE.value()); - setUpSecurityContextForClientOnly("APP-5555555555555555", scopes); - } - - static public void setUpSecurityContextForClientOnly(String clientId) { - Set scopes = new HashSet(); - scopes.add(ScopePathType.ORCID_PROFILE_CREATE.value()); - setUpSecurityContextForClientOnly(clientId, scopes); - } - - static public void setUpSecurityContextForClientOnly(String clientId, ScopePathType... scopePathTypes) { - Set scopes = new HashSet(); - for (ScopePathType scope : scopePathTypes) { - scopes.add(scope.value()); - } - setUpSecurityContextForClientOnly(clientId, scopes); - } - - static public void setUpSecurityContextForClientOnly(String clientId, Set scopes) { - SecurityContextImpl securityContext = new SecurityContextImpl(); - OrcidOAuth2Authentication mockedAuthentication = mock(OrcidOAuth2Authentication.class); - securityContext.setAuthentication(mockedAuthentication); - SecurityContextHolder.setContext(securityContext); - when(mockedAuthentication.getPrincipal()).thenReturn(new ProfileEntity(clientId)); - when(mockedAuthentication.isClientOnly()).thenReturn(true); - OAuth2Request authorizationRequest = new OAuth2Request(Collections. emptyMap(), clientId, Collections. emptyList(), true, - scopes, Collections. emptySet(), null, Collections. emptySet(), Collections. emptyMap()); - when(mockedAuthentication.getOAuth2Request()).thenReturn(authorizationRequest); - when(mockedAuthentication.isAuthenticated()).thenReturn(true); - when(mockedAuthentication.getName()).thenReturn(clientId); - OAuth2AuthenticationDetails authDetails = mock(OAuth2AuthenticationDetails.class); - when(mockedAuthentication.getDetails()).thenReturn(authDetails); - } - - static public void setUpSecurityContextForAnonymous() { - SecurityContextImpl securityContext = new SecurityContextImpl(); - ArrayList authorities = new ArrayList<>(); - authorities.add(new SimpleGrantedAuthority("ROLE_ANONYMOUS")); - AnonymousAuthenticationToken anonToken = new AnonymousAuthenticationToken("testKey", "testToken", authorities); - securityContext.setAuthentication(anonToken); - SecurityContextHolder.setContext(securityContext); - } - - static public void clearSecurityContext() { - SecurityContextHolder.setContext(new SecurityContextImpl()); - } - - static public void setupSecurityContextForWebUser(String userId, String email) { - UserDetails details = new User(userId, email, List.of()); - UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(userId, "password"); - auth.setDetails(details); - SecurityContextImpl securityContext = new SecurityContextImpl(); - securityContext.setAuthentication(auth); - SecurityContextHolder.setContext(securityContext); - } - -} diff --git a/orcid-core/src/test/java/org/orcid/core/oauth/service/OrcidRandomValueTokenServicesTest.java b/orcid-core/src/test/java/org/orcid/core/oauth/service/OrcidRandomValueTokenServicesTest.java index e88c5709da3..faf48138bf0 100644 --- a/orcid-core/src/test/java/org/orcid/core/oauth/service/OrcidRandomValueTokenServicesTest.java +++ b/orcid-core/src/test/java/org/orcid/core/oauth/service/OrcidRandomValueTokenServicesTest.java @@ -25,7 +25,7 @@ import org.orcid.core.constants.OrcidOauth2Constants; import org.orcid.core.constants.RevokeReason; import org.orcid.core.manager.ClientDetailsManager; -import org.orcid.core.oauth.OrcidOauth2ClientAuthentication; +import org.orcid.core.oauth.OrcidBearerTokenAuthentication; import org.orcid.core.oauth.OrcidOauth2TokenDetailService; import org.orcid.core.oauth.OrcidRandomValueTokenServices; import org.orcid.core.togglz.Features; @@ -90,7 +90,7 @@ public void testCreateReadLimitedAccessToken() { OAuth2Request request = new OAuth2Request(Collections. emptyMap(), clientId, Collections. emptyList(), true, new HashSet(Arrays.asList("/orcid-profile/read-limited")), Collections. emptySet(), null, Collections. emptySet(), Collections. emptyMap()); ClientDetailsEntity clientDetails = clientDetailsManager.findByClientId(clientId); - Authentication userAuthentication = new OrcidOauth2ClientAuthentication(clientDetails); + Authentication userAuthentication = new OrcidBearerTokenAuthentication(clientDetails); OAuth2Authentication authentication = new OAuth2Authentication(request, userAuthentication); OAuth2AccessToken oauth2AccessToken = tokenServices.createAccessToken(authentication); @@ -111,7 +111,7 @@ public void testCreateAddWorkAccessToken() { authorizationParameters.put(OAuth2Utils.SCOPE, "/orcid-works/create"); OAuth2Request request = new OAuth2Request(Collections. emptyMap(), clientId, Collections. emptyList(), true, new HashSet(Arrays.asList("/orcid-profile/read-limited")), Collections. emptySet(), null, Collections. emptySet(), Collections. emptyMap()); ClientDetailsEntity clientDetails = clientDetailsManager.findByClientId(clientId); - Authentication userAuthentication = new OrcidOauth2ClientAuthentication(clientDetails); + Authentication userAuthentication = new OrcidBearerTokenAuthentication(clientDetails); OAuth2Authentication authentication = new OAuth2Authentication(request, userAuthentication); OAuth2AccessToken oauth2AccessToken = tokenServices.createAccessToken(authentication); @@ -132,7 +132,7 @@ public void testReissuedAccessTokenHasUpdatedExpiration() throws InterruptedExce authorizationParameters.put(OAuth2Utils.SCOPE, "/orcid-works/create"); OAuth2Request request = new OAuth2Request(Collections. emptyMap(), clientId, Collections. emptyList(), true, new HashSet(Arrays.asList("/orcid-profile/read-limited")), Collections. emptySet(), null, Collections. emptySet(), Collections. emptyMap()); ClientDetailsEntity clientDetails = clientDetailsManager.findByClientId(clientId); - Authentication userAuthentication = new OrcidOauth2ClientAuthentication(clientDetails); + Authentication userAuthentication = new OrcidBearerTokenAuthentication(clientDetails); OAuth2Authentication authentication = new OAuth2Authentication(request, userAuthentication); OAuth2AccessToken oauth2AccessToken = tokenServices.createAccessToken(authentication); @@ -203,7 +203,7 @@ public void tokenExpireInAnHourTest() throws InterruptedException { OAuth2Request request = new OAuth2Request(Collections. emptyMap(), clientId, Collections. emptyList(), true, new HashSet(Arrays.asList("/orcid-profile/read-limited")), Collections. emptySet(), null, Collections. emptySet(), Collections. emptyMap()); ClientDetailsEntity clientDetails = clientDetailsManager.findByClientId(clientId); - Authentication userAuthentication = new OrcidOauth2ClientAuthentication(clientDetails); + Authentication userAuthentication = new OrcidBearerTokenAuthentication(clientDetails); OAuth2Authentication authentication = new OAuth2Authentication(request, userAuthentication); OAuth2AccessToken oauth2AccessToken = tokenServices.createAccessToken(authentication); @@ -232,7 +232,7 @@ public void tokenExpireIn20YearsTest() throws InterruptedException { OAuth2Request request = new OAuth2Request(requestParameters, clientId, Collections. emptyList(), true, new HashSet(Arrays.asList("/orcid-profile/read-limited")), Collections. emptySet(), null, Collections. emptySet(), Collections. emptyMap()); ClientDetailsEntity clientDetails = clientDetailsManager.findByClientId(clientId); - Authentication userAuthentication = new OrcidOauth2ClientAuthentication(clientDetails); + Authentication userAuthentication = new OrcidBearerTokenAuthentication(clientDetails); OAuth2Authentication authentication = new OAuth2Authentication(request, userAuthentication); OAuth2AccessToken oauth2AccessToken = tokenServices.createAccessToken(authentication); diff --git a/orcid-core/src/test/java/org/orcid/core/oauth/service/OrcidTokenStoreServiceTest.java b/orcid-core/src/test/java/org/orcid/core/oauth/service/OrcidTokenStoreServiceTest.java index 46a9bc9af7a..5c882cc40ab 100644 --- a/orcid-core/src/test/java/org/orcid/core/oauth/service/OrcidTokenStoreServiceTest.java +++ b/orcid-core/src/test/java/org/orcid/core/oauth/service/OrcidTokenStoreServiceTest.java @@ -33,7 +33,7 @@ import org.orcid.core.manager.ProfileEntityManager; import org.orcid.core.manager.SourceNameCacheManager; import org.orcid.core.manager.v3.read_only.RecordNameManagerReadOnly; -import org.orcid.core.oauth.OrcidOauth2UserAuthentication; +import org.orcid.core.oauth.OrcidUserAuthentication; import org.orcid.persistence.dao.OrcidOauth2TokenDetailDao; import org.orcid.persistence.dao.RecordNameDao; import org.orcid.persistence.jpa.entities.OrcidOauth2TokenDetail; @@ -134,7 +134,7 @@ public void testStoreAccessToken() throws Exception { OAuth2Request request = new OAuth2Request(Collections. emptyMap(), clientId, Collections. emptyList(), true, new HashSet(Arrays.asList("/orcid-profile/read-limited")), Collections. emptySet(), null, Collections. emptySet(), Collections. emptyMap()); ProfileEntity profileEntity = profileEntityManager.findByOrcid("4444-4444-4444-4444"); - OrcidOauth2UserAuthentication userAuthentication = new OrcidOauth2UserAuthentication(profileEntity, true); + OrcidUserAuthentication userAuthentication = new OrcidUserAuthentication(profileEntity, true); OAuth2Authentication authentication = new OAuth2Authentication(request, userAuthentication); @@ -199,7 +199,7 @@ public void testGetAccessToken() throws Exception { new OAuth2Request(Collections. emptyMap(), clientId, Collections. emptyList(), true, new HashSet(Arrays.asList(scope)), Collections. emptySet(), null, Collections. emptySet(), Collections. emptyMap()); ProfileEntity profileEntity = profileEntityManager.findByOrcid(orcid); - OrcidOauth2UserAuthentication userAuthentication = new OrcidOauth2UserAuthentication(profileEntity, true); + OrcidUserAuthentication userAuthentication = new OrcidUserAuthentication(profileEntity, true); OAuth2Authentication authentication = new OAuth2Authentication(request, userAuthentication); diff --git a/orcid-core/src/test/java/org/orcid/core/security/DefaultOAuthClientVisibilityTest.java b/orcid-core/src/test/java/org/orcid/core/security/DefaultOAuthClientVisibilityTest.java deleted file mode 100644 index 950d416295d..00000000000 --- a/orcid-core/src/test/java/org/orcid/core/security/DefaultOAuthClientVisibilityTest.java +++ /dev/null @@ -1,82 +0,0 @@ -package org.orcid.core.security; - -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.when; - -import java.util.Date; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -import javax.annotation.Resource; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBException; -import javax.xml.bind.Unmarshaller; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.orcid.core.BaseTest; -import org.orcid.core.oauth.OrcidOAuth2Authentication; -import org.orcid.core.oauth.OrcidOauth2TokenDetailService; -import org.orcid.core.oauth.OrcidOauth2UserAuthentication; -import org.orcid.jaxb.model.message.OrcidMessage; -import org.orcid.jaxb.model.message.ScopePathType; -import org.orcid.jaxb.model.message.Visibility; -import org.orcid.persistence.jpa.entities.OrcidOauth2TokenDetail; -import org.orcid.persistence.jpa.entities.ProfileEntity; -import org.orcid.core.utils.DateFieldsOnBaseEntityUtils; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.oauth2.provider.AuthorizationRequest; -import org.springframework.test.annotation.Rollback; -import org.springframework.transaction.annotation.Transactional; - -public class DefaultOAuthClientVisibilityTest extends BaseTest { - - @Resource(name = "defaultPermissionChecker") - private PermissionChecker permissionChecker; - - @Mock - private OrcidOauth2TokenDetailService orcidOauth2TokenDetailService; - - @Before - public void mockDependencies() throws Exception { - DefaultPermissionChecker defaultPermissionChecker = (DefaultPermissionChecker) permissionChecker; - defaultPermissionChecker.setOrcidOauthTokenDetailService(orcidOauth2TokenDetailService); - } - - @Test - @Transactional - @Rollback - public void testCheckClientPermissionsAllowOnlyPublicAndLimitedVisibility() throws Exception { - Set resourceIds = new HashSet(Arrays.asList("orcid")); - HashSet grantedAuthorities = new HashSet(Arrays.asList(new SimpleGrantedAuthority("ROLE_CLIENT"))); - AuthorizationRequest request = new AuthorizationRequest("4444-4444-4444-4446", Arrays.asList("/orcid-bio/external-identifiers/create")); - request.setAuthorities(grantedAuthorities); - request.setResourceIds(resourceIds); - ProfileEntity entity = new ProfileEntity("4444-4444-4444-4446"); - OrcidOauth2UserAuthentication oauth2UserAuthentication = new OrcidOauth2UserAuthentication(entity, true); - // we care only that an OAuth client request results in the correct - // visibilities - OrcidOAuth2Authentication oAuth2Authentication = new OrcidOAuth2Authentication(request, oauth2UserAuthentication, "made-up-token"); - - OrcidOauth2TokenDetail tokenDetail = new OrcidOauth2TokenDetail(); - tokenDetail.setScope("/orcid-bio/external-identifiers/create"); - DateFieldsOnBaseEntityUtils.setDateFields(tokenDetail, new Date()); - when(orcidOauth2TokenDetailService.findNonDisabledByTokenValue(any(String.class))).thenReturn(tokenDetail); - ScopePathType scopePathType = ScopePathType.ORCID_BIO_EXTERNAL_IDENTIFIERS_CREATE; - Set visibilitiesForClient = permissionChecker.obtainVisibilitiesForAuthentication(oAuth2Authentication, scopePathType, getOrcidMessage()); - assertTrue(visibilitiesForClient.size() == 3); - assertTrue(visibilitiesForClient.contains(Visibility.LIMITED)); - assertTrue(visibilitiesForClient.contains(Visibility.REGISTERED_ONLY)); - assertTrue(visibilitiesForClient.contains(Visibility.PUBLIC)); - } - - private OrcidMessage getOrcidMessage() throws JAXBException { - JAXBContext context = JAXBContext.newInstance(OrcidMessage.class); - Unmarshaller unmarshaller = context.createUnmarshaller(); - return (OrcidMessage) unmarshaller.unmarshal(DefaultPermissionCheckerTest.class.getResourceAsStream("/orcid-full-message-no-visibility-latest.xml")); - } -} diff --git a/orcid-core/src/test/java/org/orcid/core/security/DefaultPermissionCheckerTest.java b/orcid-core/src/test/java/org/orcid/core/security/DefaultPermissionCheckerTest.java deleted file mode 100644 index fd3c4f47c1f..00000000000 --- a/orcid-core/src/test/java/org/orcid/core/security/DefaultPermissionCheckerTest.java +++ /dev/null @@ -1,170 +0,0 @@ -package org.orcid.core.security; - -import static org.junit.Assert.fail; - -import java.security.AccessControlException; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -import javax.annotation.Resource; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBException; -import javax.xml.bind.Unmarshaller; - -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.orcid.core.manager.ProfileEntityCacheManager; -import org.orcid.core.manager.ProfileEntityManager; -import org.orcid.core.oauth.OrcidOAuth2Authentication; -import org.orcid.core.oauth.OrcidOauth2TokenDetailService; -import org.orcid.core.oauth.OrcidOauth2UserAuthentication; -import org.orcid.jaxb.model.message.OrcidMessage; -import org.orcid.jaxb.model.message.ScopePathType; -import org.orcid.persistence.jpa.entities.OrcidOauth2TokenDetail; -import org.orcid.persistence.jpa.entities.ProfileEntity; -import org.orcid.test.DBUnitTest; -import org.orcid.test.OrcidJUnit4ClassRunner; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.oauth2.provider.AuthorizationRequest; -import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.test.annotation.Rollback; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.transaction.annotation.Transactional; - -/** - * @author Declan Newman (declan) Date: 27/04/2012 - */ -@RunWith(OrcidJUnit4ClassRunner.class) -@ContextConfiguration(locations = { "classpath:test-orcid-core-context.xml" }) -public class DefaultPermissionCheckerTest extends DBUnitTest { - - @Resource(name = "defaultPermissionChecker") - private PermissionChecker defaultPermissionChecker; - - @Resource(name = "profileEntityManager") - private ProfileEntityManager profileEntityManager; - - @Resource - private OrcidOauth2TokenDetailService tokenDetailService; - - @Resource(name = "profileEntityCacheManager") - ProfileEntityCacheManager profileEntityCacheManager; - - @BeforeClass - public static void initDBUnitData() throws Exception { - initDBUnitData(Arrays.asList("/data/SourceClientDetailsEntityData.xml","/data/ProfileEntityData.xml", "/data/ClientDetailsEntityData.xml", "/data/Oauth2TokenDetailsData.xml")); - } - - @AfterClass - public static void removeDBUnitData() throws Exception { - removeDBUnitData(Arrays.asList("/data/Oauth2TokenDetailsData.xml", "/data/ClientDetailsEntityData.xml", "/data/ProfileEntityData.xml")); - } - - @Test - @Rollback - @Transactional - public void testCheckUserPermissionsAuthenticationScopesOrcidAndOrcidMessage() throws Exception { - Set resourceIds = new HashSet(Arrays.asList("orcid")); - HashSet grantedAuthorities = new HashSet(Arrays.asList(new SimpleGrantedAuthority("ROLE_CLIENT"))); - AuthorizationRequest request = new AuthorizationRequest("4444-4444-4444-4441", Arrays.asList("/orcid-bio/external-identifiers/create")); - request.setAuthorities(grantedAuthorities); - request.setResourceIds(resourceIds); - ProfileEntity entity = profileEntityManager.findByOrcid("4444-4444-4444-4446"); - OrcidOauth2UserAuthentication oauth2UserAuthentication = new OrcidOauth2UserAuthentication(entity, true); - OAuth2Authentication oAuth2Authentication = new OrcidOAuth2Authentication(request, oauth2UserAuthentication, "made-up-token"); - ScopePathType requiredScope = ScopePathType.ORCID_BIO_EXTERNAL_IDENTIFIERS_CREATE; - OrcidMessage orcidMessage = getOrcidMessage(); - String messageOrcid = orcidMessage.getOrcidProfile().getOrcidIdentifier().getPath(); - defaultPermissionChecker.checkPermissions(oAuth2Authentication, requiredScope, messageOrcid, orcidMessage); - } - - @Test(expected = AccessControlException.class) - @Transactional - @Rollback - public void testCheckUserPermissionsAuthenticationScopesOrcidAndOrcidMessageWhenWrongUser() throws Exception { - Set resourceIds = new HashSet(Arrays.asList("orcid")); - HashSet grantedAuthorities = new HashSet(Arrays.asList(new SimpleGrantedAuthority("ROLE_CLIENT"))); - AuthorizationRequest request = new AuthorizationRequest("4444-4444-4444-4441", Arrays.asList("/orcid-bio/external-identifiers/create")); - request.setAuthorities(grantedAuthorities); - request.setResourceIds(resourceIds); - ProfileEntity entity = profileEntityManager.findByOrcid("4444-4444-4444-4445"); - OrcidOauth2UserAuthentication oauth2UserAuthentication = new OrcidOauth2UserAuthentication(entity, true); - OAuth2Authentication oAuth2Authentication = new OrcidOAuth2Authentication(request, oauth2UserAuthentication, "made-up-token"); - ScopePathType requiredScope = ScopePathType.ORCID_BIO_EXTERNAL_IDENTIFIERS_CREATE; - OrcidMessage orcidMessage = getOrcidMessage(); - String messageOrcid = orcidMessage.getOrcidProfile().getOrcidIdentifier().getPath(); - defaultPermissionChecker.checkPermissions(oAuth2Authentication, requiredScope, messageOrcid, orcidMessage); - } - - @Test - @Transactional - @Rollback - public void testCheckClientPermissionsAuthenticationScopesOrcidAndOrcidMessage() throws Exception { - Set resourceIds = new HashSet(Arrays.asList("orcid")); - HashSet grantedAuthorities = new HashSet(Arrays.asList(new SimpleGrantedAuthority("ROLE_CLIENT"))); - AuthorizationRequest request = new AuthorizationRequest("APP-5555555555555555", Arrays.asList("/orcid-bio/external-identifiers/create")); - request.setAuthorities(grantedAuthorities); - request.setResourceIds(resourceIds); - OAuth2Authentication oAuth2Authentication = new OrcidOAuth2Authentication(request, null, "made-up-token"); - ScopePathType requiredScope = ScopePathType.ORCID_BIO_EXTERNAL_IDENTIFIERS_CREATE; - OrcidMessage orcidMessage = getOrcidMessage(); - orcidMessage.getOrcidProfile().getOrcidIdentifier().setPath("4444-4444-4444-4447"); - String messageOrcid = orcidMessage.getOrcidProfile().getOrcidIdentifier().getPath(); - defaultPermissionChecker.checkPermissions(oAuth2Authentication, requiredScope, messageOrcid, orcidMessage); - } - - @Test - public void testCheckPermissionsAuthenticationScopesAndOrcidMessage() throws Exception { - Set resourceIds = new HashSet(Arrays.asList("orcid")); - HashSet grantedAuthorities = new HashSet(Arrays.asList(new SimpleGrantedAuthority("ROLE_CLIENT"))); - AuthorizationRequest request = new AuthorizationRequest("4444-4444-4444-4441", Arrays.asList(ScopePathType.ORCID_WORKS_CREATE.value())); - request.setAuthorities(grantedAuthorities); - request.setResourceIds(resourceIds); - OAuth2Authentication oAuth2Authentication = new OrcidOAuth2Authentication(request, null, "made-up-token"); - ScopePathType requiredScope = ScopePathType.ORCID_WORKS_CREATE; - OrcidMessage orcidMessage = getOrcidMessage(); - defaultPermissionChecker.checkPermissions(oAuth2Authentication, requiredScope, orcidMessage); - } - - @Test - public void testCheckPermissionsAuthenticationScopePathTypesAndOrcid() throws Exception { - Set resourceIds = new HashSet(Arrays.asList("orcid")); - HashSet grantedAuthorities = new HashSet(Arrays.asList(new SimpleGrantedAuthority("ROLE_CLIENT"))); - AuthorizationRequest request = new AuthorizationRequest("4444-4444-4444-4441", Arrays.asList(ScopePathType.ORCID_BIO_READ_LIMITED.value())); - request.setAuthorities(grantedAuthorities); - request.setResourceIds(resourceIds); - OAuth2Authentication oAuth2Authentication = new OrcidOAuth2Authentication(request, null, "made-up-token"); - ScopePathType requiredScope = ScopePathType.ORCID_BIO_READ_LIMITED; - defaultPermissionChecker.checkPermissions(oAuth2Authentication, requiredScope, "4444-4444-4444-4447"); - } - - @Test - @Transactional - @Rollback - public void checkRemoveUserGrantWriteScopePastValitityForNonPersistentTokens() { - OrcidOauth2TokenDetail token = tokenDetailService.findIgnoringDisabledByTokenValue("00000001-d80f-4afc-8f95-9b48d28aaadb"); - DefaultPermissionChecker customPermissionChecker = (DefaultPermissionChecker) defaultPermissionChecker; - if(!customPermissionChecker.removeUserGrantWriteScopePastValitity(token)) - fail(); - } - - @Test - @Transactional - @Rollback - public void checkRemoveUserGrantWriteScopePastValitityForPersistentTokens() { - OrcidOauth2TokenDetail token = tokenDetailService.findIgnoringDisabledByTokenValue("00000002-d80f-4afc-8f95-9b48d28aaadb"); - DefaultPermissionChecker customPermissionChecker = (DefaultPermissionChecker) defaultPermissionChecker; - if(customPermissionChecker.removeUserGrantWriteScopePastValitity(token)) - fail(); - } - - private OrcidMessage getOrcidMessage() throws JAXBException { - JAXBContext context = JAXBContext.newInstance(OrcidMessage.class); - Unmarshaller unmarshaller = context.createUnmarshaller(); - return (OrcidMessage) unmarshaller.unmarshal(DefaultPermissionCheckerTest.class.getResourceAsStream("/orcid-full-message-no-visibility-latest.xml")); - } -} From b8bb156f8089c0b9255faea12aefc84fc68d2e22 Mon Sep 17 00:00:00 2001 From: amontenegro Date: Mon, 23 Mar 2026 17:04:08 -0600 Subject: [PATCH 05/16] Removing code and refactoring the authentication objects, introducing OrcidBearerTokenAuthentication --- .../core/oauth/service/OrcidRandomValueTokenServicesImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidRandomValueTokenServicesImpl.java b/orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidRandomValueTokenServicesImpl.java index 6fee9a4f801..a2fabce1f5f 100644 --- a/orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidRandomValueTokenServicesImpl.java +++ b/orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidRandomValueTokenServicesImpl.java @@ -64,7 +64,7 @@ public class OrcidRandomValueTokenServicesImpl { private AuthorizationServerUtil authorizationServerUtil; @Override - public OAuth2Authentication loadAuthentication(String accessTokenValue) throws AuthenticationException { + public OrcidBearerTokenAuthentication loadAuthentication(String accessTokenValue) throws AuthenticationException { try { JSONObject tokenInfo = authorizationServerUtil.tokenIntrospection(accessTokenValue); if(tokenInfo == null) { From ed844a95450ad5eb19ef109fcec31d4f5887e616 Mon Sep 17 00:00:00 2001 From: amontenegro Date: Wed, 25 Mar 2026 15:09:41 -0600 Subject: [PATCH 06/16] More refactoring, security mananger and source managers --- .../core/common/util/AuthenticationUtils.java | 21 +- .../core/manager/OrcidSecurityManager.java | 1 - .../impl/OrcidSecurityManagerImpl.java | 148 ++---- .../core/manager/impl/SourceManagerImpl.java | 42 +- .../core/manager/v3/OrcidSecurityManager.java | 2 - .../v3/impl/OrcidSecurityManagerImpl.java | 157 ++---- .../manager/v3/impl/SourceManagerImpl.java | 125 ++--- .../oauth/OrcidBearerTokenAuthentication.java | 94 +++- .../AuthorizationServerUtil.java | 1 - .../OrcidRandomValueTokenServicesImpl.java | 34 +- .../oauth/IETFExchangeTokenGranterTest.java | 440 ----------------- .../OrcidRandomValueTokenServicesTest.java | 464 ------------------ 12 files changed, 256 insertions(+), 1273 deletions(-) delete mode 100644 orcid-core/src/test/java/org/orcid/core/oauth/IETFExchangeTokenGranterTest.java delete mode 100644 orcid-core/src/test/java/org/orcid/core/oauth/service/OrcidRandomValueTokenServicesTest.java diff --git a/orcid-core/src/main/java/org/orcid/core/common/util/AuthenticationUtils.java b/orcid-core/src/main/java/org/orcid/core/common/util/AuthenticationUtils.java index 7732dfe426a..36e61614d35 100644 --- a/orcid-core/src/main/java/org/orcid/core/common/util/AuthenticationUtils.java +++ b/orcid-core/src/main/java/org/orcid/core/common/util/AuthenticationUtils.java @@ -1,6 +1,7 @@ package org.orcid.core.common.util; import org.apache.commons.lang3.StringUtils; +import org.orcid.core.oauth.OrcidBearerTokenAuthentication; import org.orcid.core.security.OrcidRoles; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -11,8 +12,6 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.security.oauth2.provider.OAuth2Request; import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; import org.springframework.security.web.authentication.switchuser.SwitchUserGrantedAuthority; @@ -43,10 +42,10 @@ public static String retrieveActiveSourceId() { } else if (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication.getClass())) { // Token endpoint return ((UsernamePasswordAuthenticationToken) authentication).getName(); - } else if (OAuth2Authentication.class.isAssignableFrom(authentication.getClass())) { + } else if (OrcidBearerTokenAuthentication.class.isAssignableFrom(authentication.getClass())) { // API - OAuth2Request authorizationRequest = ((OAuth2Authentication) authentication).getOAuth2Request(); - return authorizationRequest.getClientId(); + OrcidBearerTokenAuthentication authDetails = (OrcidBearerTokenAuthentication) authentication; + return authDetails.getClientId(); } else { // Normal web user return AuthenticationUtils.retrieveEffectiveOrcid(); @@ -66,10 +65,10 @@ public static String retrieveRealUserOrcid() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication == null) { return null; - } else if (OAuth2Authentication.class.isAssignableFrom(authentication.getClass())) { + } else if (OrcidBearerTokenAuthentication.class.isAssignableFrom(authentication.getClass())) { // API - OAuth2Request authorizationRequest = ((OAuth2Authentication) authentication).getOAuth2Request(); - return authorizationRequest.getClientId(); + OrcidBearerTokenAuthentication authDetails = (OrcidBearerTokenAuthentication) authentication; + return authDetails.getClientId(); } // Delegation mode String realUserIfInDelegationMode = getRealUserIfInDelegationMode(authentication); @@ -90,12 +89,6 @@ public static boolean isDelegatedByAnAdmin() { SwitchUserGrantedAuthority suga = (SwitchUserGrantedAuthority) authority; Authentication sourceAuthentication = suga.getSource(); - LOGGER.trace("isDelegatedByAnAdmin"); - LOGGER.trace("Authentication: {}", sourceAuthentication); - LOGGER.trace("Authentication type: {}", sourceAuthentication.getClass().getName()); - LOGGER.trace("User Details type: {}", sourceAuthentication.getDetails().getClass()); - LOGGER.trace("Source authorities: {}", sourceAuthentication.getAuthorities()); - if (sourceAuthentication instanceof UsernamePasswordAuthenticationToken) { if(sourceAuthentication.getAuthorities() != null) { return sourceAuthentication.getAuthorities().contains(adminAuthority); diff --git a/orcid-core/src/main/java/org/orcid/core/manager/OrcidSecurityManager.java b/orcid-core/src/main/java/org/orcid/core/manager/OrcidSecurityManager.java index 39b4372dd79..9528669359d 100644 --- a/orcid-core/src/main/java/org/orcid/core/manager/OrcidSecurityManager.java +++ b/orcid-core/src/main/java/org/orcid/core/manager/OrcidSecurityManager.java @@ -56,5 +56,4 @@ public interface OrcidSecurityManager { void checkAndFilter(String orcid, Record record); - String getOrcidFromToken(); } diff --git a/orcid-core/src/main/java/org/orcid/core/manager/impl/OrcidSecurityManagerImpl.java b/orcid-core/src/main/java/org/orcid/core/manager/impl/OrcidSecurityManagerImpl.java index 225e4b148e2..f62ab3d6403 100644 --- a/orcid-core/src/main/java/org/orcid/core/manager/impl/OrcidSecurityManagerImpl.java +++ b/orcid-core/src/main/java/org/orcid/core/manager/impl/OrcidSecurityManagerImpl.java @@ -1,6 +1,5 @@ package org.orcid.core.manager.impl; -import java.security.AccessControlException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -26,8 +25,7 @@ import org.orcid.core.manager.OrcidSecurityManager; import org.orcid.core.manager.ProfileEntityCacheManager; import org.orcid.core.manager.SourceManager; -import org.orcid.core.oauth.OrcidOauth2TokenDetailService; -import org.orcid.core.security.OrcidRoles; +import org.orcid.core.oauth.OrcidBearerTokenAuthentication; import org.orcid.core.security.OrcidUserDetailsService; import org.orcid.core.utils.SourceEntityUtils; import org.orcid.jaxb.model.clientgroup.ClientType; @@ -62,8 +60,6 @@ import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.security.oauth2.provider.OAuth2Request; import org.orcid.core.exception.OrcidUnauthorizedException; /** @@ -82,9 +78,6 @@ public class OrcidSecurityManagerImpl implements OrcidSecurityManager { @Resource private SourceManager sourceManager; - @Resource - private OrcidOauth2TokenDetailService orcidOauthTokenDetailService; - @Resource private ProfileEntityCacheManager profileEntityCacheManager; @@ -115,10 +108,9 @@ public boolean isAdmin() { public String getClientIdFromAPIRequest() { SecurityContext context = SecurityContextHolder.getContext(); Authentication authentication = context.getAuthentication(); - if (authentication != null && OAuth2Authentication.class.isAssignableFrom(authentication.getClass())) { - OAuth2Authentication oAuth2Authentication = (OAuth2Authentication) authentication; - OAuth2Request request = oAuth2Authentication.getOAuth2Request(); - return request.getClientId(); + if (authentication != null && OrcidBearerTokenAuthentication.class.isAssignableFrom(authentication.getClass())) { + OrcidBearerTokenAuthentication authDetails = (OrcidBearerTokenAuthentication) authentication; + return authDetails.getClientId(); } return null; } @@ -224,13 +216,15 @@ public void checkScopes(ScopePathType... requiredScopes) { //Verify the client is not a public client checkClientType(); - OAuth2Authentication oAuth2Authentication = getOAuth2Authentication(); - OAuth2Request authorizationRequest = oAuth2Authentication.getOAuth2Request(); - Set requestedScopes = ScopePathType.getScopesFromStrings(authorizationRequest.getScope()); - for (ScopePathType scope : requestedScopes) { - for (ScopePathType requiredScope : requiredScopes) { - if (scope.hasScope(requiredScope)) { - return; + Authentication authentication = getAuthentication(); + if(OrcidBearerTokenAuthentication.class.isAssignableFrom(authentication.getClass())) { + Set allowedScopes = ((OrcidBearerTokenAuthentication) authentication).getScopes(); + for (String scope : allowedScopes) { + ScopePathType allowed = ScopePathType.fromValue(scope); + for (ScopePathType requiredScope : requiredScopes) { + if (allowed.hasScope(requiredScope)) { + return; + } } } } @@ -450,10 +444,10 @@ public void checkAndFilter(String orcid, WorkBulk workBulk, ScopePathType scopeP String clientId = null; - OAuth2Authentication oAuth2Authentication = getOAuth2Authentication(); - if(oAuth2Authentication != null) { - OAuth2Request authorizationRequest = oAuth2Authentication.getOAuth2Request(); - clientId = authorizationRequest.getClientId(); + Authentication authentication = getAuthentication(); + if(authentication != null) { + OrcidBearerTokenAuthentication authDetails = (OrcidBearerTokenAuthentication) authentication; + clientId = authDetails.getClientId(); } List filteredElements = new ArrayList<>(); @@ -621,10 +615,10 @@ private void checkAndFilter(String orcid, VisibilityType element, ScopePathType // Check if the client is the source of the element if (element instanceof Filterable) { Filterable filterable = (Filterable) element; - OAuth2Authentication oAuth2Authentication = getOAuth2Authentication(); - if (oAuth2Authentication != null) { - OAuth2Request authorizationRequest = oAuth2Authentication.getOAuth2Request(); - String clientId = authorizationRequest.getClientId(); + Authentication authentication = getAuthentication(); + if (authentication != null) { + OrcidBearerTokenAuthentication authDetails = (OrcidBearerTokenAuthentication) authentication; + String clientId = authDetails.getClientId(); if (clientId.equals(filterable.retrieveSourcePath())) { // The client doing the request is the source of the element return; @@ -696,105 +690,47 @@ private void checkVisibility(VisibilityType element, ScopePathType requiredScope throw new IllegalArgumentException("Only 'read-only' scopes are allowed"); } } - - private boolean isNonClientCredentialScope(OAuth2Authentication oAuth2Authentication) { - OAuth2Request authorizationRequest = oAuth2Authentication.getOAuth2Request(); - Set requestedScopes = ScopePathType.getCombinedScopesFromStringsAsStrings(authorizationRequest.getScope()); - for (String scopeName : requestedScopes) { - ScopePathType scopePathType = ScopePathType.fromValue(scopeName); - if (!scopePathType.isClientCreditalScope()) { - return true; - } - } - return false; - } - - private boolean clientIsProfileSource(String clientId, ProfileEntity profile) { - Boolean claimed = profile.getClaimed(); - SourceEntity source = profile.getSource(); - return source != null && (claimed == null || !claimed) && clientId.equals(SourceEntityUtils.getSourceId(source)); - } - - private OAuth2Authentication getOAuth2Authentication() { - SecurityContext context = SecurityContextHolder.getContext(); - if (context != null && context.getAuthentication() != null) { - Authentication authentication = context.getAuthentication(); - if (OAuth2Authentication.class.isAssignableFrom(authentication.getClass())) { - OAuth2Authentication oAuth2Authentication = (OAuth2Authentication) authentication; - return oAuth2Authentication; - } else { - for (GrantedAuthority grantedAuth : authentication.getAuthorities()) { - if ("ROLE_ANONYMOUS".equals(grantedAuth.getAuthority())) { - // Assume that anonymous authority is like not having - // authority at all - return null; - } - } - - throw new AccessControlException( - "Cannot access method with authentication type " + authentication != null ? authentication.toString() : ", as it's null!"); - } - } else { - throw new IllegalStateException("No security context found. This is bad!"); - } - } private void isMyToken(String orcid) { - OAuth2Authentication oAuth2Authentication = getOAuth2Authentication(); - if (oAuth2Authentication == null) { + Authentication authentication = getAuthentication(); + if (authentication == null) { throw new OrcidUnauthorizedException("No OAuth2 authentication found"); } //Verify the client is not a public client checkClientType(); - - String clientId = sourceManager.retrieveSourceOrcid(); - ProfileEntity profile = profileEntityCacheManager.retrieve(orcid); - Authentication userAuthentication = oAuth2Authentication.getUserAuthentication(); - if (userAuthentication != null) { - Object principal = userAuthentication.getPrincipal(); - if (principal instanceof ProfileEntity) { - ProfileEntity profileEntity = (ProfileEntity) principal; - if (!orcid.equals(profileEntity.getId())) { - throw new OrcidUnauthorizedException("Access token is for a different record"); - } - } else { - throw new OrcidUnauthorizedException("Missing user authentication"); + + if(OrcidBearerTokenAuthentication.class.isAssignableFrom(authentication.getClass())) { + OrcidBearerTokenAuthentication authDetails = (OrcidBearerTokenAuthentication) authentication; + if (orcid.equals(authDetails.getUserOrcid())) { + return; } - } else if (isNonClientCredentialScope(oAuth2Authentication) && !clientIsProfileSource(clientId, profile)) { - throw new IllegalStateException("Non client credential scope found in client request"); } + throw new OrcidUnauthorizedException("Access token is for a different record"); } private void checkClientType() { String clientId = sourceManager.retrieveSourceOrcid(); - ClientDetailsEntity client = clientDetailsEntityCacheManager.retrieve(clientId); if(client.getClientType() == null || ClientType.PUBLIC_CLIENT.name().equals(client.getClientType())) { throw new OrcidUnauthorizedException("The client application is forbidden to perform the action."); } } - - @Override - public String getOrcidFromToken(){ - OAuth2Authentication oAuth2Authentication = getOAuth2Authentication(); - if (oAuth2Authentication == null) { - throw new OrcidUnauthorizedException("No OAuth2 authentication found"); - } - - checkScopes(ScopePathType.AUTHENTICATE); - - Authentication userAuthentication = oAuth2Authentication.getUserAuthentication(); - if (userAuthentication != null) { - Object principal = userAuthentication.getPrincipal(); - if (principal instanceof ProfileEntity) { - ProfileEntity profileEntity = (ProfileEntity) principal; - return profileEntity.getId(); - } else { - throw new OrcidUnauthorizedException("Missing user authentication"); + + private Authentication getAuthentication() { + SecurityContext context = SecurityContextHolder.getContext(); + if (context != null && context.getAuthentication() != null) { + Authentication authentication = context.getAuthentication(); + for (GrantedAuthority grantedAuth : authentication.getAuthorities()) { + if ("ROLE_ANONYMOUS".equals(grantedAuth.getAuthority())) { + // Assume that anonymous authority is like not having + // authority at all + return null; + } } + return authentication; } else { - throw new IllegalStateException("Non client credential scope found in client request"); + throw new IllegalStateException("No security context found. This is bad!"); } } } diff --git a/orcid-core/src/main/java/org/orcid/core/manager/impl/SourceManagerImpl.java b/orcid-core/src/main/java/org/orcid/core/manager/impl/SourceManagerImpl.java index b4f7453cb33..509d17d147b 100644 --- a/orcid-core/src/main/java/org/orcid/core/manager/impl/SourceManagerImpl.java +++ b/orcid-core/src/main/java/org/orcid/core/manager/impl/SourceManagerImpl.java @@ -9,18 +9,13 @@ import org.orcid.core.manager.SourceManager; import org.orcid.core.manager.SourceNameCacheManager; import org.orcid.core.manager.read_only.impl.ManagerReadOnlyBaseImpl; +import org.orcid.core.oauth.OrcidBearerTokenAuthentication; import org.orcid.persistence.dao.ProfileDao; import org.orcid.persistence.jpa.entities.ClientDetailsEntity; import org.orcid.persistence.jpa.entities.ProfileEntity; import org.orcid.persistence.jpa.entities.SourceEntity; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.security.oauth2.provider.OAuth2Request; -import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; -import org.springframework.security.web.authentication.switchuser.SwitchUserGrantedAuthority; /** * @@ -49,29 +44,26 @@ public SourceEntity retrieveSourceEntity() { if (authentication == null) { return null; } - - // API - if (OAuth2Authentication.class.isAssignableFrom(authentication.getClass())) { - OAuth2Request authorizationRequest = ((OAuth2Authentication) authentication).getOAuth2Request(); - String clientId = authorizationRequest.getClientId(); + + SourceEntity sourceEntity = new SourceEntity(); + // API authentication + if (OrcidBearerTokenAuthentication.class.isAssignableFrom(authentication.getClass())) { + OrcidBearerTokenAuthentication authDetails = (OrcidBearerTokenAuthentication) authentication; + String clientId = authDetails.getClientId(); ClientDetailsEntity clientDetails = clientDetailsManager.findByClientId(clientId); - SourceEntity sourceEntity = new SourceEntity(); - ClientDetailsEntity sourceClient = new ClientDetailsEntity(clientId, clientDetails.getClientName()); sourceClient.setUserOBOEnabled(clientDetails.isUserOBOEnabled()); - sourceEntity.setSourceClient(sourceClient); - - return sourceEntity; + sourceEntity.setSourceClient(sourceClient); + } else { + // User authentication + String userOrcid = AuthenticationUtils.retrieveEffectiveOrcid(); + if (userOrcid == null) { + // Must be system role + return null; + } + sourceEntity.setSourceProfile(new ProfileEntity(userOrcid)); + sourceEntity.setCachedSourceName(sourceNameCacheManager.retrieve(userOrcid)); } - String userOrcid = AuthenticationUtils.retrieveEffectiveOrcid(); - if(userOrcid == null){ - // Must be system role - return null; - } - // Normal web user - SourceEntity sourceEntity = new SourceEntity(); - sourceEntity.setSourceProfile(new ProfileEntity(userOrcid)); - sourceEntity.setCachedSourceName(sourceNameCacheManager.retrieve(userOrcid)); return sourceEntity; } diff --git a/orcid-core/src/main/java/org/orcid/core/manager/v3/OrcidSecurityManager.java b/orcid-core/src/main/java/org/orcid/core/manager/v3/OrcidSecurityManager.java index ca2426afc3f..d5d8f510c2c 100644 --- a/orcid-core/src/main/java/org/orcid/core/manager/v3/OrcidSecurityManager.java +++ b/orcid-core/src/main/java/org/orcid/core/manager/v3/OrcidSecurityManager.java @@ -57,6 +57,4 @@ public interface OrcidSecurityManager { void checkAndFilter(String orcid, Person person); void checkAndFilter(String orcid, Record record); - - String getOrcidFromToken(); } diff --git a/orcid-core/src/main/java/org/orcid/core/manager/v3/impl/OrcidSecurityManagerImpl.java b/orcid-core/src/main/java/org/orcid/core/manager/v3/impl/OrcidSecurityManagerImpl.java index 894e448237f..bfd0e746e89 100644 --- a/orcid-core/src/main/java/org/orcid/core/manager/v3/impl/OrcidSecurityManagerImpl.java +++ b/orcid-core/src/main/java/org/orcid/core/manager/v3/impl/OrcidSecurityManagerImpl.java @@ -1,6 +1,5 @@ package org.orcid.core.manager.v3.impl; -import java.security.AccessControlException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -28,8 +27,7 @@ import org.orcid.core.manager.ProfileEntityCacheManager; import org.orcid.core.manager.v3.OrcidSecurityManager; import org.orcid.core.manager.v3.SourceManager; -import org.orcid.core.oauth.OrcidOauth2TokenDetailService; -import org.orcid.core.security.OrcidRoles; +import org.orcid.core.oauth.OrcidBearerTokenAuthentication; import org.orcid.core.security.OrcidUserDetailsService; import org.orcid.core.utils.SourceEntityUtils; import org.orcid.jaxb.model.clientgroup.ClientType; @@ -67,12 +65,8 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.security.oauth2.provider.OAuth2Request; /** * @@ -90,9 +84,6 @@ public class OrcidSecurityManagerImpl implements OrcidSecurityManager { @Resource(name = "sourceManagerV3") private SourceManager sourceManager; - @Resource - private OrcidOauth2TokenDetailService orcidOauthTokenDetailService; - @Resource private ProfileEntityCacheManager profileEntityCacheManager; @@ -128,10 +119,9 @@ public boolean isPasswordConfirmationRequired() { public String getClientIdFromAPIRequest() { SecurityContext context = SecurityContextHolder.getContext(); Authentication authentication = context.getAuthentication(); - if (authentication != null && OAuth2Authentication.class.isAssignableFrom(authentication.getClass())) { - OAuth2Authentication oAuth2Authentication = (OAuth2Authentication) authentication; - OAuth2Request request = oAuth2Authentication.getOAuth2Request(); - return request.getClientId(); + if (authentication != null && OrcidBearerTokenAuthentication.class.isAssignableFrom(authentication.getClass())) { + OrcidBearerTokenAuthentication authDetails = (OrcidBearerTokenAuthentication) authentication; + return authDetails.getClientId(); } return null; } @@ -246,15 +236,19 @@ private void checkScopes(boolean tokenAlreadyChecked, ScopePathType... requiredS checkClientType(); } - OAuth2Authentication oAuth2Authentication = getOAuth2Authentication(); - OAuth2Request authorizationRequest = oAuth2Authentication.getOAuth2Request(); - Set requestedScopes = ScopePathType.getScopesFromStrings(authorizationRequest.getScope()); - for (ScopePathType scope : requestedScopes) { - for (ScopePathType requiredScope : requiredScopes) - if (scope.hasScope(requiredScope)) { - return; + Authentication authentication = getAuthentication(); + if (authentication != null) { + Set allowedScopes = ((OrcidBearerTokenAuthentication) authentication).getScopes(); + for (String scope : allowedScopes) { + ScopePathType allowed = ScopePathType.fromValue(scope); + for (ScopePathType requiredScope : requiredScopes) { + if (allowed.hasScope(requiredScope)) { + return; + } } + } } + throw new OrcidAccessControlException(); } @@ -541,10 +535,9 @@ public void checkAndFilter(String orcid, WorkBulk workBulk, ScopePathType scopeP } String clientId = null; - OAuth2Authentication oAuth2Authentication = getOAuth2Authentication(); - if (oAuth2Authentication != null) { - OAuth2Request authorizationRequest = oAuth2Authentication.getOAuth2Request(); - clientId = authorizationRequest.getClientId(); + Authentication authentication = getAuthentication(); + if (authentication != null) { + clientId = ((OrcidBearerTokenAuthentication) authentication).getClientId(); } List filteredElements = new ArrayList<>(); @@ -725,13 +718,14 @@ private void checkAndFilter(String orcid, VisibilityType element, ScopePathType // Check if the client is the source of the element if (element instanceof Filterable) { Filterable filterable = (Filterable) element; - OAuth2Authentication oAuth2Authentication = getOAuth2Authentication(); - if (oAuth2Authentication != null) { - OAuth2Request authorizationRequest = oAuth2Authentication.getOAuth2Request(); - String clientId = authorizationRequest.getClientId(); - if (clientId.equals(filterable.retrieveSourcePath())) { - // The client doing the request is the source of the element - return; + Authentication authentication = getAuthentication(); + if (authentication != null) { + if(OrcidBearerTokenAuthentication.class.isAssignableFrom(authentication.getClass())) { + String clientId = ((OrcidBearerTokenAuthentication) authentication).getClientId(); + if (clientId.equals(filterable.retrieveSourcePath())) { + // The client doing the request is the source of the element + return; + } } } } @@ -783,75 +777,22 @@ private void checkVisibility(VisibilityType element, ScopePathType requiredScope } } - private boolean isNonClientCredentialScope(OAuth2Authentication oAuth2Authentication) { - OAuth2Request authorizationRequest = oAuth2Authentication.getOAuth2Request(); - Set requestedScopes = ScopePathType.getCombinedScopesFromStringsAsStrings(authorizationRequest.getScope()); - for (String scopeName : requestedScopes) { - ScopePathType scopePathType = ScopePathType.fromValue(scopeName); - if (!scopePathType.isClientCreditalScope()) { - return true; - } - } - return false; - } - - private boolean clientIsProfileSource(String clientId, ProfileEntity profile) { - Boolean claimed = profile.getClaimed(); - SourceEntity source = profile.getSource(); - return source != null && (claimed == null || !claimed) && clientId.equals(SourceEntityUtils.getSourceId(source)); - } - - private OAuth2Authentication getOAuth2Authentication() { - SecurityContext context = SecurityContextHolder.getContext(); - if (context != null && context.getAuthentication() != null) { - Authentication authentication = context.getAuthentication(); - if (OAuth2Authentication.class.isAssignableFrom(authentication.getClass())) { - OAuth2Authentication oAuth2Authentication = (OAuth2Authentication) authentication; - return oAuth2Authentication; - } else { - for (GrantedAuthority grantedAuth : authentication.getAuthorities()) { - if ("ROLE_ANONYMOUS".equals(grantedAuth.getAuthority())) { - // Assume that anonymous authority is like not having - // authority at all - return null; - } - } - - throw new AccessControlException( - "Cannot access method with authentication type " + authentication != null ? authentication.toString() : ", as it's null!"); - } - } else { - throw new IllegalStateException("No security context found. This is bad!"); - } - } - private void isMyToken(String orcid) { - OAuth2Authentication oAuth2Authentication = getOAuth2Authentication(); - if (oAuth2Authentication == null) { + Authentication authentication = getAuthentication(); + if (authentication == null) { throw new OrcidUnauthorizedException("No OAuth2 authentication found"); } - // Verify the client is not a public client + //Verify the client is not a public client checkClientType(); - String clientId = sourceManager.retrieveActiveSourceId(); - Authentication userAuthentication = oAuth2Authentication.getUserAuthentication(); - if (userAuthentication != null) { - Object principal = userAuthentication.getPrincipal(); - if (principal instanceof ProfileEntity) { - ProfileEntity profileEntity = (ProfileEntity) principal; - if (!orcid.equals(profileEntity.getId())) { - throw new OrcidUnauthorizedException("Access token is for a different record"); - } - } else { - throw new OrcidUnauthorizedException("Missing user authentication"); - } - } else if (isNonClientCredentialScope(oAuth2Authentication)) { - ProfileEntity profile = profileEntityCacheManager.retrieve(orcid); - if(!clientIsProfileSource(clientId, profile)) { - throw new IllegalStateException("Non client credential scope found in client request"); + if(OrcidBearerTokenAuthentication.class.isAssignableFrom(authentication.getClass())) { + OrcidBearerTokenAuthentication authDetails = (OrcidBearerTokenAuthentication) authentication; + if (orcid.equals(authDetails.getUserOrcid())) { + return; } } + throw new OrcidUnauthorizedException("Access token is for a different record"); } private void checkClientType() { @@ -862,26 +803,20 @@ private void checkClientType() { } } - @Override - public String getOrcidFromToken() { - OAuth2Authentication oAuth2Authentication = getOAuth2Authentication(); - if (oAuth2Authentication == null) { - throw new OrcidUnauthorizedException("No OAuth2 authentication found"); - } - - checkScopes(false, ScopePathType.AUTHENTICATE); - - Authentication userAuthentication = oAuth2Authentication.getUserAuthentication(); - if (userAuthentication != null) { - Object principal = userAuthentication.getPrincipal(); - if (principal instanceof ProfileEntity) { - ProfileEntity profileEntity = (ProfileEntity) principal; - return profileEntity.getId(); - } else { - throw new OrcidUnauthorizedException("Missing user authentication"); + private Authentication getAuthentication() { + SecurityContext context = SecurityContextHolder.getContext(); + if (context != null && context.getAuthentication() != null) { + Authentication authentication = context.getAuthentication(); + for (GrantedAuthority grantedAuth : authentication.getAuthorities()) { + if ("ROLE_ANONYMOUS".equals(grantedAuth.getAuthority())) { + // Assume that anonymous authority is like not having + // authority at all + return null; + } } + return authentication; } else { - throw new IllegalStateException("Non client credential scope found in client request"); + throw new IllegalStateException("No security context found. This is bad!"); } } } diff --git a/orcid-core/src/main/java/org/orcid/core/manager/v3/impl/SourceManagerImpl.java b/orcid-core/src/main/java/org/orcid/core/manager/v3/impl/SourceManagerImpl.java index 3fed5bbc201..c60d63f5fac 100644 --- a/orcid-core/src/main/java/org/orcid/core/manager/v3/impl/SourceManagerImpl.java +++ b/orcid-core/src/main/java/org/orcid/core/manager/v3/impl/SourceManagerImpl.java @@ -7,10 +7,7 @@ import org.orcid.core.manager.ClientDetailsManager; import org.orcid.core.manager.SourceNameCacheManager; import org.orcid.core.manager.v3.SourceManager; -import org.orcid.core.oauth.OrcidOAuth2Authentication; -import org.orcid.core.oauth.OrcidUserAuthentication; -import org.orcid.core.oauth.OrcidOboOAuth2Authentication; -import org.orcid.core.togglz.Features; +import org.orcid.core.oauth.OrcidBearerTokenAuthentication; import org.orcid.jaxb.model.v3.release.common.Source; import org.orcid.jaxb.model.v3.release.common.SourceClientId; import org.orcid.jaxb.model.v3.release.common.SourceName; @@ -18,14 +15,10 @@ import org.orcid.persistence.dao.OrcidOauth2TokenDetailDao; import org.orcid.persistence.dao.ProfileDao; import org.orcid.persistence.jpa.entities.ClientDetailsEntity; -import org.orcid.persistence.jpa.entities.OrcidOauth2TokenDetail; import org.orcid.persistence.jpa.entities.ProfileEntity; import org.orcid.persistence.jpa.entities.SourceEntity; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.security.oauth2.provider.OAuth2Request; -import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails; /** * @@ -64,66 +57,39 @@ public Source retrieveActiveSource() { if (authentication == null) { return null; } - - // API - if (OAuth2Authentication.class.isAssignableFrom(authentication.getClass())) { - OAuth2Request authorizationRequest = ((OAuth2Authentication) authentication).getOAuth2Request(); - String clientId = authorizationRequest.getClientId(); + Source source = new Source(); + + // API authentication + if (OrcidBearerTokenAuthentication.class.isAssignableFrom(authentication.getClass())) { + OrcidBearerTokenAuthentication authDetails = (OrcidBearerTokenAuthentication) authentication; + String clientId = authDetails.getClientId(); ClientDetailsEntity clientDetails = clientDetailsManager.findByClientId(clientId); - Source source = new Source(); + source.setSourceClientId(new SourceClientId(clientId)); source.setSourceName(new SourceName(clientDetails.getClientName())); - - //OBO if needed - if(Features.OAUTH_TOKEN_VALIDATION.isActive()) { - if(OrcidOboOAuth2Authentication.class.isAssignableFrom(authentication.getClass())) { - OrcidOboOAuth2Authentication authDetails = (OrcidOboOAuth2Authentication) authentication; - if (StringUtils.isNotBlank(authDetails.getOboClientId())) { - ClientDetailsEntity oboClientDetails = clientDetailsManager.findByClientId(authDetails.getOboClientId()); - source.setAssertionOriginClientId(new SourceClientId(oboClientDetails.getClientId())); - source.setAssertionOriginName(new SourceName(oboClientDetails.getClientName())); - } else { - if(clientDetails.isUserOBOEnabled() && authDetails.getUserAuthentication() != null && OrcidUserAuthentication.class.isAssignableFrom(authDetails.getUserAuthentication().getClass())) { - OrcidUserAuthentication userAuth = (OrcidUserAuthentication) authDetails.getUserAuthentication(); - ProfileEntity profile = (ProfileEntity) userAuth.getPrincipal(); - source.setAssertionOriginOrcid(new SourceOrcid(profile.getId())); - source.setAssertionOriginName(new SourceName(sourceNameCacheManager.retrieve(profile.getId()))); - } - } - } else { - OrcidOAuth2Authentication authDetails = (OrcidOAuth2Authentication) authentication; - if(clientDetails.isUserOBOEnabled() && authDetails.getUserAuthentication() != null && OrcidUserAuthentication.class.isAssignableFrom(authDetails.getUserAuthentication().getClass())) { - OrcidUserAuthentication userAuth = (OrcidUserAuthentication) authDetails.getUserAuthentication(); - ProfileEntity profile = (ProfileEntity) userAuth.getPrincipal(); - source.setAssertionOriginOrcid(new SourceOrcid(profile.getId())); - source.setAssertionOriginName(new SourceName(sourceNameCacheManager.retrieve(profile.getId()))); - } - } - } else { - OAuth2AuthenticationDetails authDetails = (OAuth2AuthenticationDetails) ((OAuth2Authentication) authentication).getDetails(); - if (authDetails != null && authDetails.getTokenValue() != null) { //check here because mock tests can't cope otherwise. - // TODO: Use the authorization server to build this list of tokens - OrcidOauth2TokenDetail tokenDetail = orcidOauth2TokenDetailDao.findByTokenValue(authDetails.getTokenValue()); - if (!StringUtils.isEmpty(tokenDetail.getOboClientDetailsId())) { - ClientDetailsEntity oboClientDetails = clientDetailsManager.findByClientId(tokenDetail.getOboClientDetailsId()); - source.setAssertionOriginClientId(new SourceClientId(oboClientDetails.getClientId())); - source.setAssertionOriginName(new SourceName(oboClientDetails.getClientName())); - } else if (tokenDetail.getOrcid() != null && clientDetails.isUserOBOEnabled()) { - source.setAssertionOriginOrcid(new SourceOrcid(tokenDetail.getOrcid())); - source.setAssertionOriginName(new SourceName(sourceNameCacheManager.retrieve(tokenDetail.getOrcid()))); - } - } + + // Check member OBO + if(StringUtils.isNotBlank(authDetails.getOboClientId())) { + String oboClientId = authDetails.getOboClientId(); + ClientDetailsEntity oboClientDetails = clientDetailsManager.findByClientId(oboClientId); + source.setAssertionOriginClientId(new SourceClientId(oboClientId)); + source.setAssertionOriginName(new SourceName(oboClientDetails.getClientName())); + } else if(clientDetails.isUserOBOEnabled()){ + // Check user OBO + source.setAssertionOriginOrcid(new SourceOrcid(authDetails.getUserOrcid())); + source.setAssertionOriginName(new SourceName(sourceNameCacheManager.retrieve(authDetails.getUserOrcid()))); } - return source; - } - String userOrcid = AuthenticationUtils.retrieveEffectiveOrcid(); - if(userOrcid == null){ - // Must be system role - return null; + } else { + // User authentication + String userOrcid = AuthenticationUtils.retrieveEffectiveOrcid(); + if(userOrcid == null){ + // Must be system role + return null; + } + // Normal web user + source.setSourceOrcid(new SourceOrcid(userOrcid)); } - // Normal web user - Source source = new Source(); - source.setSourceOrcid(new SourceOrcid(userOrcid)); + return source; } @@ -137,25 +103,24 @@ public SourceEntity retrieveActiveSourceEntity() { if (authentication == null) { return null; } - - // API - if (OAuth2Authentication.class.isAssignableFrom(authentication.getClass())) { - OAuth2Request authorizationRequest = ((OAuth2Authentication) authentication).getOAuth2Request(); - String clientId = authorizationRequest.getClientId(); + + SourceEntity sourceEntity = new SourceEntity(); + // API authentication + if (OrcidBearerTokenAuthentication.class.isAssignableFrom(authentication.getClass())) { + OrcidBearerTokenAuthentication authDetails = (OrcidBearerTokenAuthentication) authentication; + String clientId = authDetails.getClientId(); ClientDetailsEntity clientDetails = clientDetailsManager.findByClientId(clientId); - SourceEntity sourceEntity = new SourceEntity(); - sourceEntity.setSourceClient(new ClientDetailsEntity(clientId, clientDetails.getClientName())); - return sourceEntity; - } - String userOrcid = AuthenticationUtils.retrieveEffectiveOrcid(); - if(userOrcid == null){ - // Must be system role - return null; + sourceEntity.setSourceClient(new ClientDetailsEntity(clientId, clientDetails.getClientName())); + } else { + // User authentication + String userOrcid = AuthenticationUtils.retrieveEffectiveOrcid(); + if (userOrcid == null) { + // Must be system role + return null; + } + sourceEntity.setSourceProfile(new ProfileEntity(userOrcid)); + sourceEntity.setCachedSourceName(sourceNameCacheManager.retrieve(userOrcid)); } - // Normal web user - SourceEntity sourceEntity = new SourceEntity(); - sourceEntity.setSourceProfile(new ProfileEntity(userOrcid)); - sourceEntity.setCachedSourceName(sourceNameCacheManager.retrieve(userOrcid)); return sourceEntity; } diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/OrcidBearerTokenAuthentication.java b/orcid-core/src/main/java/org/orcid/core/oauth/OrcidBearerTokenAuthentication.java index c8a034d599b..aa7ccb9c396 100644 --- a/orcid-core/src/main/java/org/orcid/core/oauth/OrcidBearerTokenAuthentication.java +++ b/orcid-core/src/main/java/org/orcid/core/oauth/OrcidBearerTokenAuthentication.java @@ -5,6 +5,7 @@ import org.springframework.security.core.GrantedAuthority; import java.util.Collection; +import java.util.Set; /** * @author Declan Newman (declan) Date: 17/04/2012 @@ -15,11 +16,26 @@ public class OrcidBearerTokenAuthentication implements Authentication { * */ private static final long serialVersionUID = 1L; - private ClientDetailsEntity clientDetails; + private String clientId; + private String userOrcid; + private String token; + private Set scopes; + private Set authorities; private boolean authenticated = false; + private String oboClientId; - public OrcidBearerTokenAuthentication(ClientDetailsEntity clientDetails) { - this.clientDetails = clientDetails; + private OrcidBearerTokenAuthentication() { + + } + + private OrcidBearerTokenAuthentication(Builder builder) { + this.clientId = builder.clientId; + this.userOrcid = builder.userOrcid; + this.token = builder.token; + this.scopes = builder.scopes; + this.authorities = builder.authorities; + this.authenticated = builder.authenticated; + this.oboClientId = builder.oboClientId; } /** @@ -38,7 +54,7 @@ public OrcidBearerTokenAuthentication(ClientDetailsEntity clientDetails) { */ @Override public Collection getAuthorities() { - return clientDetails.getAuthorities(); + return this.getAuthorities(); } /** @@ -52,7 +68,7 @@ public Collection getAuthorities() { */ @Override public Object getCredentials() { - return clientDetails.getClientSecret(); + return this.token; } /** @@ -64,7 +80,7 @@ public Object getCredentials() { */ @Override public Object getDetails() { - return clientDetails.getAuthorizedGrantTypes(); + return null; } /** @@ -83,7 +99,7 @@ public Object getDetails() { */ @Override public Object getPrincipal() { - return clientDetails; + return clientId; } /** @@ -123,7 +139,7 @@ public boolean isAuthenticated() { * parameter (which would indicate the authentication token is trusted - a * potential security risk) the implementation should throw an * {@link IllegalArgumentException}. - * + * * @param isAuthenticated * true if the token should be trusted (which may * result in an exception) or false if the token @@ -141,6 +157,66 @@ public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentExce @Override public String getName() { - return clientDetails.getClientName(); + return this.clientId; + } + + public String getClientId() { + return clientId; + } + + public String getUserOrcid() { + return userOrcid; + } + + public Set getScopes() { + return scopes; + } + + public String getOboClientId() { + return this.oboClientId; + } + + public static Builder builder(String clientId, String userOrcid, String token) { + return new Builder(clientId, userOrcid, token); + } + + public static class Builder { + private final String clientId; + private final String userOrcid; + private final String token; + private Set scopes; + private Set authorities; + private boolean authenticated = false; + private String oboClientId; + + private Builder(String clientId, String userOrcid, String token) { + this.clientId = clientId; + this.userOrcid = userOrcid; + this.token = token; + } + + public Builder scopes(Set scopes) { + this.scopes = scopes; + return this; + } + + public Builder authorities(Set authorities) { + this.authorities = authorities; + return this; + } + + public Builder authenticated(boolean authenticated) { + this.authenticated = authenticated; + return this; + } + + public Builder oboClientId(String oboClientId) { + this.oboClientId = oboClientId; + return this; + } + + public OrcidBearerTokenAuthentication build() { + return new OrcidBearerTokenAuthentication(this); + } } } diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/authorizationServer/AuthorizationServerUtil.java b/orcid-core/src/main/java/org/orcid/core/oauth/authorizationServer/AuthorizationServerUtil.java index 3015f6a4555..c0fc48d13bc 100644 --- a/orcid-core/src/main/java/org/orcid/core/oauth/authorizationServer/AuthorizationServerUtil.java +++ b/orcid-core/src/main/java/org/orcid/core/oauth/authorizationServer/AuthorizationServerUtil.java @@ -9,7 +9,6 @@ import org.orcid.core.togglz.Features; import org.orcid.core.utils.http.HttpRequestUtils; import org.springframework.beans.factory.annotation.Value; -import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; import org.springframework.stereotype.Component; import javax.annotation.Resource; diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidRandomValueTokenServicesImpl.java b/orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidRandomValueTokenServicesImpl.java index a2fabce1f5f..65389872441 100644 --- a/orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidRandomValueTokenServicesImpl.java +++ b/orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidRandomValueTokenServicesImpl.java @@ -1,6 +1,7 @@ package org.orcid.core.oauth.service; import java.util.*; +import java.util.stream.Collectors; import javax.annotation.Resource; import javax.persistence.PersistenceException; @@ -13,6 +14,7 @@ import org.orcid.core.constants.OrcidOauth2Constants; import org.orcid.core.constants.RevokeReason; import org.orcid.core.exception.ClientDeactivatedException; +import org.orcid.core.exception.InvalidTokenException; import org.orcid.core.exception.LockedException; import org.orcid.core.manager.ClientDetailsEntityCacheManager; import org.orcid.core.manager.ProfileEntityManager; @@ -39,15 +41,14 @@ import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; -import org.orcid.core.exception.InvalidTokenException - +import org.orcid.core.exception.OrcidInvalidScopeException; import com.google.common.collect.Sets; /** * @author Declan Newman (declan) Date: 11/05/2012 */ @Deprecated -//TODO: This have to be removed!!!! +//TODO: This have to be removed, just a placeholder to remember where we validate the tokens!!!! public class OrcidRandomValueTokenServicesImpl { private static final Logger LOGGER = LoggerFactory.getLogger(OrcidRandomValueTokenServicesImpl.class); @@ -63,7 +64,6 @@ public class OrcidRandomValueTokenServicesImpl { @Resource private AuthorizationServerUtil authorizationServerUtil; - @Override public OrcidBearerTokenAuthentication loadAuthentication(String accessTokenValue) throws AuthenticationException { try { JSONObject tokenInfo = authorizationServerUtil.tokenIntrospection(accessTokenValue); @@ -96,31 +96,25 @@ public OrcidBearerTokenAuthentication loadAuthentication(String accessTokenValue } private OrcidBearerTokenAuthentication buildAuthentication(String accessTokenValue, JSONObject tokenInfo) throws JSONException { - // What will we get when the token does not exists? String clientId = tokenInfo.getString("client_id"); - Authentication authentication = null; - Set scopes = OAuth2Utils.parseParameterList(tokenInfo.getString("scope")); - AuthorizationRequest request = new AuthorizationRequest(clientId, scopes); - request.setApproved(true); - if(tokenInfo.has("username")) { - String orcid = tokenInfo.getString("username"); - ProfileEntity profile = new ProfileEntity(orcid); - authentication = new OrcidOauth2UserAuthentication(profile, true); - } - // `orcid` is the only resource id and it is applied to all clients - request.setResourceIds(Set.of("orcid")); + String userOrcid = tokenInfo.getString("username"); + + OrcidBearerTokenAuthentication.Builder builder = OrcidBearerTokenAuthentication.builder(clientId, userOrcid, accessTokenValue); + + Set scopes = Arrays.stream(tokenInfo.getString("scope").split("[\\s,]+")) + .collect(Collectors.toSet()); + builder.scopes(scopes); // Set granted authorities if(tokenInfo.has("clientGrantedAuthority")) { - GrantedAuthority ga = new SimpleGrantedAuthority(tokenInfo.getString("clientGrantedAuthority")); - request.setAuthorities(List.of(ga)); + builder.authorities(Sets.newHashSet(new SimpleGrantedAuthority(tokenInfo.getString("clientGrantedAuthority")))); } if(tokenInfo.has("OBO_CLIENT_ID")) { String oboClientId = tokenInfo.getString("OBO_CLIENT_ID"); - return new OrcidOboOAuth2Authentication(oboClientId, request, authentication, accessTokenValue); + builder.oboClientId(oboClientId); } - return new OrcidOAuth2Authentication(request, authentication, accessTokenValue); + return builder.build(); } } diff --git a/orcid-core/src/test/java/org/orcid/core/oauth/IETFExchangeTokenGranterTest.java b/orcid-core/src/test/java/org/orcid/core/oauth/IETFExchangeTokenGranterTest.java deleted file mode 100644 index 11293d1b3b3..00000000000 --- a/orcid-core/src/test/java/org/orcid/core/oauth/IETFExchangeTokenGranterTest.java +++ /dev/null @@ -1,440 +0,0 @@ -package org.orcid.core.oauth; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.io.IOException; -import java.net.URISyntaxException; -import java.security.NoSuchAlgorithmException; -import java.text.ParseException; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.orcid.core.constants.OrcidOauth2Constants; -import org.orcid.core.constants.RevokeReason; -import org.orcid.core.exception.ClientDeactivatedException; -import org.orcid.core.exception.LockedException; -import org.orcid.core.exception.OrcidInvalidScopeException; -import org.orcid.core.manager.ClientDetailsEntityCacheManager; -import org.orcid.core.manager.ProfileEntityManager; -import org.orcid.core.oauth.openid.OpenIDConnectKeyService; -import org.orcid.core.oauth.service.OrcidOAuth2RequestValidator; -import org.orcid.persistence.dao.MemberOBOWhitelistedClientDao; -import org.orcid.persistence.dao.ProfileDao; -import org.orcid.persistence.jpa.entities.ClientDetailsEntity; -import org.orcid.persistence.jpa.entities.MemberOBOWhitelistedClientEntity; -import org.orcid.persistence.jpa.entities.OrcidGrantedAuthority; -import org.orcid.persistence.jpa.entities.OrcidOauth2TokenDetail; -import org.orcid.persistence.jpa.entities.ProfileEntity; -import org.orcid.test.TargetProxyHelper; -import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; -import org.springframework.security.oauth2.provider.TokenRequest; - -import com.nimbusds.jose.JOSEException; -import com.nimbusds.jwt.JWTClaimsSet; -import com.nimbusds.jwt.JWTClaimsSet.Builder; - -public class IETFExchangeTokenGranterTest { - - private static final String GRANT_TYPE = OrcidOauth2Constants.IETF_EXCHANGE_GRANT_TYPE; - - private static final String ORCID = "0000-0000-0000-0000"; - - private static final String AUDIENCE_CLIENT_ID = "AUD_CLIENT"; - - private static final String ACTIVE_CLIENT_ID = "ACTIVE"; - - @Mock - private OrcidRandomValueTokenServices tokenServicesMock; - - @Mock - private ClientDetailsEntityCacheManager clientDetailsEntityCacheManagerMock; - - @Mock - private OrcidOAuth2RequestValidator orcidOAuth2RequestValidatorMock; - - @Mock - private OpenIDConnectKeyService openIDConnectKeyServiceMock; - - @Mock - private ClientDetailsEntity activeClientMock; - - @Mock - private MemberOBOWhitelistedClientDao memberOBOWhitelistedClientDaoMock; - - @Mock - private OrcidOauth2TokenDetailService orcidOauthTokenDetailServiceMock; - - @Mock - private ProfileEntityManager profileEntityManagerMock; - - @Mock - private ProfileDao profileDaoMock; - - private IETFExchangeTokenGranter tokenGranter; - - @Before - public void before() { - MockitoAnnotations.initMocks(this); - - ClientDetailsEntity lockedClient = new ClientDetailsEntity("LOCKED"); - ClientDetailsEntity deactivatedClient = new ClientDetailsEntity("DEACTIVATED"); - - when(activeClientMock.getId()).thenReturn(ACTIVE_CLIENT_ID); - when(activeClientMock.getClientId()).thenReturn(ACTIVE_CLIENT_ID); - when(activeClientMock.getAuthorizedGrantTypes()).thenReturn(Set.of(OrcidOauth2Constants.IETF_EXCHANGE_GRANT_TYPE)); - - // For sake of testing, ACTIVE and AUD_CLIENT could be the same mock - when(clientDetailsEntityCacheManagerMock.retrieve(ACTIVE_CLIENT_ID)).thenReturn(activeClientMock); - when(clientDetailsEntityCacheManagerMock.retrieve(AUDIENCE_CLIENT_ID)).thenReturn(activeClientMock); - - when(clientDetailsEntityCacheManagerMock.retrieve("LOCKED")).thenReturn(lockedClient); - when(clientDetailsEntityCacheManagerMock.retrieve("DEACTIVATED")).thenReturn(deactivatedClient); - - doThrow(LockedException.class).when(orcidOAuth2RequestValidatorMock).validateClientIsEnabled(eq(lockedClient)); - doThrow(ClientDeactivatedException.class).when(orcidOAuth2RequestValidatorMock).validateClientIsEnabled(eq(deactivatedClient)); - - when(openIDConnectKeyServiceMock.verify(any())).thenReturn(true); - - ClientDetailsEntity oboClient = new ClientDetailsEntity(AUDIENCE_CLIENT_ID); - MemberOBOWhitelistedClientEntity e = new MemberOBOWhitelistedClientEntity(); - e.setWhitelistedClientDetailsEntity(oboClient); - - List oboClients = List.of(e); - - when(memberOBOWhitelistedClientDaoMock.getWhitelistForClient(ACTIVE_CLIENT_ID)).thenReturn(oboClients); - - OrcidOauth2TokenDetail token1 = getOrcidOauth2TokenDetail(true, "/read-limited", System.currentTimeMillis() + 60000, false); - - when(orcidOauthTokenDetailServiceMock.findByClientIdAndUserName(any(), any())).thenReturn(List.of(token1)); - - ProfileEntity profile = new ProfileEntity(ORCID); - when(profileEntityManagerMock.findByOrcid(eq(ORCID))).thenReturn(profile); - - OrcidGrantedAuthority oga = new OrcidGrantedAuthority(); - oga.setAuthority("ROLE_USER"); - oga.setOrcid(ORCID); - when(profileDaoMock.getGrantedAuthoritiesForProfile(eq(ORCID))).thenReturn(List.of(oga)); - - // Active token - DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(""); - token.setAdditionalInformation(new HashMap()); - token.setExpiration(new Date(System.currentTimeMillis() + 60000)); - token.setScope(Set.of()); - token.setTokenType(""); - token.setValue(""); - - when(tokenServicesMock.createAccessToken(any())).thenReturn(token); - - // Disabled token - DefaultOAuth2AccessToken disabledToken = new DefaultOAuth2AccessToken(""); - disabledToken.setAdditionalInformation(new HashMap()); - disabledToken.setExpiration(new Date(System.currentTimeMillis() + 60000)); - disabledToken.setScope(Set.of()); - disabledToken.setTokenType(""); - disabledToken.setValue(""); - - when(tokenServicesMock.createRevokedAccessToken(any(), any())).thenReturn(disabledToken); - - tokenGranter = new IETFExchangeTokenGranter(tokenServicesMock); - - TargetProxyHelper.injectIntoProxy(tokenGranter, "clientDetailsEntityCacheManager", clientDetailsEntityCacheManagerMock); - TargetProxyHelper.injectIntoProxy(tokenGranter, "orcidOAuth2RequestValidator", orcidOAuth2RequestValidatorMock); - TargetProxyHelper.injectIntoProxy(tokenGranter, "openIDConnectKeyService", openIDConnectKeyServiceMock); - TargetProxyHelper.injectIntoProxy(tokenGranter, "memberOBOWhitelistedClientDao", memberOBOWhitelistedClientDaoMock); - TargetProxyHelper.injectIntoProxy(tokenGranter, "orcidOauthTokenDetailService", orcidOauthTokenDetailServiceMock); - TargetProxyHelper.injectIntoProxy(tokenGranter, "profileEntityManager", profileEntityManagerMock); - TargetProxyHelper.injectIntoProxy(tokenGranter, "profileDao", profileDaoMock); - } - - @Test(expected = ClientDeactivatedException.class) - public void grantDeactivatedClientTest() throws NoSuchAlgorithmException, IOException, ParseException, URISyntaxException, JOSEException { - tokenGranter.grant(GRANT_TYPE, getTokenRequest("DEACTIVATED", List.of("/read-limited"))); - } - - @Test(expected = LockedException.class) - public void grantLockedClientTest() throws NoSuchAlgorithmException, IOException, ParseException, URISyntaxException, JOSEException { - tokenGranter.grant(GRANT_TYPE, getTokenRequest("LOCKED", List.of("/read-limited"))); - } - - @Test - public void grantMissingRequestParamsTest() { - String clientId = ACTIVE_CLIENT_ID; - List scope = List.of("/read-limited"); - - try { - // Missing token - Map requestParameters = new HashMap(); - requestParameters.put(OrcidOauth2Constants.IETF_EXCHANGE_SUBJECT_TOKEN_TYPE, "urn:ietf:params:oauth:token-type:id_token"); - requestParameters.put(OrcidOauth2Constants.IETF_EXCHANGE_REQUESTED_TOKEN_TYPE, "urn:ietf:params:oauth:token-type:access_token"); - TokenRequest t1 = new TokenRequest(requestParameters, clientId, scope, GRANT_TYPE); - tokenGranter.grant(GRANT_TYPE, t1); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("Missing IETF Token exchange request parameter(s). Required: subject_token subject_token_type requested_token_type", iae.getMessage()); - } catch (Exception e) { - fail(); - } - - try { - // Missing subject token type - Map requestParameters = new HashMap(); - requestParameters.put(OrcidOauth2Constants.IETF_EXCHANGE_SUBJECT_TOKEN, buildJWTToken(false)); - requestParameters.put(OrcidOauth2Constants.IETF_EXCHANGE_REQUESTED_TOKEN_TYPE, "urn:ietf:params:oauth:token-type:access_token"); - TokenRequest t2 = new TokenRequest(requestParameters, clientId, scope, GRANT_TYPE); - tokenGranter.grant(GRANT_TYPE, t2); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("Missing IETF Token exchange request parameter(s). Required: subject_token subject_token_type requested_token_type", iae.getMessage()); - } catch (Exception e) { - fail(); - } - - try { - // Missing requested token type - Map requestParameters = new HashMap(); - requestParameters.put(OrcidOauth2Constants.IETF_EXCHANGE_SUBJECT_TOKEN, buildJWTToken(false)); - requestParameters.put(OrcidOauth2Constants.IETF_EXCHANGE_SUBJECT_TOKEN_TYPE, "urn:ietf:params:oauth:token-type:id_token"); - TokenRequest t3 = new TokenRequest(requestParameters, clientId, scope, GRANT_TYPE); - tokenGranter.grant(GRANT_TYPE, t3); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("Missing IETF Token exchange request parameter(s). Required: subject_token subject_token_type requested_token_type", iae.getMessage()); - } catch (Exception e) { - fail(); - } - } - - @Test - public void grantClientDoesntHaveRequiredGrantTest() { - // Remove the required grant type - when(activeClientMock.getAuthorizedGrantTypes()).thenReturn(Set.of(OrcidOauth2Constants.GRANT_TYPE_CLIENT_CREDENTIALS)); - try { - tokenGranter.grant(GRANT_TYPE, getTokenRequest(ACTIVE_CLIENT_ID, List.of("/read-limited"))); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("Client does not have urn:ietf:params:oauth:grant-type:token-exchange enabled", iae.getMessage()); - } catch (Exception e) { - fail(); - } - } - - @Test - public void grantClientNotOboWhitelistedTest() { - try { - ClientDetailsEntity oboClient = new ClientDetailsEntity("OTHER_CLIENT"); - MemberOBOWhitelistedClientEntity e = new MemberOBOWhitelistedClientEntity(); - e.setWhitelistedClientDetailsEntity(oboClient); - when(memberOBOWhitelistedClientDaoMock.getWhitelistForClient(AUDIENCE_CLIENT_ID)).thenReturn(List.of(e)); - tokenGranter.grant(GRANT_TYPE, getTokenRequest(ACTIVE_CLIENT_ID, List.of("/read-limited"))); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("Client ACTIVE cannot act on behalf of client AUD_CLIENT", iae.getMessage()); - } catch (Exception e) { - fail(); - } - } - - @Test - public void grantRequestingAndOBOClientMustNotBeTheSameClientTest() throws JOSEException, NoSuchAlgorithmException, IOException, ParseException, URISyntaxException { - Builder claims = new JWTClaimsSet.Builder(); - claims.claim("aud", AUDIENCE_CLIENT_ID); - claims.claim("sub", ORCID); - claims.expirationTime(new Date(System.currentTimeMillis() + 60000)); - - OpenIDConnectKeyService.OpenIDConnectKeyServiceConfig config = new OpenIDConnectKeyService.OpenIDConnectKeyServiceConfig(); - config.setKeyName("OpenIDTestKey1"); - config.setJsonKey( - "{\"keys\":[{\"kty\":\"RSA\",\"d\":\"i6C2Vdr7HDMj9wOBx28epQ7KPpzU_RDfGmQF8c81MoQU2KkpuNcFD49Rixzp3nQa58vtCOzAKeHwglpqm4elcai-uTW0bcdW1DOqYbwzQEk7pVQF-mMEUC-Rvd3Y5SIhCrHQYHGq9Q58uyuolG-Exq4h1AgyhUBX3CETCqzhPshOmB_Y4OuasdhyuVNySBbo-ZOYSd-HMrsrv1lt5WckWz22wmsREjO5AoRPpF17UVp3nMRCTy2v1acUrNtG64MdaFUpmLt9a-RqseFErE2Tm-kEUSBjYucswQ0_ZIs_VUdPWet4twqulB2bJi2ET6pP25DufOtR0x3ijvEPAfvhwQ\",\"e\":\"AQAB\",\"use\":\"sig\",\"kid\":\"OpenIDTestKey1\",\"alg\":\"RS256\",\"n\":\"qCtxWP2HppC8PBEXUh6b5RPECAzQS01khDwbxCSndO-YtS1MYpNlmtUgdtoAEoIP9TFMqXOsltKmGFioy0CeWLi53M-iX-Ygjd3zSQAbr0BU0-86somdbIlFxuvGA8v6AC7MNlICTwbGExCufL_hivrzF1XVqi5zIovM1LA8k2bP4BKMEjNwhGBGJ0E9KcQYv65foZr9K0C6YYJDFE6YqsHP_czvbI1ij7MfDvN5cwmHRGMGOyzDCmT_SmjoZAZ4vSXbl2wI5txIj70RLLSK4oahktb-09c0lDVYpCno7LqsLR8E3DuTUniYwYMHlXeBor_G7sJw2alF568m1iZ_zQ\"}]}"); - OpenIDConnectKeyService service = new OpenIDConnectKeyService(config); - - String token = service.sign(claims.build()).serialize(); - - Map requestParameters = new HashMap(); - requestParameters.put(OrcidOauth2Constants.IETF_EXCHANGE_SUBJECT_TOKEN, token); - requestParameters.put(OrcidOauth2Constants.IETF_EXCHANGE_SUBJECT_TOKEN_TYPE, "urn:ietf:params:oauth:token-type:id_token"); - requestParameters.put(OrcidOauth2Constants.IETF_EXCHANGE_REQUESTED_TOKEN_TYPE, "urn:ietf:params:oauth:token-type:access_token"); - TokenRequest tokenRequest = new TokenRequest(requestParameters, AUDIENCE_CLIENT_ID, List.of("/read-limited"), GRANT_TYPE); - try { - tokenGranter.grant(GRANT_TYPE, tokenRequest); - fail(); - } catch (IllegalArgumentException iae) { - assertEquals("Attempt to exchange own id_token, use refresh token instead", iae.getMessage()); - } catch (Exception e) { - fail(); - } - } - - @Test - public void grantDisabledTokenDoesntWorkTest() throws NoSuchAlgorithmException, IOException, ParseException, URISyntaxException, JOSEException { - try { - OrcidOauth2TokenDetail token1 = getOrcidOauth2TokenDetail(true, "/read-limited", System.currentTimeMillis() + 60000, true); - token1.setRevokeReason(RevokeReason.USER_REVOKED.name()); - - when(orcidOauthTokenDetailServiceMock.findByClientIdAndUserName(any(), any())).thenReturn(List.of(token1)); - tokenGranter.grant(GRANT_TYPE, getTokenRequest(ACTIVE_CLIENT_ID, List.of("/read-limited"))); - fail(); - } catch (OrcidInvalidScopeException oise) { - assertEquals("The id_token is not associated with a valid scope", oise.getMessage()); - } catch (Exception e) { - fail(); - } - } - - @Test - public void grantUserDisabledTokenWithActivitiesReadLimitedGenerateDeactivatedTokenTest() - throws NoSuchAlgorithmException, IOException, ParseException, URISyntaxException, JOSEException { - OrcidOauth2TokenDetail token1 = getOrcidOauth2TokenDetail(true, "/activities/update", System.currentTimeMillis() + 60000, true); - token1.setRevokeReason(RevokeReason.USER_REVOKED.name()); - - when(orcidOauthTokenDetailServiceMock.findByClientIdAndUserName(any(), any())).thenReturn(List.of(token1)); - tokenGranter.grant(GRANT_TYPE, getTokenRequest(ACTIVE_CLIENT_ID, List.of("/activities/update"))); - // Verify revoke token was created - verify(tokenServicesMock, times(1)).createRevokedAccessToken(any(), eq(RevokeReason.USER_REVOKED)); - // Verify regular token was never created - verify(tokenServicesMock, never()).createAccessToken(any()); - } - - @Test - public void grantClientDisabledTokenWithActivitiesReadLimitedThrowExceptionTest() - throws NoSuchAlgorithmException, IOException, ParseException, URISyntaxException, JOSEException { - OrcidOauth2TokenDetail token1 = getOrcidOauth2TokenDetail(true, "/activities/update", System.currentTimeMillis() + 60000, true); - token1.setRevokeReason(RevokeReason.CLIENT_REVOKED.name()); - - when(orcidOauthTokenDetailServiceMock.findByClientIdAndUserName(any(), any())).thenReturn(List.of(token1)); - try { - tokenGranter.grant(GRANT_TYPE, getTokenRequest(ACTIVE_CLIENT_ID, List.of("/activities/update"))); - } catch(OrcidInvalidScopeException e) { - assertEquals("There are no active tokens with valid scopes for this account", e.getMessage()); - } catch(Exception e) { - fail("Unhandled exception:" + e.getMessage()); - } - } - - @Test - public void grantStaffDisabledTokenWithActivitiesReadLimitedThrowExceptionTest() - throws NoSuchAlgorithmException, IOException, ParseException, URISyntaxException, JOSEException { - OrcidOauth2TokenDetail token1 = getOrcidOauth2TokenDetail(true, "/activities/update", System.currentTimeMillis() + 60000, true); - token1.setRevokeReason(RevokeReason.STAFF_REVOKED.name()); - - when(orcidOauthTokenDetailServiceMock.findByClientIdAndUserName(any(), any())).thenReturn(List.of(token1)); - try { - tokenGranter.grant(GRANT_TYPE, getTokenRequest(ACTIVE_CLIENT_ID, List.of("/activities/update"))); - } catch(OrcidInvalidScopeException e) { - assertEquals("There are no active tokens with valid scopes for this account", e.getMessage()); - } catch(Exception e) { - fail("Unhandled exception:" + e.getMessage()); - } - } - - @Test - public void grantTokenWhenAMemberRevokedTokenIsPresentTest() - throws NoSuchAlgorithmException, IOException, ParseException, URISyntaxException, JOSEException { - OrcidOauth2TokenDetail token1 = getOrcidOauth2TokenDetail(true, "/activities/update", System.currentTimeMillis() + 60000, true); - OrcidOauth2TokenDetail token2 = getOrcidOauth2TokenDetail(true, "/activities/update", System.currentTimeMillis() + 60000, false); - token1.setRevokeReason(RevokeReason.STAFF_REVOKED.name()); - - when(orcidOauthTokenDetailServiceMock.findByClientIdAndUserName(any(), any())).thenReturn(List.of(token1, token2)); - tokenGranter.grant(GRANT_TYPE, getTokenRequest(ACTIVE_CLIENT_ID, List.of("/activities/update"))); - // Verify revoke token was never created - verify(tokenServicesMock, never()).createRevokedAccessToken(any(), any()); - // Verify regular token was created - verify(tokenServicesMock, times(1)).createAccessToken(any()); - } - - @Test - public void grantDisabledTokenWithActivitiesUpdateAndOtherActiveTokenWithOtherScopesGenerateDeactivatedTokenTest() - throws NoSuchAlgorithmException, IOException, ParseException, URISyntaxException, JOSEException { - // Deactivated token - OrcidOauth2TokenDetail token1 = getOrcidOauth2TokenDetail(true, "/activities/update", System.currentTimeMillis() + 60000, true); - token1.setRevokeReason(RevokeReason.USER_REVOKED.name()); - - // Active token with other scope - OrcidOauth2TokenDetail token2 = getOrcidOauth2TokenDetail(true, "/activities/read-limited /read-limited /read-public", System.currentTimeMillis() + 60000, false); - token2.setApproved(true); - token2.setScope("/activities/read-limited /read-limited /read-public"); - token2.setTokenExpiration(new Date(System.currentTimeMillis() + 60000)); - token2.setTokenDisabled(false); - - // Revoke token should be generated - when(orcidOauthTokenDetailServiceMock.findByClientIdAndUserName(any(), any())).thenReturn(List.of(token1, token2)); - tokenGranter.grant(GRANT_TYPE, getTokenRequest(ACTIVE_CLIENT_ID, List.of("/activities/update"))); - // Verify revoke token was created - verify(tokenServicesMock, times(1)).createRevokedAccessToken(any(), eq(RevokeReason.USER_REVOKED)); - // Verify regular token was never created - verify(tokenServicesMock, never()).createAccessToken(any()); - } - - @Test - public void grantDisabledTokenWithActivitiesUpdateAndOtherActiveTokenWithActivitiesUpdateScopesGenerateActiveTokenTest() - throws NoSuchAlgorithmException, IOException, ParseException, URISyntaxException, JOSEException { - // Deactivated token - OrcidOauth2TokenDetail token1 = getOrcidOauth2TokenDetail(true, "/activities/update", System.currentTimeMillis() + 60000, true); - token1.setRevokeReason(RevokeReason.USER_REVOKED.name()); - - // Active token with other scope - OrcidOauth2TokenDetail token2 = getOrcidOauth2TokenDetail(true, "/activities/read-limited /read-limited /activities/update /read-public", System.currentTimeMillis() + 60000, true); - token2.setTokenDisabled(false); - - // Revoke token should be generated - when(orcidOauthTokenDetailServiceMock.findByClientIdAndUserName(any(), any())).thenReturn(List.of(token1, token2)); - tokenGranter.grant(GRANT_TYPE, getTokenRequest(ACTIVE_CLIENT_ID, List.of("/activities/update"))); - // Verify revoke token was never created - verify(tokenServicesMock, never()).createRevokedAccessToken(any(), any()); - // Verify regular token was created - verify(tokenServicesMock, times(1)).createAccessToken(any()); - } - - @Test - public void grantTest() throws NoSuchAlgorithmException, IOException, ParseException, URISyntaxException, JOSEException { - tokenGranter.grant(GRANT_TYPE, getTokenRequest(ACTIVE_CLIENT_ID, List.of("/read-limited"))); - verify(tokenServicesMock, times(1)).createAccessToken(any()); - } - - private TokenRequest getTokenRequest(String clientId, List scope) - throws NoSuchAlgorithmException, IOException, ParseException, URISyntaxException, JOSEException { - Map requestParameters = new HashMap(); - requestParameters.put(OrcidOauth2Constants.IETF_EXCHANGE_SUBJECT_TOKEN, buildJWTToken(false)); - requestParameters.put(OrcidOauth2Constants.IETF_EXCHANGE_SUBJECT_TOKEN_TYPE, "urn:ietf:params:oauth:token-type:id_token"); - requestParameters.put(OrcidOauth2Constants.IETF_EXCHANGE_REQUESTED_TOKEN_TYPE, "urn:ietf:params:oauth:token-type:access_token"); - return new TokenRequest(requestParameters, clientId, scope, GRANT_TYPE); - } - - private String buildJWTToken(boolean expired) throws NoSuchAlgorithmException, IOException, ParseException, URISyntaxException, JOSEException { - Builder claims = new JWTClaimsSet.Builder(); - claims.claim("aud", AUDIENCE_CLIENT_ID); - claims.claim("sub", ORCID); - claims.expirationTime(expired ? new Date(0) : new Date(System.currentTimeMillis() + 60000)); - - OpenIDConnectKeyService.OpenIDConnectKeyServiceConfig config = new OpenIDConnectKeyService.OpenIDConnectKeyServiceConfig(); - config.setKeyName("OpenIDTestKey1"); - config.setJsonKey( - "{\"keys\":[{\"kty\":\"RSA\",\"d\":\"i6C2Vdr7HDMj9wOBx28epQ7KPpzU_RDfGmQF8c81MoQU2KkpuNcFD49Rixzp3nQa58vtCOzAKeHwglpqm4elcai-uTW0bcdW1DOqYbwzQEk7pVQF-mMEUC-Rvd3Y5SIhCrHQYHGq9Q58uyuolG-Exq4h1AgyhUBX3CETCqzhPshOmB_Y4OuasdhyuVNySBbo-ZOYSd-HMrsrv1lt5WckWz22wmsREjO5AoRPpF17UVp3nMRCTy2v1acUrNtG64MdaFUpmLt9a-RqseFErE2Tm-kEUSBjYucswQ0_ZIs_VUdPWet4twqulB2bJi2ET6pP25DufOtR0x3ijvEPAfvhwQ\",\"e\":\"AQAB\",\"use\":\"sig\",\"kid\":\"OpenIDTestKey1\",\"alg\":\"RS256\",\"n\":\"qCtxWP2HppC8PBEXUh6b5RPECAzQS01khDwbxCSndO-YtS1MYpNlmtUgdtoAEoIP9TFMqXOsltKmGFioy0CeWLi53M-iX-Ygjd3zSQAbr0BU0-86somdbIlFxuvGA8v6AC7MNlICTwbGExCufL_hivrzF1XVqi5zIovM1LA8k2bP4BKMEjNwhGBGJ0E9KcQYv65foZr9K0C6YYJDFE6YqsHP_czvbI1ij7MfDvN5cwmHRGMGOyzDCmT_SmjoZAZ4vSXbl2wI5txIj70RLLSK4oahktb-09c0lDVYpCno7LqsLR8E3DuTUniYwYMHlXeBor_G7sJw2alF568m1iZ_zQ\"}]}"); - OpenIDConnectKeyService service = new OpenIDConnectKeyService(config); - - return service.sign(claims.build()).serialize(); - } - - private OrcidOauth2TokenDetail getOrcidOauth2TokenDetail(Boolean approved, String scope, long expiration, Boolean disabled) { - OrcidOauth2TokenDetail token = new OrcidOauth2TokenDetail(); - token.setApproved(approved); - token.setScope(scope); - token.setTokenExpiration(new Date(expiration)); - token.setTokenDisabled(disabled); - return token; - } -} diff --git a/orcid-core/src/test/java/org/orcid/core/oauth/service/OrcidRandomValueTokenServicesTest.java b/orcid-core/src/test/java/org/orcid/core/oauth/service/OrcidRandomValueTokenServicesTest.java deleted file mode 100644 index faf48138bf0..00000000000 --- a/orcid-core/src/test/java/org/orcid/core/oauth/service/OrcidRandomValueTokenServicesTest.java +++ /dev/null @@ -1,464 +0,0 @@ -package org.orcid.core.oauth.service; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; - -import java.io.Serializable; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Collections; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; - -import javax.annotation.Resource; - -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.orcid.core.constants.OrcidOauth2Constants; -import org.orcid.core.constants.RevokeReason; -import org.orcid.core.manager.ClientDetailsManager; -import org.orcid.core.oauth.OrcidBearerTokenAuthentication; -import org.orcid.core.oauth.OrcidOauth2TokenDetailService; -import org.orcid.core.oauth.OrcidRandomValueTokenServices; -import org.orcid.core.togglz.Features; -import org.orcid.persistence.jpa.entities.ClientDetailsEntity; -import org.orcid.persistence.jpa.entities.OrcidOauth2TokenDetail; -import org.orcid.test.DBUnitTest; -import org.orcid.test.OrcidJUnit4ClassRunner; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.oauth2.common.OAuth2AccessToken; -import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; -import org.springframework.security.oauth2.common.util.OAuth2Utils; -import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.security.oauth2.provider.OAuth2Request; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.context.request.RequestAttributes; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; -import org.togglz.junit.TogglzRule; - -/** - * - * @author Will Simpson - */ -@RunWith(OrcidJUnit4ClassRunner.class) -@ContextConfiguration(locations = { "classpath:test-orcid-core-context.xml" }) -public class OrcidRandomValueTokenServicesTest extends DBUnitTest { - - @Resource - private OrcidRandomValueTokenServices tokenServices; - - @Resource - private OrcidOauth2TokenDetailService orcidOauthTokenDetailService; - - @Resource - private ClientDetailsManager clientDetailsManager; - - @Rule - public TogglzRule togglzRule = TogglzRule.allDisabled(Features.class); - - @BeforeClass - public static void initDBUnitData() throws Exception { - initDBUnitData(Arrays.asList("/data/SubjectEntityData.xml", "/data/SourceClientDetailsEntityData.xml", - "/data/ProfileEntityData.xml", "/data/ClientDetailsEntityData.xml", "/data/OrcidOauth2AuthorisationDetailsData.xml")); - } - - @AfterClass - public static void removeDBUnitData() throws Exception { - removeDBUnitData(Arrays.asList("/data/OrcidOauth2AuthorisationDetailsData.xml", "/data/ClientDetailsEntityData.xml", "/data/ProfileEntityData.xml", - "/data/SubjectEntityData.xml")); - } - - @Test - public void testCreateReadLimitedAccessToken() { - Date earliestExpiry = oneHoursTime(); - Map authorizationParameters = new HashMap<>(); - String clientId = "4444-4444-4444-4441"; - authorizationParameters.put(OAuth2Utils.CLIENT_ID, clientId); - authorizationParameters.put(OAuth2Utils.SCOPE, "/orcid-profile/read-limited"); - OAuth2Request request = new OAuth2Request(Collections. emptyMap(), clientId, Collections. emptyList(), true, new HashSet(Arrays.asList("/orcid-profile/read-limited")), Collections. emptySet(), null, Collections. emptySet(), Collections. emptyMap()); - - ClientDetailsEntity clientDetails = clientDetailsManager.findByClientId(clientId); - Authentication userAuthentication = new OrcidBearerTokenAuthentication(clientDetails); - OAuth2Authentication authentication = new OAuth2Authentication(request, userAuthentication); - OAuth2AccessToken oauth2AccessToken = tokenServices.createAccessToken(authentication); - - Date latestExpiry = oneHoursTime(); - - assertNotNull(oauth2AccessToken); - assertFalse(oauth2AccessToken.getExpiration().before(earliestExpiry)); - assertFalse(oauth2AccessToken.getExpiration().after(latestExpiry)); - } - - @Test - public void testCreateAddWorkAccessToken() { - Date earliestExpiry = oneHoursTime(); - - Map authorizationParameters = new HashMap<>(); - String clientId = "4444-4444-4444-4441"; - authorizationParameters.put(OAuth2Utils.CLIENT_ID, clientId); - authorizationParameters.put(OAuth2Utils.SCOPE, "/orcid-works/create"); - OAuth2Request request = new OAuth2Request(Collections. emptyMap(), clientId, Collections. emptyList(), true, new HashSet(Arrays.asList("/orcid-profile/read-limited")), Collections. emptySet(), null, Collections. emptySet(), Collections. emptyMap()); - ClientDetailsEntity clientDetails = clientDetailsManager.findByClientId(clientId); - Authentication userAuthentication = new OrcidBearerTokenAuthentication(clientDetails); - OAuth2Authentication authentication = new OAuth2Authentication(request, userAuthentication); - OAuth2AccessToken oauth2AccessToken = tokenServices.createAccessToken(authentication); - - Date latestExpiry = oneHoursTime(); - - assertNotNull(oauth2AccessToken); - assertFalse(oauth2AccessToken.getExpiration().before(earliestExpiry)); - assertFalse(oauth2AccessToken.getExpiration().after(latestExpiry)); - } - - @Test - public void testReissuedAccessTokenHasUpdatedExpiration() throws InterruptedException { - Date earliestExpiry = oneHoursTime(); - - Map authorizationParameters = new HashMap<>(); - String clientId = "4444-4444-4444-4441"; - authorizationParameters.put(OAuth2Utils.CLIENT_ID, clientId); - authorizationParameters.put(OAuth2Utils.SCOPE, "/orcid-works/create"); - OAuth2Request request = new OAuth2Request(Collections. emptyMap(), clientId, Collections. emptyList(), true, new HashSet(Arrays.asList("/orcid-profile/read-limited")), Collections. emptySet(), null, Collections. emptySet(), Collections. emptyMap()); - ClientDetailsEntity clientDetails = clientDetailsManager.findByClientId(clientId); - Authentication userAuthentication = new OrcidBearerTokenAuthentication(clientDetails); - OAuth2Authentication authentication = new OAuth2Authentication(request, userAuthentication); - OAuth2AccessToken oauth2AccessToken = tokenServices.createAccessToken(authentication); - - Date latestExpiry = oneHoursTime(); - - assertNotNull(oauth2AccessToken); - assertFalse(oauth2AccessToken.getExpiration().before(earliestExpiry)); - assertFalse(oauth2AccessToken.getExpiration().after(latestExpiry)); - - Thread.sleep(1000); - earliestExpiry = oneHoursTime(); - - OAuth2AccessToken reissuedOauth2AccessToken = tokenServices.createAccessToken(authentication); - - latestExpiry = oneHoursTime(); - - assertNotNull(reissuedOauth2AccessToken); - - assertFalse(reissuedOauth2AccessToken.getExpiration().before(earliestExpiry)); - assertFalse(reissuedOauth2AccessToken.getExpiration().after(latestExpiry)); - } - - private Date twentyYearsTime() { - Calendar earliestExpiry = new GregorianCalendar(); - // This is roughly 2 years in seconds - used in the implementation, but - // not sure how was calculated now. - earliestExpiry.add(Calendar.SECOND, 631138519); - return earliestExpiry.getTime(); - } - - private Date oneHoursTime() { - Calendar earliestExpiry = new GregorianCalendar(); - earliestExpiry.add(Calendar.HOUR, 1); - return earliestExpiry.getTime(); - } - - @Test - public void invalidTokenThrowsInvalidTokenExceptionTest() { - // Mock request attributes - MockHttpServletRequest mockHttpServletRequest = new MockHttpServletRequest(); - RequestAttributes attrs = new ServletRequestAttributes(mockHttpServletRequest); - RequestContextHolder.setRequestAttributes(attrs); - String invalidTokenValue = "invalid"; - - mockHttpServletRequest.setMethod(RequestMethod.GET.name()); - catchInvalidTokenExceptionOnLoadAuthentication(invalidTokenValue, "Invalid access token: invalid"); - - mockHttpServletRequest.setMethod(RequestMethod.POST.name()); - catchInvalidTokenExceptionOnLoadAuthentication(invalidTokenValue, "Invalid access token: invalid"); - - mockHttpServletRequest.setMethod(RequestMethod.PUT.name()); - catchInvalidTokenExceptionOnLoadAuthentication(invalidTokenValue, "Invalid access token: invalid"); - - mockHttpServletRequest.setMethod(RequestMethod.DELETE.name()); - catchInvalidTokenExceptionOnLoadAuthentication(invalidTokenValue, "Invalid access token: invalid"); - } - - /** - * Check that the token created with a non persistent code will expire within an hour - * */ - @Test - public void tokenExpireInAnHourTest() throws InterruptedException { - Map authorizationParameters = new HashMap<>(); - String clientId = "4444-4444-4444-4441"; - authorizationParameters.put(OAuth2Utils.CLIENT_ID, clientId); - authorizationParameters.put(OAuth2Utils.SCOPE, "/orcid-works/create"); - authorizationParameters.put("code", "code2"); - - OAuth2Request request = new OAuth2Request(Collections. emptyMap(), clientId, Collections. emptyList(), true, new HashSet(Arrays.asList("/orcid-profile/read-limited")), Collections. emptySet(), null, Collections. emptySet(), Collections. emptyMap()); - ClientDetailsEntity clientDetails = clientDetailsManager.findByClientId(clientId); - Authentication userAuthentication = new OrcidBearerTokenAuthentication(clientDetails); - OAuth2Authentication authentication = new OAuth2Authentication(request, userAuthentication); - OAuth2AccessToken oauth2AccessToken = tokenServices.createAccessToken(authentication); - - Date tokenExpiration = oauth2AccessToken.getExpiration(); - Thread.sleep(2000); - - //The token expires in less than one hour - assertFalse(tokenExpiration.after(oneHoursTime())); - } - - /** - * Check that the token created with a persistent code will expire within 20 years - * */ - @Test - public void tokenExpireIn20YearsTest() throws InterruptedException { - Date in20years = twentyYearsTime(); - - Thread.sleep(2000); - - Map requestParameters = new HashMap<>(); - String clientId = "4444-4444-4444-4441"; - requestParameters.put(OAuth2Utils.CLIENT_ID, clientId); - requestParameters.put(OAuth2Utils.SCOPE, "/orcid-works/create"); - requestParameters.put("code", "code1"); - requestParameters.put(OrcidOauth2Constants.IS_PERSISTENT, "true"); - - OAuth2Request request = new OAuth2Request(requestParameters, clientId, Collections. emptyList(), true, new HashSet(Arrays.asList("/orcid-profile/read-limited")), Collections. emptySet(), null, Collections. emptySet(), Collections. emptyMap()); - ClientDetailsEntity clientDetails = clientDetailsManager.findByClientId(clientId); - Authentication userAuthentication = new OrcidBearerTokenAuthentication(clientDetails); - OAuth2Authentication authentication = new OAuth2Authentication(request, userAuthentication); - OAuth2AccessToken oauth2AccessToken = tokenServices.createAccessToken(authentication); - - - Date tokenExpiration = oauth2AccessToken.getExpiration(); - - //The token expires in 20 years - assertFalse(in20years.after(tokenExpiration)); - - in20years = twentyYearsTime(); - - //Confirm the token expires in 20 years - assertFalse(tokenExpiration.after(in20years)); - } - - @Test - public void expiredTokenDoesntWorkOnGetPostPutWithTogglzOnTest() { - // Mock request attributes - MockHttpServletRequest mockHttpServletRequest = new MockHttpServletRequest(); - RequestAttributes attrs = new ServletRequestAttributes(mockHttpServletRequest); - RequestContextHolder.setRequestAttributes(attrs); - - // Check GET requests fail - checkAuthenticationFailsOnExpiredTokenWithRequestMethod(mockHttpServletRequest, RequestMethod.GET); - // Check POST requests fail - checkAuthenticationFailsOnExpiredTokenWithRequestMethod(mockHttpServletRequest, RequestMethod.POST); - // Check PUT requests fail - checkAuthenticationFailsOnExpiredTokenWithRequestMethod(mockHttpServletRequest, RequestMethod.PUT); - } - - private void checkAuthenticationFailsOnExpiredTokenWithRequestMethod(MockHttpServletRequest mockHttpServletRequest, RequestMethod rm) { - mockHttpServletRequest.setMethod(rm.name()); - - OrcidOauth2TokenDetail expiredToken = buildExpiredToken("token-value-" + rm.name()); - expiredToken = buildExpiredToken("token-value-2-" + rm.name()); - orcidOauthTokenDetailService.createNew(expiredToken); - - // The first time we try to use it, we get a InvalidTokenException with message Access token expired: token-value - catchInvalidTokenExceptionOnLoadAuthentication("token-value-2-" + rm.name(), "Access token expired: token-value-2-" + rm.name()); - - // Second time we try to use it, we get a InvalidTokenException with message Invalid access token: token-value - catchInvalidTokenExceptionOnLoadAuthentication("token-value-2-" + rm.name(), "Invalid access token: token-value-2-" + rm.name()); - } - - @Test - public void disabedTokenDoesntWorkOnGetPostPutTest() { - // Mock request attributes - MockHttpServletRequest mockHttpServletRequest = new MockHttpServletRequest(); - RequestAttributes attrs = new ServletRequestAttributes(mockHttpServletRequest); - RequestContextHolder.setRequestAttributes(attrs); - - // All GET requests should fail, regardless of the RevokeReason - checkAuthenticationFailsOnDisabledTokenWithRequestMethod(mockHttpServletRequest, RequestMethod.GET, RevokeReason.AUTH_CODE_REUSED); - checkAuthenticationFailsOnDisabledTokenWithRequestMethod(mockHttpServletRequest, RequestMethod.GET, RevokeReason.CLIENT_REVOKED); - checkAuthenticationFailsOnDisabledTokenWithRequestMethod(mockHttpServletRequest, RequestMethod.GET, RevokeReason.RECORD_DEACTIVATED); - checkAuthenticationFailsOnDisabledTokenWithRequestMethod(mockHttpServletRequest, RequestMethod.GET, RevokeReason.STAFF_REVOKED); - checkAuthenticationFailsOnDisabledTokenWithRequestMethod(mockHttpServletRequest, RequestMethod.GET, RevokeReason.USER_REVOKED); - - // All POST requests should fail, regardless of the RevokeReason - checkAuthenticationFailsOnDisabledTokenWithRequestMethod(mockHttpServletRequest, RequestMethod.POST, RevokeReason.AUTH_CODE_REUSED); - checkAuthenticationFailsOnDisabledTokenWithRequestMethod(mockHttpServletRequest, RequestMethod.POST, RevokeReason.CLIENT_REVOKED); - checkAuthenticationFailsOnDisabledTokenWithRequestMethod(mockHttpServletRequest, RequestMethod.POST, RevokeReason.RECORD_DEACTIVATED); - checkAuthenticationFailsOnDisabledTokenWithRequestMethod(mockHttpServletRequest, RequestMethod.POST, RevokeReason.STAFF_REVOKED); - checkAuthenticationFailsOnDisabledTokenWithRequestMethod(mockHttpServletRequest, RequestMethod.POST, RevokeReason.USER_REVOKED); - - // All PUT requests should fail, regardless of the RevokeReason - checkAuthenticationFailsOnDisabledTokenWithRequestMethod(mockHttpServletRequest, RequestMethod.PUT, RevokeReason.AUTH_CODE_REUSED); - checkAuthenticationFailsOnDisabledTokenWithRequestMethod(mockHttpServletRequest, RequestMethod.PUT, RevokeReason.CLIENT_REVOKED); - checkAuthenticationFailsOnDisabledTokenWithRequestMethod(mockHttpServletRequest, RequestMethod.PUT, RevokeReason.RECORD_DEACTIVATED); - checkAuthenticationFailsOnDisabledTokenWithRequestMethod(mockHttpServletRequest, RequestMethod.PUT, RevokeReason.STAFF_REVOKED); - checkAuthenticationFailsOnDisabledTokenWithRequestMethod(mockHttpServletRequest, RequestMethod.PUT, RevokeReason.USER_REVOKED); - } - - private void checkAuthenticationFailsOnDisabledTokenWithRequestMethod(MockHttpServletRequest mockHttpServletRequest, RequestMethod rm, RevokeReason revokeReason) { - mockHttpServletRequest.setMethod(rm.name()); - - OrcidOauth2TokenDetail disabledToken = buildDisabledToken("token-value-" + rm.name() + revokeReason.name(), revokeReason); - disabledToken = buildDisabledToken("token-value-2-" + rm.name() + revokeReason.name(), revokeReason); - orcidOauthTokenDetailService.createNew(disabledToken); - - catchInvalidTokenExceptionOnLoadAuthentication("token-value-2-" + rm.name(), "Invalid access token: token-value-2-" + rm.name()); - } - - @Test - public void disabledTokenOnDeleteWithTogglzOnTest() { - // Mock request attributes - MockHttpServletRequest mockHttpServletRequest = new MockHttpServletRequest(); - RequestAttributes attrs = new ServletRequestAttributes(mockHttpServletRequest); - RequestContextHolder.setRequestAttributes(attrs); - mockHttpServletRequest.setMethod(RequestMethod.DELETE.name()); - - /////////////////////////////// - // Active tokens should work // - /////////////////////////////// - String activeTokenValue = "active-token-" + Math.random(); - OrcidOauth2TokenDetail activeToken = buildToken(activeTokenValue, "/activites-update"); - orcidOauthTokenDetailService.createNew(activeToken); - try { - tokenServices.loadAuthentication(activeTokenValue); - } catch(Exception e) { - fail(e.getMessage()); - } - - ////////////////////////////// - // USER_REVOKED should work // - ////////////////////////////// - String tokenValue = "token-value-" + RequestMethod.DELETE + RevokeReason.USER_REVOKED; - OrcidOauth2TokenDetail userRevokedDisabledToken = buildDisabledToken(tokenValue, RevokeReason.USER_REVOKED); - orcidOauthTokenDetailService.createNew(userRevokedDisabledToken); - - try { - tokenServices.loadAuthentication(tokenValue); - } catch(Exception e) { - fail(e.getMessage()); - } - - /////////////////////////// - // All other should fail // - /////////////////////////// - disabledTokensOnDeleteShouldAllwaysFailIfTheRevokeReasonIsNotUserRevoked(); - } - - private void disabledTokensOnDeleteShouldAllwaysFailIfTheRevokeReasonIsNotUserRevoked() { - // AUTH_CODE_REUSED should fail - String tokenValue = "token-value-" + Math.random() + "-" + RequestMethod.DELETE + RevokeReason.AUTH_CODE_REUSED; - OrcidOauth2TokenDetail disabledToken = buildDisabledToken(tokenValue, RevokeReason.AUTH_CODE_REUSED); - orcidOauthTokenDetailService.createNew(disabledToken); - - try { - tokenServices.loadAuthentication(tokenValue); - fail(); - } catch(InvalidTokenException e) { - assertEquals("Invalid access token: " + tokenValue + ", revoke reason: AUTH_CODE_REUSED", e.getMessage()); - } - - // CLIENT_REVOKED should fail - tokenValue = "token-value-" + Math.random() + "-" + RequestMethod.DELETE + RevokeReason.CLIENT_REVOKED; - disabledToken = buildDisabledToken(tokenValue, RevokeReason.CLIENT_REVOKED); - orcidOauthTokenDetailService.createNew(disabledToken); - - try { - tokenServices.loadAuthentication(tokenValue); - fail(); - } catch(InvalidTokenException e) { - assertEquals("Invalid access token: " + tokenValue + ", revoke reason: CLIENT_REVOKED", e.getMessage()); - } - - // RECORD_DEACTIVATED should fail - tokenValue = "token-value-" + Math.random() + "-" + RequestMethod.DELETE + RevokeReason.RECORD_DEACTIVATED; - disabledToken = buildDisabledToken(tokenValue, RevokeReason.RECORD_DEACTIVATED); - orcidOauthTokenDetailService.createNew(disabledToken); - - try { - tokenServices.loadAuthentication(tokenValue); - fail(); - } catch(InvalidTokenException e) { - assertEquals("Invalid access token: " + tokenValue + ", revoke reason: RECORD_DEACTIVATED", e.getMessage()); - } - - // STAFF_REVOKED should fail - tokenValue = "token-value-" + Math.random() + "-" + RequestMethod.DELETE + RevokeReason.STAFF_REVOKED; - disabledToken = buildDisabledToken(tokenValue, RevokeReason.STAFF_REVOKED); - orcidOauthTokenDetailService.createNew(disabledToken); - - try { - tokenServices.loadAuthentication(tokenValue); - fail(); - } catch(InvalidTokenException e) { - assertEquals("Invalid access token: " + tokenValue + ", revoke reason: STAFF_REVOKED", e.getMessage()); - } - } - - /** - * Load authentication using a persistent token - * */ - @Test - public void loadAuthenticationWithPersistentTokenTest() { - try { - OAuth2Authentication result = tokenServices.loadAuthentication("persistent-token-2"); - assertNotNull(result); - } catch(Exception e) { - fail(); - } - } - - private void catchInvalidTokenExceptionOnLoadAuthentication(String invalidTokenValue, String expectedMessage) { - try { - tokenServices.loadAuthentication(invalidTokenValue); - fail("Invalid access token must fail"); - } catch (InvalidTokenException i) { - assertEquals(expectedMessage, i.getMessage()); - } catch (Exception e) { - fail("Invalid exception found: " + e.getCause()); - } - } - - private OrcidOauth2TokenDetail buildExpiredToken(String tokenValue) { - return buildExpiredOrDisabledToken(tokenValue, true, null); - } - - private OrcidOauth2TokenDetail buildDisabledToken(String tokenValue, RevokeReason revokeReason) { - return buildExpiredOrDisabledToken(tokenValue, false, revokeReason); - } - - private OrcidOauth2TokenDetail buildExpiredOrDisabledToken(String tokenValue, Boolean buildExpired, RevokeReason revokeReason) { - OrcidOauth2TokenDetail token = buildToken(tokenValue, "/read-limited"); - if(buildExpired) { - token.setTokenExpiration(new Date(System.currentTimeMillis() - 60000)); - } else { - token.setTokenExpiration(new Date(System.currentTimeMillis() + 60000)); - token.setTokenDisabled(true); - token.setRevokeReason(revokeReason.name()); - token.setRevocationDate(new Date()); - } - return token; - } - - private OrcidOauth2TokenDetail buildToken(String tokenValue, String scope) { - OrcidOauth2TokenDetail token = new OrcidOauth2TokenDetail(); - token.setApproved(true); - token.setAuthenticationKey("authentication-key"); - token.setClientDetailsId("4444-4444-4444-4441"); - token.setOrcid("4444-4444-4444-4442"); - token.setResourceId("orcid"); - token.setScope(scope); - token.setTokenValue(tokenValue); - return token; - } -} From 383680cfb9d6ad0674148e6a519297767ca72bbf Mon Sep 17 00:00:00 2001 From: amontenegro Date: Wed, 25 Mar 2026 15:33:18 -0600 Subject: [PATCH 07/16] Notification managers fixe --- .../manager/impl/NotificationManagerImpl.java | 12 +++--- .../v3/impl/NotificationManagerImpl.java | 10 ++--- .../ClientDetailsManagerReadOnly.java | 1 + .../ClientDetailsManagerReadOnlyImpl.java | 41 +++++++++++++++---- .../impl/WorkManagerReadOnlyImpl.java | 3 -- 5 files changed, 45 insertions(+), 22 deletions(-) diff --git a/orcid-core/src/main/java/org/orcid/core/manager/impl/NotificationManagerImpl.java b/orcid-core/src/main/java/org/orcid/core/manager/impl/NotificationManagerImpl.java index cb03186855a..7c61b3aace1 100644 --- a/orcid-core/src/main/java/org/orcid/core/manager/impl/NotificationManagerImpl.java +++ b/orcid-core/src/main/java/org/orcid/core/manager/impl/NotificationManagerImpl.java @@ -30,8 +30,8 @@ import org.orcid.core.manager.SourceManager; import org.orcid.core.manager.read_only.EmailManagerReadOnly; import org.orcid.core.manager.read_only.impl.ManagerReadOnlyBaseImpl; +import org.orcid.core.manager.v3.read_only.ClientDetailsManagerReadOnly; import org.orcid.core.manager.v3.read_only.GivenPermissionToManagerReadOnly; -import org.orcid.core.oauth.OrcidOauth2TokenDetailService; import org.orcid.core.togglz.Features; import org.orcid.core.utils.SourceEntityUtils; import org.orcid.jaxb.model.clientgroup.RedirectUriType; @@ -46,7 +46,6 @@ import org.orcid.jaxb.model.notification_v2.Notification; import org.orcid.jaxb.model.notification_v2.NotificationType; import org.orcid.model.notification.institutional_sign_in_v2.NotificationInstitutionalConnection; -import org.orcid.persistence.dao.GenericDao; import org.orcid.persistence.dao.NotificationDao; import org.orcid.persistence.dao.ProfileDao; import org.orcid.persistence.dao.ProfileEventDao; @@ -56,7 +55,6 @@ import org.orcid.persistence.jpa.entities.NotificationEntity; import org.orcid.persistence.jpa.entities.NotificationInstitutionalConnectionEntity; import org.orcid.persistence.jpa.entities.ProfileEntity; -import org.orcid.persistence.jpa.entities.ProfileEventEntity; import org.orcid.persistence.jpa.entities.SourceEntity; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -101,9 +99,6 @@ public class NotificationManagerImpl extends ManagerReadOnlyBaseImpl implements @Resource private SourceManager sourceManager; - @Resource - private OrcidOauth2TokenDetailService orcidOauth2TokenDetailService; - @Resource private ClientDetailsEntityCacheManager clientDetailsEntityCacheManager; @@ -121,6 +116,9 @@ public class NotificationManagerImpl extends ManagerReadOnlyBaseImpl implements @Resource private SourceEntityUtils sourceEntityUtils; + + @Resource(name = "clientDetailsManagerReadOnlyV3") + private ClientDetailsManagerReadOnly clientDetailsManagerReadOnly; private static final Logger LOGGER = LoggerFactory.getLogger(NotificationManagerImpl.class); @@ -273,7 +271,7 @@ public List filterActionedNotificationAlerts(Collection { // Filter only INSTITUTIONAL_CONNECTION notifications if (NotificationType.INSTITUTIONAL_CONNECTION.equals(n.getNotificationType())) { - boolean alreadyConnected = orcidOauth2TokenDetailService.doesClientKnowUser(n.getSource().retrieveSourcePath(), userOrcid); + boolean alreadyConnected = clientDetailsManagerReadOnly.doesClientKnowUser(n.getSource().retrieveSourcePath(), userOrcid); if (alreadyConnected) { flagAsArchived(userOrcid, n.getPutCode(), false); } diff --git a/orcid-core/src/main/java/org/orcid/core/manager/v3/impl/NotificationManagerImpl.java b/orcid-core/src/main/java/org/orcid/core/manager/v3/impl/NotificationManagerImpl.java index f8c5c2e98ec..f469065dd23 100644 --- a/orcid-core/src/main/java/org/orcid/core/manager/v3/impl/NotificationManagerImpl.java +++ b/orcid-core/src/main/java/org/orcid/core/manager/v3/impl/NotificationManagerImpl.java @@ -16,9 +16,9 @@ import org.orcid.core.manager.v3.NotificationManager; import org.orcid.core.manager.v3.RecordNameManager; import org.orcid.core.manager.v3.SourceManager; +import org.orcid.core.manager.v3.read_only.ClientDetailsManagerReadOnly; import org.orcid.core.manager.v3.read_only.EmailManagerReadOnly; import org.orcid.core.manager.v3.read_only.impl.ManagerReadOnlyBaseImpl; -import org.orcid.core.oauth.OrcidOauth2TokenDetailService; import org.orcid.core.togglz.Features; import org.orcid.core.utils.ReleaseNameUtils; import org.orcid.core.utils.SourceEntityUtils; @@ -102,9 +102,6 @@ public class NotificationManagerImpl extends ManagerReadOnlyBaseImpl implements @Resource(name = "sourceManagerV3") private SourceManager sourceManager; - @Resource - private OrcidOauth2TokenDetailService orcidOauth2TokenDetailService; - @Resource private ClientDetailsEntityCacheManager clientDetailsEntityCacheManager; @@ -129,6 +126,9 @@ public class NotificationManagerImpl extends ManagerReadOnlyBaseImpl implements @Resource(name = "recordNameManagerV3") private RecordNameManager recordNameManagerV3; + @Resource(name = "clientDetailsManagerReadOnlyV3") + private ClientDetailsManagerReadOnly clientDetailsManagerReadOnly; + @Value("${org.orcid.notifications.auto.archive.days:181}") private Integer autoArchiveDays; @@ -543,7 +543,7 @@ public List filterActionedNotificationAlerts(Collection { // Filter only INSTITUTIONAL_CONNECTION notifications if (NotificationType.INSTITUTIONAL_CONNECTION.equals(n.getNotificationType())) { - boolean alreadyConnected = orcidOauth2TokenDetailService.doesClientKnowUser(n.getSource().retrieveSourcePath(), userOrcid); + boolean alreadyConnected = clientDetailsManagerReadOnly.doesClientKnowUser(n.getSource().retrieveSourcePath(), userOrcid); if (alreadyConnected) { flagAsArchived(userOrcid, n.getPutCode(), false); } diff --git a/orcid-core/src/main/java/org/orcid/core/manager/v3/read_only/ClientDetailsManagerReadOnly.java b/orcid-core/src/main/java/org/orcid/core/manager/v3/read_only/ClientDetailsManagerReadOnly.java index ba6ded14b2d..59efcea9ec9 100644 --- a/orcid-core/src/main/java/org/orcid/core/manager/v3/read_only/ClientDetailsManagerReadOnly.java +++ b/orcid-core/src/main/java/org/orcid/core/manager/v3/read_only/ClientDetailsManagerReadOnly.java @@ -43,4 +43,5 @@ public interface ClientDetailsManagerReadOnly { ClientSummary getClientSummary(String clientId); + boolean doesClientKnowUser(String clientId, String userOrcid); } diff --git a/orcid-core/src/main/java/org/orcid/core/manager/v3/read_only/impl/ClientDetailsManagerReadOnlyImpl.java b/orcid-core/src/main/java/org/orcid/core/manager/v3/read_only/impl/ClientDetailsManagerReadOnlyImpl.java index 2a03d5d22da..ee5a83f73c5 100644 --- a/orcid-core/src/main/java/org/orcid/core/manager/v3/read_only/impl/ClientDetailsManagerReadOnlyImpl.java +++ b/orcid-core/src/main/java/org/orcid/core/manager/v3/read_only/impl/ClientDetailsManagerReadOnlyImpl.java @@ -1,9 +1,6 @@ package org.orcid.core.manager.v3.read_only.impl; -import java.util.Date; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; import javax.annotation.Resource; import javax.persistence.NoResultException; @@ -11,23 +8,32 @@ import org.orcid.core.adapter.v3.JpaJaxbClientAdapter; import org.orcid.core.manager.EncryptionManager; import org.orcid.core.manager.v3.read_only.ClientDetailsManagerReadOnly; +import org.orcid.jaxb.model.message.ScopePathType; import org.orcid.jaxb.model.v3.release.client.ClientSummary; import org.orcid.persistence.dao.ClientDetailsDao; import org.orcid.persistence.dao.ClientRedirectDao; import org.orcid.persistence.dao.ClientSecretDao; +import org.orcid.persistence.dao.OrcidOauth2TokenDetailDao; import org.orcid.persistence.jpa.entities.ClientDetailsEntity; +import org.orcid.persistence.jpa.entities.OrcidOauth2TokenDetail; +import org.orcid.pojo.ajaxForm.PojoUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ClientDetailsManagerReadOnlyImpl implements ClientDetailsManagerReadOnly { - private static final Logger LOGGER = LoggerFactory.getLogger(ClientDetailsManagerReadOnlyImpl.class); - + private static final Logger LOGGER = LoggerFactory.getLogger(ClientDetailsManagerReadOnlyImpl.class); + + private final List CLIENT_KNOW_USER_SCOPES = Arrays.asList(ScopePathType.ACTIVITIES_UPDATE.value(), ScopePathType.AFFILIATIONS_CREATE.value(), ScopePathType.AFFILIATIONS_UPDATE.value()); + @Resource(name = "jpaJaxbClientAdapterV3") protected JpaJaxbClientAdapter jpaJaxbClientAdapter; @Resource - protected EncryptionManager encryptionManager; + protected EncryptionManager encryptionManager; + + @Resource(name="orcidOauth2TokenDetailDaoReadOnly") + private OrcidOauth2TokenDetailDao orcidOauth2TokenDetailDaoReadOnly; private ClientDetailsDao clientDetailsDao; @@ -174,4 +180,25 @@ public ClientSummary getClientSummary(String clientId) { return jpaJaxbClientAdapter.toClientSummary(clientDetailsEntity); } + @Override + public boolean doesClientKnowUser(String clientId, String userOrcid) { + List existingTokens = orcidOauth2TokenDetailDaoReadOnly.findByClientIdAndUserName(clientId, userOrcid); + if (existingTokens == null || existingTokens.isEmpty()) { + return false; + } + Date now = new Date(); + for (OrcidOauth2TokenDetail token : existingTokens) { + if (token.getTokenExpiration() != null && token.getTokenExpiration().after(now) && (token.getTokenDisabled() == null || !token.getTokenDisabled())) { + // Verify the token have at least one of the required scopes + if(!PojoUtil.isEmpty(token.getScope())) { + for(String scope : token.getScope().split(" ")) { + if(CLIENT_KNOW_USER_SCOPES.contains(scope.trim())) { + return true; + } + } + } + } + } + return false; + } } diff --git a/orcid-core/src/main/java/org/orcid/core/manager/v3/read_only/impl/WorkManagerReadOnlyImpl.java b/orcid-core/src/main/java/org/orcid/core/manager/v3/read_only/impl/WorkManagerReadOnlyImpl.java index 1e8e27389b3..dec2ec560a5 100644 --- a/orcid-core/src/main/java/org/orcid/core/manager/v3/read_only/impl/WorkManagerReadOnlyImpl.java +++ b/orcid-core/src/main/java/org/orcid/core/manager/v3/read_only/impl/WorkManagerReadOnlyImpl.java @@ -101,9 +101,6 @@ public class WorkManagerReadOnlyImpl extends ManagerReadOnlyBaseImpl implements @Resource private JSONWorkExternalIdentifiersConverterV3 jsonWorkExternalIdentifiersConverterV3; - @Resource(name = "clientDetailsManagerReadOnlyV3") - private ClientDetailsManagerReadOnly clientDetailsManagerReadOnly; - @Resource protected ClientDetailsEntityCacheManager clientDetailsEntityCacheManager; From 0927a0aaf06525db6de11e7cd5e8998f57b32bd5 Mon Sep 17 00:00:00 2001 From: amontenegro Date: Wed, 25 Mar 2026 15:45:25 -0600 Subject: [PATCH 08/16] Registration manager fixed --- .../manager/impl/RegistrationManagerImpl.java | 13 +++---- .../aop/OrcidApiVisibilitySecurityAspect.java | 39 ------------------- 2 files changed, 6 insertions(+), 46 deletions(-) delete mode 100644 orcid-core/src/main/java/org/orcid/core/security/visibility/aop/OrcidApiVisibilitySecurityAspect.java diff --git a/orcid-core/src/main/java/org/orcid/core/manager/impl/RegistrationManagerImpl.java b/orcid-core/src/main/java/org/orcid/core/manager/impl/RegistrationManagerImpl.java index 3759cb92210..af8aeb5cf9b 100644 --- a/orcid-core/src/main/java/org/orcid/core/manager/impl/RegistrationManagerImpl.java +++ b/orcid-core/src/main/java/org/orcid/core/manager/impl/RegistrationManagerImpl.java @@ -47,7 +47,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Required; -import org.springframework.security.oauth2.common.exceptions.InvalidRequestException; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionCallback; @@ -166,7 +165,7 @@ public String doInTransaction(TransactionStatus status) { if(PojoUtil.isEmpty(duplicateAdditionalAddress)){ duplicateAdditionalAddress = emailAddressAdditional; } else { - throw new InvalidRequestException("More than 2 duplicate emails"); + throw new IllegalArgumentException("More than 2 duplicate emails"); } } } @@ -220,7 +219,7 @@ public String doInTransaction(TransactionStatus status) { }); return orcidId; } catch (Exception e) { - throw new InvalidRequestException("Unable to register user due: " + e.getMessage(), e.getCause()); + throw new IllegalArgumentException("Unable to register user due: " + e.getMessage(), e.getCause()); } } @@ -336,7 +335,7 @@ private String createMinimalProfile(Registration registration, boolean usedCaptc * @param emailAddress * The email we want to check */ - private void checkAutoDeprecateIsEnabledForEmail(String emailAddress) throws InvalidRequestException { + private void checkAutoDeprecateIsEnabledForEmail(String emailAddress) { // If the email doesn't exists, just return if (!emailManager.emailExists(emailAddress)) { return; @@ -344,12 +343,12 @@ private void checkAutoDeprecateIsEnabledForEmail(String emailAddress) throws Inv // Check the record is not claimed if (profileEntityManager.isProfileClaimedByEmail(emailAddress)) { - throw new InvalidRequestException("Email " + emailAddress + " already exists and is claimed, so, it can't be used again"); + throw new IllegalArgumentException("Email " + emailAddress + " already exists and is claimed, so, it can't be used again"); } // Check the auto deprecate is enabled for this email address if (!emailManager.isAutoDeprecateEnableForEmail(emailAddress)) { - throw new InvalidRequestException("Autodeprecate is not enabled for " + emailAddress); + throw new IllegalArgumentException("Autodeprecate is not enabled for " + emailAddress); } } @@ -363,7 +362,7 @@ private String getOrcidIdFromEmail(String emailAddress) { Map emailMap = emailManager.findOricdIdsByCommaSeparatedEmails(emailAddress); String unclaimedOrcid = emailMap == null ? null : emailMap.get(emailAddress); if (PojoUtil.isEmpty(unclaimedOrcid)) { - throw new InvalidRequestException("Unable to find orcid id for " + emailAddress); + throw new IllegalArgumentException("Unable to find orcid id for " + emailAddress); } return unclaimedOrcid; } diff --git a/orcid-core/src/main/java/org/orcid/core/security/visibility/aop/OrcidApiVisibilitySecurityAspect.java b/orcid-core/src/main/java/org/orcid/core/security/visibility/aop/OrcidApiVisibilitySecurityAspect.java deleted file mode 100644 index 60b306e2925..00000000000 --- a/orcid-core/src/main/java/org/orcid/core/security/visibility/aop/OrcidApiVisibilitySecurityAspect.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.orcid.core.security.visibility.aop; - -import org.aspectj.lang.annotation.AfterReturning; -import org.aspectj.lang.annotation.Aspect; -import org.orcid.core.security.visibility.filter.VisibilityFilter; -import org.orcid.jaxb.model.message.OrcidMessage; -import org.orcid.jaxb.model.message.OrcidProfile; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; -import javax.ws.rs.core.Response; - -/** - * @author Declan Newman (declan) Date: 16/03/2012 - */ -@Aspect -@Component -public class OrcidApiVisibilitySecurityAspect { - - @Resource(name = "visibilityFilter") - private VisibilityFilter visibilityFilter; - - @AfterReturning(pointcut = "@annotation(visibilityAnnotation)", returning = "response") - public void simpleVisibilityResponseFilter(Response response, VisibilityControl visibilityAnnotation) { - Object entity = response != null ? response.getEntity() : null; - if (entity != null && OrcidMessage.class.isAssignableFrom(entity.getClass())) { - visibilityFilter.filter((OrcidMessage) entity, visibilityAnnotation.visibilities()); - } - } - - @AfterReturning(pointcut = "@annotation(visibilityAnnotation)", returning = "profile") - public void simpleVisibilityProfileFilter(OrcidProfile profile, VisibilityControl visibilityAnnotation) { - if (profile != null) { - OrcidMessage message = new OrcidMessage(profile); - visibilityFilter.filter(message, visibilityAnnotation.visibilities()); - } - } - -} From d91efa6011234bdb4046d7caabe6d73a63bb33fa Mon Sep 17 00:00:00 2001 From: amontenegro Date: Thu, 26 Mar 2026 10:23:06 -0600 Subject: [PATCH 09/16] More cleanup --- .../OrcidOAuth2AuthenticationEntryPoint.java | 86 ------------------ .../oauth/OrcidAPIAccessDeniedHandler.java | 34 ++++++++ .../oauth/OrcidOauth2TokenEndPointFilter.java | 87 ------------------- .../OrcidT1Oauth2TokenEndPointFilter.java | 71 --------------- .../OrcidWebOauth2TokenEndPointFilter.java | 78 ----------------- .../orcid-oauth2-api-common-config.xml | 2 + .../resources/orcid-api-security-context.xml | 56 ++---------- .../impl/InstitutionalSignInManagerImpl.java | 8 +- .../v3/impl/FindMyStuffManagerImpl.java | 10 +-- .../v3/impl/ProfileEntityManagerImpl.java | 39 +++++---- 10 files changed, 72 insertions(+), 399 deletions(-) delete mode 100644 orcid-api-common/src/main/java/org/orcid/api/common/exception/OrcidOAuth2AuthenticationEntryPoint.java create mode 100644 orcid-api-common/src/main/java/org/orcid/api/common/security/oauth/OrcidAPIAccessDeniedHandler.java delete mode 100644 orcid-api-common/src/main/java/org/orcid/api/common/security/oauth/OrcidOauth2TokenEndPointFilter.java delete mode 100644 orcid-api-common/src/main/java/org/orcid/api/common/security/oauth/OrcidT1Oauth2TokenEndPointFilter.java delete mode 100644 orcid-api-common/src/main/java/org/orcid/api/common/security/oauth/OrcidWebOauth2TokenEndPointFilter.java diff --git a/orcid-api-common/src/main/java/org/orcid/api/common/exception/OrcidOAuth2AuthenticationEntryPoint.java b/orcid-api-common/src/main/java/org/orcid/api/common/exception/OrcidOAuth2AuthenticationEntryPoint.java deleted file mode 100644 index 0fbf390a4da..00000000000 --- a/orcid-api-common/src/main/java/org/orcid/api/common/exception/OrcidOAuth2AuthenticationEntryPoint.java +++ /dev/null @@ -1,86 +0,0 @@ -package org.orcid.api.common.exception; - -import java.io.IOException; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.orcid.jaxb.model.error_v2.OrcidError; -import org.springframework.http.ResponseEntity; -import org.springframework.security.access.AccessDeniedException; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.oauth2.common.exceptions.OAuth2Exception; -import org.springframework.security.oauth2.provider.error.DefaultOAuth2ExceptionRenderer; -import org.springframework.security.oauth2.provider.error.DefaultWebResponseExceptionTranslator; -import org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint; -import org.springframework.security.oauth2.provider.error.OAuth2ExceptionRenderer; -import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator; -import org.springframework.security.web.AuthenticationEntryPoint; -import org.springframework.security.web.access.AccessDeniedHandler; -import org.springframework.web.context.request.ServletWebRequest; -import org.springframework.web.servlet.HandlerExceptionResolver; -import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver; - -/** - * - * @author Will Simpson - * - */ -public class OrcidOAuth2AuthenticationEntryPoint extends OAuth2AuthenticationEntryPoint implements AccessDeniedHandler, AuthenticationEntryPoint { - - private WebResponseExceptionTranslator exceptionTranslator = new DefaultWebResponseExceptionTranslator(); - - private OAuth2ExceptionRenderer exceptionRenderer = new DefaultOAuth2ExceptionRenderer(); - - // This is from Spring MVC. - private HandlerExceptionResolver handlerExceptionResolver = new DefaultHandlerExceptionResolver(); - - public void setExceptionTranslator(WebResponseExceptionTranslator exceptionTranslator) { - this.exceptionTranslator = exceptionTranslator; - } - - public void setExceptionRenderer(OAuth2ExceptionRenderer exceptionRenderer) { - this.exceptionRenderer = exceptionRenderer; - } - - @Override - public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { - handleAsOrcidError(request, response, accessDeniedException); - - } - - public void handleAsOrcidError(HttpServletRequest request, HttpServletResponse response, Exception authException) throws IOException, ServletException { - try { - ResponseEntity result = exceptionTranslator.translate(authException); - result = (ResponseEntity) enhanceResponse(result, authException); - OrcidError orcidError = new OrcidError(); - orcidError.setResponseCode(result.getStatusCode().value()); - orcidError.setDeveloperMessage(result.getBody().getLocalizedMessage()); - ResponseEntity errorResponseEntity = new ResponseEntity<>(orcidError, result.getHeaders(), result.getStatusCode()); - exceptionRenderer.handleHttpEntityResponse(errorResponseEntity, new ServletWebRequest(request, response)); - response.flushBuffer(); - } catch (ServletException e) { - // Re-use some of the default Spring dispatcher behaviour - the - // exception came from the filter chain and - // not from an MVC handler so it won't be caught by the dispatcher - // (even if there is one) - if (handlerExceptionResolver.resolveException(request, response, this, e) == null) { - throw e; - } - } catch (IOException e) { - throw e; - } catch (RuntimeException e) { - throw e; - } catch (Exception e) { - // Wrap other Exceptions. These are not expected to happen - throw new RuntimeException(e); - } - } - - @Override - public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { - handleAsOrcidError(request, response, authException); - } - -} \ No newline at end of file diff --git a/orcid-api-common/src/main/java/org/orcid/api/common/security/oauth/OrcidAPIAccessDeniedHandler.java b/orcid-api-common/src/main/java/org/orcid/api/common/security/oauth/OrcidAPIAccessDeniedHandler.java new file mode 100644 index 00000000000..fd73e4d0157 --- /dev/null +++ b/orcid-api-common/src/main/java/org/orcid/api/common/security/oauth/OrcidAPIAccessDeniedHandler.java @@ -0,0 +1,34 @@ +package org.orcid.api.common.security.oauth; + +import org.orcid.jaxb.model.v3.release.error.OrcidError; +import org.springframework.http.HttpStatus; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.web.access.AccessDeniedHandler; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +public class OrcidAPIAccessDeniedHandler implements AccessDeniedHandler { + @Override + public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { + try { + //TODO: This is NOT ready, exceptions should be handled and the proper error message returned + OrcidError orcidError = new OrcidError(); + orcidError.setResponseCode(response.getStatus()); + orcidError.setDeveloperMessage("GENERIC ERROR MESSAGE"); + + response.setStatus(HttpStatus.FORBIDDEN.value()); + response.getWriter().write(orcidError.toString()); + response.flushBuffer(); + } catch (IOException e) { + throw e; + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + // Wrap other Exceptions. These are not expected to happen + throw new RuntimeException(e); + } + } +} diff --git a/orcid-api-common/src/main/java/org/orcid/api/common/security/oauth/OrcidOauth2TokenEndPointFilter.java b/orcid-api-common/src/main/java/org/orcid/api/common/security/oauth/OrcidOauth2TokenEndPointFilter.java deleted file mode 100644 index 96be30f8bd8..00000000000 --- a/orcid-api-common/src/main/java/org/orcid/api/common/security/oauth/OrcidOauth2TokenEndPointFilter.java +++ /dev/null @@ -1,87 +0,0 @@ -package org.orcid.api.common.security.oauth; - -import java.io.IOException; - -import javax.annotation.Resource; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.orcid.core.locale.LocaleManager; -import org.orcid.core.security.MethodNotAllowedException; -import org.springframework.security.authentication.BadCredentialsException; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.oauth2.common.exceptions.InvalidRequestException; -import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter; -import org.springframework.web.bind.annotation.RequestMethod; - -/** - + - + - + This filter should be removed once the authorization server is inplace - + - + - + */ -@Deprecated(forRemoval = true) -public class OrcidOauth2TokenEndPointFilter extends ClientCredentialsTokenEndpointFilter { - - private final static String PUBLIC_ROLE = "ROLE_PUBLIC"; - - @Resource - private LocaleManager localeManager; - - private OrcidOauth2TokenEndPointFilter() { - super(); - } - - private OrcidOauth2TokenEndPointFilter(String path) { - super(path); - } - - @Override - public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { - if (request.getMethod().equals(RequestMethod.GET.name())) { - InvalidRequestException ire = new InvalidRequestException(localeManager.resolveMessage("apiError.token_request_callmethod.exception")); - throw new MethodNotAllowedException(localeManager.resolveMessage("apiError.token_request_callmethod.exception"), ire); - } - - String clientId = request.getParameter("client_id"); - String clientSecret = request.getParameter("client_secret"); - - // If the request is already authenticated we can assume that this - // filter is not needed - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - if (authentication != null && authentication.isAuthenticated()) { - return authentication; - } - - if (clientId == null) { - throw new BadCredentialsException(localeManager.resolveMessage("apiError.client_credentials.exception")); - } - - if (clientSecret == null) { - clientSecret = ""; - } - - clientId = clientId.trim(); - UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(clientId, "{noop}"+clientSecret); - - authentication = this.getAuthenticationManager().authenticate(authRequest); - - if (authentication != null) { - for (GrantedAuthority auth : authentication.getAuthorities()) { - if (PUBLIC_ROLE.equals(auth.getAuthority())) { - InvalidRequestException ire = new InvalidRequestException(localeManager.resolveMessage("apiError.memberapi_access.exception")); - throw new MethodNotAllowedException(localeManager.resolveMessage("apiError.memberapi_access.exception"), ire); - } - } - } - - return authentication; - } - -} diff --git a/orcid-api-common/src/main/java/org/orcid/api/common/security/oauth/OrcidT1Oauth2TokenEndPointFilter.java b/orcid-api-common/src/main/java/org/orcid/api/common/security/oauth/OrcidT1Oauth2TokenEndPointFilter.java deleted file mode 100644 index 8af19c8a7de..00000000000 --- a/orcid-api-common/src/main/java/org/orcid/api/common/security/oauth/OrcidT1Oauth2TokenEndPointFilter.java +++ /dev/null @@ -1,71 +0,0 @@ -package org.orcid.api.common.security.oauth; - -import java.io.IOException; - -import javax.annotation.Resource; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.orcid.core.locale.LocaleManager; -import org.orcid.core.security.MethodNotAllowedException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.security.authentication.BadCredentialsException; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.oauth2.common.exceptions.InvalidRequestException; -import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter; -import org.springframework.web.bind.annotation.RequestMethod; - -/** - * @author Angel Montenegro (amontenegro) Date: 27/03/2014 - */ -public class OrcidT1Oauth2TokenEndPointFilter extends ClientCredentialsTokenEndpointFilter { - - @Resource - private LocaleManager localeManager; - - private static final Logger LOGGER = LoggerFactory.getLogger(OrcidT1Oauth2TokenEndPointFilter.class); - - @Override - public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { - if (request.getMethod().equals(RequestMethod.GET.name())) { - InvalidRequestException ire = new InvalidRequestException(localeManager.resolveMessage("apiError.token_request_callmethod.exception")); - throw new MethodNotAllowedException(localeManager.resolveMessage("apiError.token_request_callmethod.exception"), ire); - } - - String clientId = request.getParameter("client_id"); - String clientSecret = request.getParameter("client_secret"); - - LOGGER.info("About to attempt authentication: clientId={}", clientId); - - // If the request is already authenticated we can assume that this - // filter is not needed - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - if (authentication != null && authentication.isAuthenticated()) { - LOGGER.info("Already got authentication in security context holder: principal={}, name={}", authentication.getPrincipal(), authentication.getName()); - return authentication; - } - - if (clientId == null) { - throw new BadCredentialsException(localeManager.resolveMessage("apiError.client_credentials.exception")); - } - - if (clientSecret == null) { - clientSecret = ""; - } - - clientId = clientId.trim(); - UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(clientId, "{noop}"+clientSecret); - - Authentication authenticationResult = this.getAuthenticationManager().authenticate(authRequest); - if (authenticationResult != null) { - LOGGER.info("Got authentication result: principal={}, name={}", authenticationResult.getPrincipal(), authenticationResult.getName()); - } - return authenticationResult; - } - -} \ No newline at end of file diff --git a/orcid-api-common/src/main/java/org/orcid/api/common/security/oauth/OrcidWebOauth2TokenEndPointFilter.java b/orcid-api-common/src/main/java/org/orcid/api/common/security/oauth/OrcidWebOauth2TokenEndPointFilter.java deleted file mode 100644 index feb2ec39ffd..00000000000 --- a/orcid-api-common/src/main/java/org/orcid/api/common/security/oauth/OrcidWebOauth2TokenEndPointFilter.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.orcid.api.common.security.oauth; - -import java.io.IOException; - -import javax.annotation.Resource; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.orcid.core.locale.LocaleManager; -import org.orcid.core.security.MethodNotAllowedException; -import org.orcid.core.togglz.Features; -import org.springframework.security.authentication.AnonymousAuthenticationToken; -import org.springframework.security.authentication.BadCredentialsException; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.oauth2.common.exceptions.InvalidRequestException; -import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter; -import org.springframework.web.bind.annotation.RequestMethod; - -/** - * @author Shobhit Tyagi - */ -@Deprecated(forRemoval = true) -public class OrcidWebOauth2TokenEndPointFilter extends ClientCredentialsTokenEndpointFilter { - - @Resource - private LocaleManager localeManager; - - private OrcidWebOauth2TokenEndPointFilter() { - super(); - } - - private OrcidWebOauth2TokenEndPointFilter(String path) { - super(path); - } - - @Override - public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { - /** - * - * - * This filter should be removed once the authorization server is inplace - * - * - * */ - if (request.getMethod().equals(RequestMethod.GET.name())) { - InvalidRequestException ire = new InvalidRequestException(localeManager.resolveMessage("apiError.token_request_callmethod.exception")); - throw new MethodNotAllowedException(localeManager.resolveMessage("apiError.token_request_callmethod.exception"), ire); - } - - String clientId = request.getParameter("client_id"); - String clientSecret = request.getParameter("client_secret"); - - // If the request is already authenticated we can assume that this - // filter is not needed - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - if (authentication != null && authentication.isAuthenticated()) { - return authentication; - } - - if (clientId == null) { - throw new BadCredentialsException(localeManager.resolveMessage("apiError.client_credentials.exception")); - } - - if (clientSecret == null) { - clientSecret = ""; - } - - clientId = clientId.trim(); - UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(clientId, "{noop}"+clientSecret); - authentication = this.getAuthenticationManager().authenticate(authRequest); - return authentication; - } - -} diff --git a/orcid-api-common/src/main/resources/orcid-oauth2-api-common-config.xml b/orcid-api-common/src/main/resources/orcid-oauth2-api-common-config.xml index 75d7bacafd6..83f001f8a76 100644 --- a/orcid-api-common/src/main/resources/orcid-oauth2-api-common-config.xml +++ b/orcid-api-common/src/main/resources/orcid-oauth2-api-common-config.xml @@ -55,4 +55,6 @@ + + \ No newline at end of file diff --git a/orcid-api-web/src/main/resources/orcid-api-security-context.xml b/orcid-api-web/src/main/resources/orcid-api-security-context.xml index 501ce70e75c..118e07a14ff 100644 --- a/orcid-api-web/src/main/resources/orcid-api-security-context.xml +++ b/orcid-api-web/src/main/resources/orcid-api-security-context.xml @@ -2,10 +2,8 @@ + http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-5.6.xsd"> @@ -29,14 +27,11 @@ - + - - - - - + + @@ -85,22 +80,6 @@ - - - - - - - - - - - - - - - - @@ -108,32 +87,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/orcid-core/src/main/java/org/orcid/core/manager/impl/InstitutionalSignInManagerImpl.java b/orcid-core/src/main/java/org/orcid/core/manager/impl/InstitutionalSignInManagerImpl.java index eedd84844d3..935ffeedbd7 100644 --- a/orcid-core/src/main/java/org/orcid/core/manager/impl/InstitutionalSignInManagerImpl.java +++ b/orcid-core/src/main/java/org/orcid/core/manager/impl/InstitutionalSignInManagerImpl.java @@ -18,7 +18,7 @@ import org.orcid.core.manager.ClientDetailsEntityCacheManager; import org.orcid.core.manager.InstitutionalSignInManager; import org.orcid.core.manager.NotificationManager; -import org.orcid.core.oauth.OrcidOauth2TokenDetailService; +import org.orcid.core.manager.v3.read_only.ClientDetailsManagerReadOnly; import org.orcid.core.utils.JsonUtils; import org.orcid.persistence.dao.UserConnectionDao; import org.orcid.persistence.jpa.entities.ClientDetailsEntity; @@ -51,8 +51,8 @@ public class InstitutionalSignInManagerImpl implements InstitutionalSignInManage @Resource protected ClientDetailsEntityCacheManager clientDetailsEntityCacheManager; - @Resource - protected OrcidOauth2TokenDetailService orcidOauth2TokenDetailService; + @Resource(name = "clientDetailsManagerReadOnlyV3") + private ClientDetailsManagerReadOnly clientDetailsManagerReadOnly; @Resource protected NotificationManager notificationManager; @@ -90,7 +90,7 @@ public void createUserConnectionAndNotify(String idType, String remoteUserId, St public void sendNotification(String userOrcid, String providerId) throws UnsupportedEncodingException { try { ClientDetailsEntity clientDetails = clientDetailsEntityCacheManager.retrieveByIdP(providerId); - boolean clientKnowsUser = orcidOauth2TokenDetailService.doesClientKnowUser(clientDetails.getClientId(), userOrcid); + boolean clientKnowsUser = clientDetailsManagerReadOnly.doesClientKnowUser(clientDetails.getClientId(), userOrcid); // If the client doesn't know about the user yet, send a // notification if (!clientKnowsUser) { diff --git a/orcid-core/src/main/java/org/orcid/core/manager/v3/impl/FindMyStuffManagerImpl.java b/orcid-core/src/main/java/org/orcid/core/manager/v3/impl/FindMyStuffManagerImpl.java index b377a551d1b..7dabe885e65 100644 --- a/orcid-core/src/main/java/org/orcid/core/manager/v3/impl/FindMyStuffManagerImpl.java +++ b/orcid-core/src/main/java/org/orcid/core/manager/v3/impl/FindMyStuffManagerImpl.java @@ -17,8 +17,8 @@ import org.orcid.core.manager.impl.OrcidUrlManager; import org.orcid.core.manager.v3.FindMyStuffManager; import org.orcid.core.manager.v3.NotificationManager; +import org.orcid.core.manager.v3.read_only.ClientDetailsManagerReadOnly; import org.orcid.core.manager.v3.read_only.WorkManagerReadOnly; -import org.orcid.core.oauth.OrcidOauth2TokenDetailService; import org.orcid.core.utils.v3.identifiers.finders.Finder; import org.orcid.jaxb.model.clientgroup.RedirectUriType; import org.orcid.jaxb.model.v3.release.notification.Notification; @@ -50,15 +50,15 @@ public class FindMyStuffManagerImpl implements FindMyStuffManager { @Resource private ClientDetailsEntityCacheManager clientDetailsEntityCacheManager; - @Resource - private OrcidOauth2TokenDetailService orcidOauth2TokenDetailService; - @Resource(name = "jpaJaxbNotificationAdapterV3") private JpaJaxbNotificationAdapter notificationAdapter; @Resource private FindMyStuffHistoryDao findMyStuffHistoryDao; + @Resource(name = "clientDetailsManagerReadOnlyV3") + private ClientDetailsManagerReadOnly clientDetailsManagerReadOnly; + @Resource List finders = new ArrayList(); @@ -111,7 +111,7 @@ public List findIfAppropriate(String orcid) { } // check for existing permissions for (Finder f : finders) { - if (f.isEnabled() && orcidOauth2TokenDetailService.doesClientKnowUser(f.getRelatedClientId(), orcid)) + if (f.isEnabled() && clientDetailsManagerReadOnly.doesClientKnowUser(f.getRelatedClientId(), orcid)) skipServices.add(f.getFinderName()); } diff --git a/orcid-core/src/main/java/org/orcid/core/manager/v3/impl/ProfileEntityManagerImpl.java b/orcid-core/src/main/java/org/orcid/core/manager/v3/impl/ProfileEntityManagerImpl.java index 1ed149a59c8..ef7abcf029d 100644 --- a/orcid-core/src/main/java/org/orcid/core/manager/v3/impl/ProfileEntityManagerImpl.java +++ b/orcid-core/src/main/java/org/orcid/core/manager/v3/impl/ProfileEntityManagerImpl.java @@ -22,8 +22,8 @@ import org.orcid.core.manager.v3.*; import org.orcid.core.manager.v3.read_only.RecordNameManagerReadOnly; import org.orcid.core.manager.v3.read_only.impl.ProfileEntityManagerReadOnlyImpl; -import org.orcid.core.oauth.OrcidOauth2TokenDetailService; import org.orcid.core.profile.history.ProfileHistoryEventType; +import org.orcid.core.utils.cache.redis.RedisClient; import org.orcid.jaxb.model.clientgroup.MemberType; import org.orcid.jaxb.model.common.AvailableLocales; import org.orcid.jaxb.model.message.ScopePathType; @@ -66,9 +66,6 @@ public class ProfileEntityManagerImpl extends ProfileEntityManagerReadOnlyImpl i @Resource(name = "peerReviewManagerV3") private PeerReviewManager peerReviewManager; - @Resource - private ProfileEntityCacheManager profileEntityCacheManager; - @Resource(name = "workManagerV3") private WorkManager workManager; @@ -105,15 +102,9 @@ public class ProfileEntityManagerImpl extends ProfileEntityManagerReadOnlyImpl i @Resource(name = "notificationManagerV3") private NotificationManager notificationManager; - @Resource - private OrcidOauth2TokenDetailService orcidOauth2TokenService; - @Resource private ClientDetailsEntityCacheManager clientDetailsEntityCacheManager; - @Resource - private OrcidUrlManager orcidUrlManager; - @Resource private LocaleManager localeManager; @@ -126,9 +117,6 @@ public class ProfileEntityManagerImpl extends ProfileEntityManagerReadOnlyImpl i @Resource private TransactionTemplate transactionTemplate; - @Resource - private OrcidOauth2TokenDetailService orcidOauth2TokenDetailService; - @Resource(name = "profileHistoryEventManagerV3") private ProfileHistoryEventManager profileHistoryEventManager; @@ -147,6 +135,15 @@ public class ProfileEntityManagerImpl extends ProfileEntityManagerReadOnlyImpl i @Resource private ProfileEmailDomainManager profileEmailDomainManager; + @Resource(name="orcidOauth2TokenDetailDaoReadOnly") + private OrcidOauth2TokenDetailDao orcidOauth2TokenDetailDaoReadOnly; + + @Resource(name="orcidOauth2TokenDetailDao") + private OrcidOauth2TokenDetailDao orcidOauth2TokenDetailDao; + + @Resource + private RedisClient redisClient; + @Override public boolean orcidExists(String orcid) { return profileDao.orcidExists(orcid); @@ -292,13 +289,21 @@ public boolean unreviewProfile(String orcid) { @Override public void disableClientAccess(String clientDetailsId, String userOrcid) { - orcidOauth2TokenService.disableClientAccess(clientDetailsId, userOrcid); + List userTokens = orcidOauth2TokenDetailDaoReadOnly.findByUserName(userOrcid); + if(userTokens != null && !userTokens.isEmpty()) { + for(OrcidOauth2TokenDetail token : userTokens) { + if(clientDetailsId.equals(token.getClientDetailsId())) { + redisClient.remove(token.getTokenValue()); + } + } + } + // And then disable all user tokens + orcidOauth2TokenDetailDao.disableClientAccessTokensByUserOrcid(userOrcid, clientDetailsId); } @Override public List getApplications(String orcid) { - // TODO: Use the authorization server to build this list of tokens - List tokenDetails = orcidOauth2TokenService.findByUserName(orcid); + List tokenDetails = orcidOauth2TokenDetailDaoReadOnly.findByUserName(orcid); Map distinctApplications = new HashMap<>(); for (OrcidOauth2TokenDetail token : tokenDetails) { if ((token.getTokenDisabled() == null || !token.getTokenDisabled()) && token.getOboClientDetailsId() == null) { @@ -649,7 +654,7 @@ private void clearRecord(String orcid, Boolean disableTokens) { if (disableTokens) { // Disable any token that belongs to this record - orcidOauth2TokenDetailService.disableAccessTokenByUserOrcid(orcid, RevokeReason.RECORD_DEACTIVATED); + orcidOauth2TokenDetailDao.disableAccessTokenByUserOrcid(orcid, RevokeReason.RECORD_DEACTIVATED.name()); } // Change default visibility to private From f0d354da0619f85ff9e0be8ed49983be4a31a294 Mon Sep 17 00:00:00 2001 From: amontenegro Date: Thu, 26 Mar 2026 17:00:10 -0600 Subject: [PATCH 10/16] More code refactoring and removing old dependencies --- orcid-api-common/pom.xml | 64 +- .../api/common/OrcidApiCommonEndpoints.java | 134 +- .../filter/OboApiVersionCheckFilter.java | 54 +- .../api/common/filter/TokenTargetFilter.java | 59 +- .../oauth/APIAuthenticationEntryPoint.java | 16 + .../orcid/api/common/writer/rdf/Geonames.java | 1529 +++++++++++++++++ .../org/orcid/api/common/writer/rdf/PAV.java | 273 +++ .../org/orcid/api/common/writer/rdf/PROV.java | 503 ++++++ .../orcid-oauth2-api-common-config.xml | 1 + .../NotificationsApiServiceDelegatorImpl.java | 9 +- .../NotificationsApiServiceDelegatorImpl.java | 9 +- .../resources/orcid-api-security-context.xml | 37 +- .../main/resources/orcid-api-web-context.xml | 1 - .../v3/impl/ProfileEntityManagerImpl.java | 1 + .../ProfileEntityManagerReadOnly.java | 2 + .../ProfileEntityManagerReadOnlyImpl.java | 15 +- .../oauth/OrcidBearerTokenAuthentication.java | 4 - ...rcidMultiSecretAuthenticationProvider.java | 51 + .../openid/OpenIDConnectDiscoveryService.java | 125 ++ .../oauth/openid/OpenIDConnectKeyService.java | 128 ++ .../oauth/openid/OpenIDConnectUserInfo.java | 67 + .../InstitutionalSignInManagerTest.java | 15 - .../core/manager/NotificationManagerTest.java | 9 - .../impl/ProfileEntityManagerImplTest.java | 4 - .../impl/RegistrationManagerImplTest.java | 27 +- .../manager/v3/FindMyStuffManagerTest.java | 12 - .../manager/v3/NotificationManagerTest.java | 6 - .../v3/impl/ProfileEntityManagerImplTest.java | 4 - .../orcid/core/oauth/OAuthErrorUtilsTest.java | 5 +- .../OrcidClientCredentialsCheckerTest.java | 115 -- .../OrcidRefreshTokenTokenGranterTest.java | 441 ----- .../OrcidOauthRedirectResolverTest.java | 245 --- .../OrcidAuthorizationCodeServiceTest.java | 127 -- .../OrcidOauth2TokenDetailServiceTest.java | 353 ---- .../service/OrcidTokenStoreServiceTest.java | 279 --- ...cidApiAuthorizationSecurityAspectTest.java | 129 -- .../core/utils/SecurityContextTestUtils.java | 89 + ...ClientCredentialEndPointDelegatorImpl.java | 78 - .../orcid-internal-api-security-context.xml | 18 +- .../orcid/api/filters/ApiRateLimitFilter.java | 17 +- .../util/AuthorizationRequestLocalCache.java | 59 - ...riginalAuthorizationRequestLocalCache.java | 59 - .../util/RequestInfoFormLocalCache.java | 58 - .../controllers/BaseWorkspaceController.java | 4 - .../controllers/FindMyStuffController.java | 4 - .../web/controllers/LoginController.java | 294 +--- .../controllers/OauthAuthorizeController.java | 337 ---- .../web/controllers/OauthControllerBase.java | 168 -- .../OauthGenericCallsController.java | 174 +- .../web/controllers/OauthLoginController.java | 216 --- .../OauthRegistrationController.java | 224 --- .../web/controllers/OpenIDController.java | 47 +- .../controllers/PublicProfileController.java | 8 +- .../controllers/RegistrationController.java | 20 - .../web/controllers/helper/OauthHelper.java | 263 --- .../OauthInvalidRequestException.java | 17 - .../OAuthAuthorizeNotSignedInFilter.java | 87 - .../web/util/ThirdPartyLinkManager.java | 10 +- .../resources/orcid-frontend-security.xml | 46 +- .../PublicProfileControllerTest.java | 23 +- .../OAuthAuthorizeNotSignedInFilterTest.java | 185 -- 61 files changed, 2998 insertions(+), 4360 deletions(-) create mode 100644 orcid-api-common/src/main/java/org/orcid/api/common/security/oauth/APIAuthenticationEntryPoint.java create mode 100644 orcid-api-common/src/main/java/org/orcid/api/common/writer/rdf/Geonames.java create mode 100644 orcid-api-common/src/main/java/org/orcid/api/common/writer/rdf/PAV.java create mode 100644 orcid-api-common/src/main/java/org/orcid/api/common/writer/rdf/PROV.java create mode 100644 orcid-core/src/main/java/org/orcid/core/oauth/OrcidMultiSecretAuthenticationProvider.java create mode 100644 orcid-core/src/main/java/org/orcid/core/oauth/openid/OpenIDConnectDiscoveryService.java create mode 100644 orcid-core/src/main/java/org/orcid/core/oauth/openid/OpenIDConnectKeyService.java create mode 100644 orcid-core/src/main/java/org/orcid/core/oauth/openid/OpenIDConnectUserInfo.java delete mode 100644 orcid-core/src/test/java/org/orcid/core/oauth/OrcidClientCredentialsCheckerTest.java delete mode 100644 orcid-core/src/test/java/org/orcid/core/oauth/OrcidRefreshTokenTokenGranterTest.java delete mode 100644 orcid-core/src/test/java/org/orcid/core/oauth/security/OrcidOauthRedirectResolverTest.java delete mode 100644 orcid-core/src/test/java/org/orcid/core/oauth/service/OrcidAuthorizationCodeServiceTest.java delete mode 100644 orcid-core/src/test/java/org/orcid/core/oauth/service/OrcidOauth2TokenDetailServiceTest.java delete mode 100644 orcid-core/src/test/java/org/orcid/core/oauth/service/OrcidTokenStoreServiceTest.java delete mode 100644 orcid-core/src/test/java/org/orcid/core/security/visibility/aop/OrcidApiAuthorizationSecurityAspectTest.java create mode 100644 orcid-core/src/test/java/org/orcid/core/utils/SecurityContextTestUtils.java delete mode 100644 orcid-internal-api/src/main/java/org/orcid/internal/server/delegator/impl/InternalClientCredentialEndPointDelegatorImpl.java delete mode 100644 orcid-web/src/main/java/org/orcid/frontend/util/AuthorizationRequestLocalCache.java delete mode 100644 orcid-web/src/main/java/org/orcid/frontend/util/OriginalAuthorizationRequestLocalCache.java delete mode 100644 orcid-web/src/main/java/org/orcid/frontend/util/RequestInfoFormLocalCache.java delete mode 100644 orcid-web/src/main/java/org/orcid/frontend/web/controllers/OauthAuthorizeController.java delete mode 100644 orcid-web/src/main/java/org/orcid/frontend/web/controllers/OauthControllerBase.java delete mode 100644 orcid-web/src/main/java/org/orcid/frontend/web/controllers/OauthLoginController.java delete mode 100644 orcid-web/src/main/java/org/orcid/frontend/web/controllers/OauthRegistrationController.java delete mode 100644 orcid-web/src/main/java/org/orcid/frontend/web/controllers/helper/OauthHelper.java delete mode 100644 orcid-web/src/main/java/org/orcid/frontend/web/exception/OauthInvalidRequestException.java delete mode 100644 orcid-web/src/main/java/org/orcid/frontend/web/filter/OAuthAuthorizeNotSignedInFilter.java delete mode 100644 orcid-web/src/test/java/org/orcid/frontend/web/filter/OAuthAuthorizeNotSignedInFilterTest.java diff --git a/orcid-api-common/pom.xml b/orcid-api-common/pom.xml index 5c1aee9582c..01c4a1504be 100644 --- a/orcid-api-common/pom.xml +++ b/orcid-api-common/pom.xml @@ -161,69 +161,7 @@ 11 - - - - org.codehaus.mojo - exec-maven-plugin - 3.1.0 - - - geonames - generate-sources - - java - - - jena.schemagen - - --inference \ - -i ${project.basedir}/src/main/vocabs/geonames_v3.1.rdf \ - -e RDF \ - --package org.orcid.api.common.writer.rdf.vocabs \ - -o ${jena.dir} \ - -n Geonames - - - - - pav - generate-sources - - java - - - jena.schemagen - - --inference \ - -i ${project.basedir}/src/main/vocabs/pav.rdf \ - -e RDF \ - --package org.orcid.api.common.writer.rdf.vocabs \ - -o ${jena.dir} \ - -n PAV - - - - - prov-o - generate-sources - - java - - - jena.schemagen - - --inference \ - -i ${project.basedir}/src/main/vocabs/prov-o.rdf \ - -e RDF \ - --package org.orcid.api.common.writer.rdf.vocabs \ - -o ${jena.dir} \ - -n PROV - - - - - + org.codehaus.mojo build-helper-maven-plugin diff --git a/orcid-api-common/src/main/java/org/orcid/api/common/OrcidApiCommonEndpoints.java b/orcid-api-common/src/main/java/org/orcid/api/common/OrcidApiCommonEndpoints.java index f6285a38fdd..f2b59e60c7b 100644 --- a/orcid-api-common/src/main/java/org/orcid/api/common/OrcidApiCommonEndpoints.java +++ b/orcid-api-common/src/main/java/org/orcid/api/common/OrcidApiCommonEndpoints.java @@ -13,19 +13,13 @@ import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.MultivaluedHashMap; -import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; - import org.apache.commons.lang.StringUtils; import org.orcid.core.oauth.authorizationServer.AuthorizationServerUtil; -import org.orcid.api.common.oauth.OrcidClientCredentialEndPointDelegator; import org.orcid.core.constants.OrcidOauth2Constants; import org.orcid.core.togglz.Features; -import org.springframework.security.oauth2.common.exceptions.UnsupportedGrantTypeException; import org.springframework.stereotype.Component; - import java.io.IOException; import java.net.URISyntaxException; @@ -35,9 +29,6 @@ public class OrcidApiCommonEndpoints { @Context private UriInfo uriInfo; - @Resource - private OrcidClientCredentialEndPointDelegator orcidClientCredentialEndPointDelegator; - @Resource private AuthorizationServerUtil authorizationServerUtil; @@ -55,98 +46,49 @@ public Response obtainOauth2TokenPost(@HeaderParam("Authorization") @DefaultValu // Token delegation is not implemented in the authorization server if(grantType == null) { - throw new UnsupportedGrantTypeException("grant_type is missing"); + throw new IllegalArgumentException("grant_type is missing"); } - if(Features.OAUTH_AUTHORIZATION_CODE_EXCHANGE.isActive()) { - Response response = null; - if(StringUtils.isNotBlank(authorization)) { - switch (grantType) { - case OrcidOauth2Constants.GRANT_TYPE_AUTHORIZATION_CODE: - response = authorizationServerUtil.forwardAuthorizationCodeExchangeRequest(authorization, redirectUri, code); - break; - case OrcidOauth2Constants.GRANT_TYPE_REFRESH_TOKEN: - response = authorizationServerUtil.forwardRefreshTokenRequest(authorization, refreshToken, scopeList); - break; - case OrcidOauth2Constants.GRANT_TYPE_CLIENT_CREDENTIALS: - response = authorizationServerUtil.forwardClientCredentialsRequest(authorization, scopeList); - break; - case IETF_EXCHANGE_GRANT_TYPE: - response = authorizationServerUtil.forwardTokenExchangeRequest(authorization, subjectToken, subjectTokenType, requestedTokenType, scopeList); - break; - default: - response = authorizationServerUtil.forwardOtherTokenExchangeRequest(authorization, grantType, code, scopeList); - break; - } - } else { - switch (grantType) { - case OrcidOauth2Constants.GRANT_TYPE_AUTHORIZATION_CODE: - response = authorizationServerUtil.forwardAuthorizationCodeExchangeRequest(clientId, clientSecret, redirectUri, code); - break; - case OrcidOauth2Constants.GRANT_TYPE_REFRESH_TOKEN: - response = authorizationServerUtil.forwardRefreshTokenRequest(clientId, clientSecret, refreshToken, scopeList); - break; - case OrcidOauth2Constants.GRANT_TYPE_CLIENT_CREDENTIALS: - response = authorizationServerUtil.forwardClientCredentialsRequest(clientId, clientSecret, scopeList); - break; - case IETF_EXCHANGE_GRANT_TYPE: - response = authorizationServerUtil.forwardTokenExchangeRequest(clientId, clientSecret, subjectToken, subjectTokenType, requestedTokenType, scopeList); - break; - default: - response = authorizationServerUtil.forwardOtherTokenExchangeRequest(clientId, clientSecret, grantType, code, scopeList); - break; - } + Response response = null; + if(StringUtils.isNotBlank(authorization)) { + switch (grantType) { + case OrcidOauth2Constants.GRANT_TYPE_AUTHORIZATION_CODE: + response = authorizationServerUtil.forwardAuthorizationCodeExchangeRequest(authorization, redirectUri, code); + break; + case OrcidOauth2Constants.GRANT_TYPE_REFRESH_TOKEN: + response = authorizationServerUtil.forwardRefreshTokenRequest(authorization, refreshToken, scopeList); + break; + case OrcidOauth2Constants.GRANT_TYPE_CLIENT_CREDENTIALS: + response = authorizationServerUtil.forwardClientCredentialsRequest(authorization, scopeList); + break; + case IETF_EXCHANGE_GRANT_TYPE: + response = authorizationServerUtil.forwardTokenExchangeRequest(authorization, subjectToken, subjectTokenType, requestedTokenType, scopeList); + break; + default: + response = authorizationServerUtil.forwardOtherTokenExchangeRequest(authorization, grantType, code, scopeList); + break; } - Object entity = response.getEntity(); - int statusCode = response.getStatus(); - return Response.status(statusCode).entity(entity).header(Features.OAUTH_AUTHORIZATION_CODE_EXCHANGE.name(),"ON").build(); } else { - MultivaluedMap formParams = new MultivaluedHashMap(); - if (clientId != null) { - formParams.add(OrcidOauth2Constants.CLIENT_ID_PARAM, clientId); - } - if (scopeList != null) { - formParams.add(OrcidOauth2Constants.SCOPE_PARAM, scopeList); - } - if (grantType != null) { - formParams.add(OrcidOauth2Constants.GRANT_TYPE, grantType); - } - - if (code != null) { - formParams.add("code", code); - } - - if (state != null) { - formParams.add(OrcidOauth2Constants.STATE_PARAM, state); - } - - if (redirectUri != null) { - formParams.add(OrcidOauth2Constants.REDIRECT_URI_PARAM, redirectUri); - } - - if (redirectUri != null) { - formParams.add(OrcidOauth2Constants.REDIRECT_URI_PARAM, redirectUri); + switch (grantType) { + case OrcidOauth2Constants.GRANT_TYPE_AUTHORIZATION_CODE: + response = authorizationServerUtil.forwardAuthorizationCodeExchangeRequest(clientId, clientSecret, redirectUri, code); + break; + case OrcidOauth2Constants.GRANT_TYPE_REFRESH_TOKEN: + response = authorizationServerUtil.forwardRefreshTokenRequest(clientId, clientSecret, refreshToken, scopeList); + break; + case OrcidOauth2Constants.GRANT_TYPE_CLIENT_CREDENTIALS: + response = authorizationServerUtil.forwardClientCredentialsRequest(clientId, clientSecret, scopeList); + break; + case IETF_EXCHANGE_GRANT_TYPE: + response = authorizationServerUtil.forwardTokenExchangeRequest(clientId, clientSecret, subjectToken, subjectTokenType, requestedTokenType, scopeList); + break; + default: + response = authorizationServerUtil.forwardOtherTokenExchangeRequest(clientId, clientSecret, grantType, code, scopeList); + break; } - - if (refreshToken != null) { - formParams.add(OrcidOauth2Constants.REFRESH_TOKEN, refreshToken); - } - - if (revokeOld != null) { - formParams.add(OrcidOauth2Constants.REVOKE_OLD, revokeOld); - } - // IETF Token exchange - if (subjectToken != null) { - formParams.add(OrcidOauth2Constants.IETF_EXCHANGE_SUBJECT_TOKEN, subjectToken); - } - if (subjectTokenType != null) { - formParams.add(OrcidOauth2Constants.IETF_EXCHANGE_SUBJECT_TOKEN_TYPE, subjectTokenType); - } - if (requestedTokenType != null) { - formParams.add(OrcidOauth2Constants.IETF_EXCHANGE_REQUESTED_TOKEN_TYPE, requestedTokenType); - } - - return orcidClientCredentialEndPointDelegator.obtainOauth2Token(authorization, formParams); } + Object entity = response.getEntity(); + int statusCode = response.getStatus(); + return Response.status(statusCode).entity(entity).header(Features.OAUTH_AUTHORIZATION_CODE_EXCHANGE.name(),"ON").build(); } } diff --git a/orcid-api-common/src/main/java/org/orcid/api/common/filter/OboApiVersionCheckFilter.java b/orcid-api-common/src/main/java/org/orcid/api/common/filter/OboApiVersionCheckFilter.java index faabc261264..51154f91cf6 100644 --- a/orcid-api-common/src/main/java/org/orcid/api/common/filter/OboApiVersionCheckFilter.java +++ b/orcid-api-common/src/main/java/org/orcid/api/common/filter/OboApiVersionCheckFilter.java @@ -1,7 +1,5 @@ package org.orcid.api.common.filter; -import java.util.HashMap; -import java.util.Map; import java.util.regex.Matcher; import javax.ws.rs.container.ContainerRequestContext; @@ -9,31 +7,20 @@ import javax.ws.rs.ext.Provider; import org.apache.commons.lang3.StringUtils; -import org.orcid.core.constants.OrcidOauth2Constants; import org.orcid.core.exception.OboNotValidForApiVersionException; -import org.orcid.core.oauth.OrcidOauth2TokenDetailService; -import org.orcid.core.oauth.OrcidOboOAuth2Authentication; -import org.orcid.core.togglz.Features; -import org.orcid.core.utils.JsonUtils; +import org.orcid.core.oauth.OrcidBearerTokenAuthentication; import org.orcid.core.utils.cache.redis.RedisClient; -import org.orcid.persistence.jpa.entities.OrcidOauth2TokenDetail; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails; import org.springframework.stereotype.Component; - @Component @Provider public class OboApiVersionCheckFilter implements ContainerRequestFilter { - @Autowired - private OrcidOauth2TokenDetailService orcidOauth2TokenService; - @Autowired private RedisClient redisClient; @@ -45,7 +32,7 @@ public void filter(ContainerRequestContext request) { String version = getApiVersion(request); boolean oboRequest = isOboRequest(); - if ((oboRequest && version.startsWith("2.")) || (oboRequest && version.startsWith("3.0_rc1"))) { + if (oboRequest && (version.startsWith("2.") || version.startsWith("3.0_rc1"))) { // OBO tokens can't be used pre v3.0_rc2 throw new OboNotValidForApiVersionException(); } @@ -57,29 +44,10 @@ private boolean isOboRequest() { SecurityContext context = SecurityContextHolder.getContext(); if (context != null && context.getAuthentication() != null) { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - // If the auth server token validation is on, then, the authentication type for OBO requests will be OrcidOboOAuth2Authentication - if(Features.OAUTH_TOKEN_VALIDATION.isActive()) { - if(OrcidOboOAuth2Authentication.class.isAssignableFrom(authentication.getClass())) { - OrcidOboOAuth2Authentication authDetails = (OrcidOboOAuth2Authentication) authentication; - if (StringUtils.isNotBlank(authDetails.getOboClientId())) { - return true; - } - } - } else if (OAuth2Authentication.class.isAssignableFrom(authentication.getClass())) { - OAuth2AuthenticationDetails authDetails = (OAuth2AuthenticationDetails) ((OAuth2Authentication) authentication).getDetails(); - if (authDetails != null && authDetails.getTokenValue() != null) { - Map cachedAccessToken = getTokenFromCache(authDetails.getTokenValue()); - if(cachedAccessToken != null) { - if(cachedAccessToken.containsKey(OrcidOauth2Constants.IS_OBO_TOKEN)) { - return true; - } - } else { - // Fallback to database if it is not in the cache - OrcidOauth2TokenDetail tokenDetail = orcidOauth2TokenService.findIgnoringDisabledByTokenValue(authDetails.getTokenValue()); - if(tokenDetail != null) { - return tokenDetail.getOboClientDetailsId() != null; - } - } + if(OrcidBearerTokenAuthentication.class.isAssignableFrom(authentication.getClass())) { + OrcidBearerTokenAuthentication authDetails = (OrcidBearerTokenAuthentication) authentication; + if (StringUtils.isNotBlank(authDetails.getOboClientId())) { + return true; } } } @@ -95,14 +63,4 @@ private String getApiVersion(ContainerRequestContext request) { return null; } - private Map getTokenFromCache(String accessTokenValue) { - if(isTokenCacheEnabled) { - String tokenJsonInfo = redisClient.get(accessTokenValue); - if(StringUtils.isNotBlank(tokenJsonInfo)) { - return JsonUtils.readObjectFromJsonString(tokenJsonInfo, HashMap.class); - } - } - return null; - } - } diff --git a/orcid-api-common/src/main/java/org/orcid/api/common/filter/TokenTargetFilter.java b/orcid-api-common/src/main/java/org/orcid/api/common/filter/TokenTargetFilter.java index dd367992c82..4954b775468 100644 --- a/orcid-api-common/src/main/java/org/orcid/api/common/filter/TokenTargetFilter.java +++ b/orcid-api-common/src/main/java/org/orcid/api/common/filter/TokenTargetFilter.java @@ -3,30 +3,23 @@ import java.security.AccessControlException; import java.util.regex.Matcher; -import javax.annotation.Resource; import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerRequestFilter; import javax.ws.rs.ext.Provider; import org.orcid.core.exception.OrcidUnauthorizedException; -import org.orcid.core.oauth.OrcidOauth2TokenDetailService; -import org.orcid.persistence.jpa.entities.ProfileEntity; +import org.orcid.core.oauth.OrcidBearerTokenAuthentication; import org.orcid.utils.OrcidStringUtils; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; -import org.glassfish.jersey.server.ContainerRequest; - +@Deprecated @Provider public class TokenTargetFilter implements ContainerRequestFilter { - @Resource - private OrcidOauth2TokenDetailService orcidOauth2TokenService; - @Override public void filter(ContainerRequestContext request) { Matcher m = OrcidStringUtils.orcidPattern.matcher(request.getUriInfo().getPath()); @@ -38,19 +31,47 @@ public void filter(ContainerRequestContext request) { private void validateTargetRecord(String targetOrcid, ContainerRequestContext request) { // Verify if it is the owner of the token + + //TODO: CAN WE DELETE THIS FILTER???? + + System.out.println("------------------------------------------------------------"); + System.out.println("------------------------------------------------------------"); + System.out.println("------------------------------------------------------------"); + System.out.println("------------------------------------------------------------"); + System.out.println("------------------------------------------------------------"); + System.out.println("------------------------------------------------------------"); + System.out.println("------------------------------------------------------------"); + System.out.println("------------------------------------------------------------"); + System.out.println("------------------------------------------------------------"); + System.out.println("------------------------------------------------------------"); + System.out.println("------------------------------------------------------------"); + System.out.println("------------------------------------------------------------"); + System.out.println("------------------------------------------------------------"); + + System.out.println("TokenTargetFilter"); + + System.out.println("------------------------------------------------------------"); + System.out.println("------------------------------------------------------------"); + System.out.println("------------------------------------------------------------"); + System.out.println("------------------------------------------------------------"); + System.out.println("------------------------------------------------------------"); + System.out.println("------------------------------------------------------------"); + System.out.println("------------------------------------------------------------"); + System.out.println("------------------------------------------------------------"); + System.out.println("------------------------------------------------------------"); + System.out.println("------------------------------------------------------------"); + System.out.println("------------------------------------------------------------"); + System.out.println("------------------------------------------------------------"); + System.out.println("------------------------------------------------------------"); + SecurityContext context = SecurityContextHolder.getContext(); if (context != null && context.getAuthentication() != null) { Authentication authentication = context.getAuthentication(); - if (OAuth2Authentication.class.isAssignableFrom(authentication.getClass())) { - OAuth2Authentication oauth2Auth = (OAuth2Authentication) authentication; - Authentication userAuthentication = oauth2Auth.getUserAuthentication(); - if (userAuthentication != null) { - Object principal = userAuthentication.getPrincipal(); - if (principal instanceof ProfileEntity) { - ProfileEntity tokenOwner = (ProfileEntity) principal; - if (!targetOrcid.equals(tokenOwner.getId())) { - throwException(); - } + if (OrcidBearerTokenAuthentication.class.isAssignableFrom(authentication.getClass())) { + OrcidBearerTokenAuthentication authDetails = (OrcidBearerTokenAuthentication) authentication; + if (authDetails != null) { + if (!targetOrcid.equals(authDetails.getUserOrcid())) { + throwException(); } } } diff --git a/orcid-api-common/src/main/java/org/orcid/api/common/security/oauth/APIAuthenticationEntryPoint.java b/orcid-api-common/src/main/java/org/orcid/api/common/security/oauth/APIAuthenticationEntryPoint.java new file mode 100644 index 00000000000..49516adba88 --- /dev/null +++ b/orcid-api-common/src/main/java/org/orcid/api/common/security/oauth/APIAuthenticationEntryPoint.java @@ -0,0 +1,16 @@ +package org.orcid.api.common.security.oauth; + +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +public class APIAuthenticationEntryPoint implements AuthenticationEntryPoint { + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { + + } +} diff --git a/orcid-api-common/src/main/java/org/orcid/api/common/writer/rdf/Geonames.java b/orcid-api-common/src/main/java/org/orcid/api/common/writer/rdf/Geonames.java new file mode 100644 index 00000000000..02206d456e3 --- /dev/null +++ b/orcid-api-common/src/main/java/org/orcid/api/common/writer/rdf/Geonames.java @@ -0,0 +1,1529 @@ +/* CVS $Id: $ */ +package org.orcid.api.common.writer.rdf.vocabs; +import org.apache.jena.rdf.model.*; + +/** + * Vocabulary definitions from C:\Users\angel\Documents\ORCID\development\tmp\deleteme\ORCID-Source\orcid-api-common/src/main/vocabs/geonames_v3.1.rdf + * @author Auto-generated by schemagen on 26 Mar 2026 15:47 + */ +public class Geonames { + /**

The RDF model that holds the vocabulary terms

*/ + private static final Model M_MODEL = ModelFactory.createDefaultModel(); + + /**

The namespace of the vocabulary as a string

*/ + public static final String NS = "http://www.geonames.org/ontology#"; + + /**

The namespace of the vocabulary as a string

+ * @return namespace as String + * @see #NS */ + public static String getURI() {return NS;} + + /**

The namespace of the vocabulary as a resource

*/ + public static final Resource NAMESPACE = M_MODEL.createResource( NS ); + + /**

The ontology's owl:versionInfo as a string

*/ + public static final String VERSION_INFO = "Version 3.1 - 2012-10-29"; + + public static final Property alternateName = M_MODEL.createProperty( "http://www.geonames.org/ontology#alternateName" ); + + /**

Links to an RDF document containing the descriptions of children features

*/ + public static final Property childrenFeatures = M_MODEL.createProperty( "http://www.geonames.org/ontology#childrenFeatures" ); + + public static final Property colloquialName = M_MODEL.createProperty( "http://www.geonames.org/ontology#colloquialName" ); + + /**

A two letters country code in the ISO 3166 listThe countryCode value for a + * Geoname Feature is equal to the countryCode value of the parentCountry value.

+ */ + public static final Property countryCode = M_MODEL.createProperty( "http://www.geonames.org/ontology#countryCode" ); + + /**

The main category of the feature, as defined in geonames taxonomy.

*/ + public static final Property featureClass = M_MODEL.createProperty( "http://www.geonames.org/ontology#featureClass" ); + + /**

Type of the feature, as defined in geonames taxonomy.

*/ + public static final Property featureCode = M_MODEL.createProperty( "http://www.geonames.org/ontology#featureCode" ); + + public static final Property geonamesID = M_MODEL.createProperty( "http://www.geonames.org/ontology#geonamesID" ); + + public static final Property historicalName = M_MODEL.createProperty( "http://www.geonames.org/ontology#historicalName" ); + + /**

Indicates that the subject resource is located in the object feature

*/ + public static final Property locatedIn = M_MODEL.createProperty( "http://www.geonames.org/ontology#locatedIn" ); + + /**

A geonames map centered on the feature.

*/ + public static final Property locationMap = M_MODEL.createProperty( "http://www.geonames.org/ontology#locationMap" ); + + /**

The main international name of a feature. The value has no xml:lang tag.

*/ + public static final Property name = M_MODEL.createProperty( "http://www.geonames.org/ontology#name" ); + + /**

A feature close to the reference feature

*/ + public static final Property nearby = M_MODEL.createProperty( "http://www.geonames.org/ontology#nearby" ); + + /**

Links to an RDF document containing the descriptions of nearby features

*/ + public static final Property nearbyFeatures = M_MODEL.createProperty( "http://www.geonames.org/ontology#nearbyFeatures" ); + + /**

A feature sharing a common boarder with the reference feature

*/ + public static final Property neighbour = M_MODEL.createProperty( "http://www.geonames.org/ontology#neighbour" ); + + /**

Links to an RDF document containing the descriptions of neighbouring features. + * Applies when the feature has definite boarders.

+ */ + public static final Property neighbouringFeatures = M_MODEL.createProperty( "http://www.geonames.org/ontology#neighbouringFeatures" ); + + /**

A name in an official local language

*/ + public static final Property officialName = M_MODEL.createProperty( "http://www.geonames.org/ontology#officialName" ); + + public static final Property parentADM1 = M_MODEL.createProperty( "http://www.geonames.org/ontology#parentADM1" ); + + public static final Property parentADM2 = M_MODEL.createProperty( "http://www.geonames.org/ontology#parentADM2" ); + + public static final Property parentADM3 = M_MODEL.createProperty( "http://www.geonames.org/ontology#parentADM3" ); + + public static final Property parentADM4 = M_MODEL.createProperty( "http://www.geonames.org/ontology#parentADM4" ); + + public static final Property parentCountry = M_MODEL.createProperty( "http://www.geonames.org/ontology#parentCountry" ); + + /**

A feature parent of the current one, in either administrative or physical + * subdivision.

+ */ + public static final Property parentFeature = M_MODEL.createProperty( "http://www.geonames.org/ontology#parentFeature" ); + + public static final Property population = M_MODEL.createProperty( "http://www.geonames.org/ontology#population" ); + + public static final Property postalCode = M_MODEL.createProperty( "http://www.geonames.org/ontology#postalCode" ); + + public static final Property shortName = M_MODEL.createProperty( "http://www.geonames.org/ontology#shortName" ); + + /**

A Wikipedia article of which subject is the resource.

*/ + public static final Property wikipediaArticle = M_MODEL.createProperty( "http://www.geonames.org/ontology#wikipediaArticle" ); + + /**

A class of features.

*/ + public static final Resource Class = M_MODEL.createResource( "http://www.geonames.org/ontology#Class" ); + + /**

A feature code.

*/ + public static final Resource Code = M_MODEL.createResource( "http://www.geonames.org/ontology#Code" ); + + /**

A geographical feature

*/ + public static final Resource Feature = M_MODEL.createResource( "http://www.geonames.org/ontology#Feature" ); + + /**

A feature described in geonames database, uniquely defined by its geonames + * identifier

+ */ + public static final Resource GeonamesFeature = M_MODEL.createResource( "http://www.geonames.org/ontology#GeonamesFeature" ); + + /**

A Web page displaying a map

*/ + public static final Resource Map = M_MODEL.createResource( "http://www.geonames.org/ontology#Map" ); + + /**

A Document containing RDF description of one or several features.

*/ + public static final Resource RDFData = M_MODEL.createResource( "http://www.geonames.org/ontology#RDFData" ); + + /**

A Wikipedia article

*/ + public static final Resource WikipediaArticle = M_MODEL.createResource( "http://www.geonames.org/ontology#WikipediaArticle" ); + + /**

country, state, region ...

*/ + public static final Resource A = M_MODEL.createResource( "http://www.geonames.org/ontology#A" ); + + public static final Resource A_ADM1 = M_MODEL.createResource( "http://www.geonames.org/ontology#A.ADM1" ); + + public static final Resource A_ADM1H = M_MODEL.createResource( "http://www.geonames.org/ontology#A.ADM1H" ); + + public static final Resource A_ADM2 = M_MODEL.createResource( "http://www.geonames.org/ontology#A.ADM2" ); + + public static final Resource A_ADM2H = M_MODEL.createResource( "http://www.geonames.org/ontology#A.ADM2H" ); + + public static final Resource A_ADM3 = M_MODEL.createResource( "http://www.geonames.org/ontology#A.ADM3" ); + + public static final Resource A_ADM3H = M_MODEL.createResource( "http://www.geonames.org/ontology#A.ADM3H" ); + + public static final Resource A_ADM4 = M_MODEL.createResource( "http://www.geonames.org/ontology#A.ADM4" ); + + public static final Resource A_ADM4H = M_MODEL.createResource( "http://www.geonames.org/ontology#A.ADM4H" ); + + public static final Resource A_ADM5 = M_MODEL.createResource( "http://www.geonames.org/ontology#A.ADM5" ); + + public static final Resource A_ADMD = M_MODEL.createResource( "http://www.geonames.org/ontology#A.ADMD" ); + + public static final Resource A_ADMH = M_MODEL.createResource( "http://www.geonames.org/ontology#A.ADMH" ); + + public static final Resource A_LTER = M_MODEL.createResource( "http://www.geonames.org/ontology#A.LTER" ); + + public static final Resource A_PCL = M_MODEL.createResource( "http://www.geonames.org/ontology#A.PCL" ); + + public static final Resource A_PCLD = M_MODEL.createResource( "http://www.geonames.org/ontology#A.PCLD" ); + + public static final Resource A_PCLF = M_MODEL.createResource( "http://www.geonames.org/ontology#A.PCLF" ); + + public static final Resource A_PCLH = M_MODEL.createResource( "http://www.geonames.org/ontology#A.PCLH" ); + + public static final Resource A_PCLI = M_MODEL.createResource( "http://www.geonames.org/ontology#A.PCLI" ); + + public static final Resource A_PCLIX = M_MODEL.createResource( "http://www.geonames.org/ontology#A.PCLIX" ); + + public static final Resource A_PCLS = M_MODEL.createResource( "http://www.geonames.org/ontology#A.PCLS" ); + + public static final Resource A_PPCLH = M_MODEL.createResource( "http://www.geonames.org/ontology#A.PPCLH" ); + + public static final Resource A_PPLH = M_MODEL.createResource( "http://www.geonames.org/ontology#A.PPLH" ); + + public static final Resource A_PRSH = M_MODEL.createResource( "http://www.geonames.org/ontology#A.PRSH" ); + + public static final Resource A_TERR = M_MODEL.createResource( "http://www.geonames.org/ontology#A.TERR" ); + + public static final Resource A_ZN = M_MODEL.createResource( "http://www.geonames.org/ontology#A.ZN" ); + + public static final Resource A_ZNB = M_MODEL.createResource( "http://www.geonames.org/ontology#A.ZNB" ); + + /**

stream, lake, ...

*/ + public static final Resource H = M_MODEL.createResource( "http://www.geonames.org/ontology#H" ); + + public static final Resource H_AIRS = M_MODEL.createResource( "http://www.geonames.org/ontology#H.AIRS" ); + + public static final Resource H_ANCH = M_MODEL.createResource( "http://www.geonames.org/ontology#H.ANCH" ); + + public static final Resource H_BAY = M_MODEL.createResource( "http://www.geonames.org/ontology#H.BAY" ); + + public static final Resource H_BAYS = M_MODEL.createResource( "http://www.geonames.org/ontology#H.BAYS" ); + + public static final Resource H_BGHT = M_MODEL.createResource( "http://www.geonames.org/ontology#H.BGHT" ); + + public static final Resource H_BNK = M_MODEL.createResource( "http://www.geonames.org/ontology#H.BNK" ); + + public static final Resource H_BNKR = M_MODEL.createResource( "http://www.geonames.org/ontology#H.BNKR" ); + + public static final Resource H_BNKX = M_MODEL.createResource( "http://www.geonames.org/ontology#H.BNKX" ); + + public static final Resource H_BOG = M_MODEL.createResource( "http://www.geonames.org/ontology#H.BOG" ); + + public static final Resource H_CAPG = M_MODEL.createResource( "http://www.geonames.org/ontology#H.CAPG" ); + + public static final Resource H_CHN = M_MODEL.createResource( "http://www.geonames.org/ontology#H.CHN" ); + + public static final Resource H_CHNL = M_MODEL.createResource( "http://www.geonames.org/ontology#H.CHNL" ); + + public static final Resource H_CHNM = M_MODEL.createResource( "http://www.geonames.org/ontology#H.CHNM" ); + + public static final Resource H_CHNN = M_MODEL.createResource( "http://www.geonames.org/ontology#H.CHNN" ); + + public static final Resource H_CNFL = M_MODEL.createResource( "http://www.geonames.org/ontology#H.CNFL" ); + + public static final Resource H_CNL = M_MODEL.createResource( "http://www.geonames.org/ontology#H.CNL" ); + + public static final Resource H_CNLA = M_MODEL.createResource( "http://www.geonames.org/ontology#H.CNLA" ); + + public static final Resource H_CNLB = M_MODEL.createResource( "http://www.geonames.org/ontology#H.CNLB" ); + + public static final Resource H_CNLD = M_MODEL.createResource( "http://www.geonames.org/ontology#H.CNLD" ); + + public static final Resource H_CNLI = M_MODEL.createResource( "http://www.geonames.org/ontology#H.CNLI" ); + + public static final Resource H_CNLN = M_MODEL.createResource( "http://www.geonames.org/ontology#H.CNLN" ); + + public static final Resource H_CNLQ = M_MODEL.createResource( "http://www.geonames.org/ontology#H.CNLQ" ); + + public static final Resource H_CNLSB = M_MODEL.createResource( "http://www.geonames.org/ontology#H.CNLSB" ); + + public static final Resource H_CNLX = M_MODEL.createResource( "http://www.geonames.org/ontology#H.CNLX" ); + + public static final Resource H_COVE = M_MODEL.createResource( "http://www.geonames.org/ontology#H.COVE" ); + + public static final Resource H_CRKT = M_MODEL.createResource( "http://www.geonames.org/ontology#H.CRKT" ); + + public static final Resource H_CRNT = M_MODEL.createResource( "http://www.geonames.org/ontology#H.CRNT" ); + + public static final Resource H_CUTF = M_MODEL.createResource( "http://www.geonames.org/ontology#H.CUTF" ); + + public static final Resource H_DCK = M_MODEL.createResource( "http://www.geonames.org/ontology#H.DCK" ); + + public static final Resource H_DCKB = M_MODEL.createResource( "http://www.geonames.org/ontology#H.DCKB" ); + + public static final Resource H_DOMG = M_MODEL.createResource( "http://www.geonames.org/ontology#H.DOMG" ); + + public static final Resource H_DPRG = M_MODEL.createResource( "http://www.geonames.org/ontology#H.DPRG" ); + + public static final Resource H_DTCH = M_MODEL.createResource( "http://www.geonames.org/ontology#H.DTCH" ); + + public static final Resource H_DTCHD = M_MODEL.createResource( "http://www.geonames.org/ontology#H.DTCHD" ); + + public static final Resource H_DTCHI = M_MODEL.createResource( "http://www.geonames.org/ontology#H.DTCHI" ); + + public static final Resource H_DTCHM = M_MODEL.createResource( "http://www.geonames.org/ontology#H.DTCHM" ); + + public static final Resource H_ESTY = M_MODEL.createResource( "http://www.geonames.org/ontology#H.ESTY" ); + + public static final Resource H_FISH = M_MODEL.createResource( "http://www.geonames.org/ontology#H.FISH" ); + + public static final Resource H_FJD = M_MODEL.createResource( "http://www.geonames.org/ontology#H.FJD" ); + + public static final Resource H_FJDS = M_MODEL.createResource( "http://www.geonames.org/ontology#H.FJDS" ); + + public static final Resource H_FLLS = M_MODEL.createResource( "http://www.geonames.org/ontology#H.FLLS" ); + + public static final Resource H_FLLSX = M_MODEL.createResource( "http://www.geonames.org/ontology#H.FLLSX" ); + + public static final Resource H_FLTM = M_MODEL.createResource( "http://www.geonames.org/ontology#H.FLTM" ); + + public static final Resource H_FLTT = M_MODEL.createResource( "http://www.geonames.org/ontology#H.FLTT" ); + + public static final Resource H_GLCR = M_MODEL.createResource( "http://www.geonames.org/ontology#H.GLCR" ); + + public static final Resource H_GULF = M_MODEL.createResource( "http://www.geonames.org/ontology#H.GULF" ); + + public static final Resource H_GYSR = M_MODEL.createResource( "http://www.geonames.org/ontology#H.GYSR" ); + + public static final Resource H_HBR = M_MODEL.createResource( "http://www.geonames.org/ontology#H.HBR" ); + + public static final Resource H_HBRX = M_MODEL.createResource( "http://www.geonames.org/ontology#H.HBRX" ); + + public static final Resource H_INLT = M_MODEL.createResource( "http://www.geonames.org/ontology#H.INLT" ); + + public static final Resource H_INLTQ = M_MODEL.createResource( "http://www.geonames.org/ontology#H.INLTQ" ); + + public static final Resource H_LBED = M_MODEL.createResource( "http://www.geonames.org/ontology#H.LBED" ); + + public static final Resource H_LGN = M_MODEL.createResource( "http://www.geonames.org/ontology#H.LGN" ); + + public static final Resource H_LGNS = M_MODEL.createResource( "http://www.geonames.org/ontology#H.LGNS" ); + + public static final Resource H_LGNX = M_MODEL.createResource( "http://www.geonames.org/ontology#H.LGNX" ); + + public static final Resource H_LK = M_MODEL.createResource( "http://www.geonames.org/ontology#H.LK" ); + + public static final Resource H_LKC = M_MODEL.createResource( "http://www.geonames.org/ontology#H.LKC" ); + + public static final Resource H_LKI = M_MODEL.createResource( "http://www.geonames.org/ontology#H.LKI" ); + + public static final Resource H_LKN = M_MODEL.createResource( "http://www.geonames.org/ontology#H.LKN" ); + + public static final Resource H_LKNI = M_MODEL.createResource( "http://www.geonames.org/ontology#H.LKNI" ); + + public static final Resource H_LKO = M_MODEL.createResource( "http://www.geonames.org/ontology#H.LKO" ); + + public static final Resource H_LKOI = M_MODEL.createResource( "http://www.geonames.org/ontology#H.LKOI" ); + + public static final Resource H_LKS = M_MODEL.createResource( "http://www.geonames.org/ontology#H.LKS" ); + + public static final Resource H_LKSB = M_MODEL.createResource( "http://www.geonames.org/ontology#H.LKSB" ); + + public static final Resource H_LKSC = M_MODEL.createResource( "http://www.geonames.org/ontology#H.LKSC" ); + + public static final Resource H_LKSI = M_MODEL.createResource( "http://www.geonames.org/ontology#H.LKSI" ); + + public static final Resource H_LKSN = M_MODEL.createResource( "http://www.geonames.org/ontology#H.LKSN" ); + + public static final Resource H_LKSNI = M_MODEL.createResource( "http://www.geonames.org/ontology#H.LKSNI" ); + + public static final Resource H_LKX = M_MODEL.createResource( "http://www.geonames.org/ontology#H.LKX" ); + + public static final Resource H_MFGN = M_MODEL.createResource( "http://www.geonames.org/ontology#H.MFGN" ); + + public static final Resource H_MGV = M_MODEL.createResource( "http://www.geonames.org/ontology#H.MGV" ); + + public static final Resource H_MOOR = M_MODEL.createResource( "http://www.geonames.org/ontology#H.MOOR" ); + + public static final Resource H_MRSH = M_MODEL.createResource( "http://www.geonames.org/ontology#H.MRSH" ); + + public static final Resource H_MRSHN = M_MODEL.createResource( "http://www.geonames.org/ontology#H.MRSHN" ); + + public static final Resource H_NRWS = M_MODEL.createResource( "http://www.geonames.org/ontology#H.NRWS" ); + + public static final Resource H_OCN = M_MODEL.createResource( "http://www.geonames.org/ontology#H.OCN" ); + + public static final Resource H_OVF = M_MODEL.createResource( "http://www.geonames.org/ontology#H.OVF" ); + + public static final Resource H_PND = M_MODEL.createResource( "http://www.geonames.org/ontology#H.PND" ); + + public static final Resource H_PNDI = M_MODEL.createResource( "http://www.geonames.org/ontology#H.PNDI" ); + + public static final Resource H_PNDN = M_MODEL.createResource( "http://www.geonames.org/ontology#H.PNDN" ); + + public static final Resource H_PNDNI = M_MODEL.createResource( "http://www.geonames.org/ontology#H.PNDNI" ); + + public static final Resource H_PNDS = M_MODEL.createResource( "http://www.geonames.org/ontology#H.PNDS" ); + + public static final Resource H_PNDSF = M_MODEL.createResource( "http://www.geonames.org/ontology#H.PNDSF" ); + + public static final Resource H_PNDSI = M_MODEL.createResource( "http://www.geonames.org/ontology#H.PNDSI" ); + + public static final Resource H_PNDSN = M_MODEL.createResource( "http://www.geonames.org/ontology#H.PNDSN" ); + + public static final Resource H_POOL = M_MODEL.createResource( "http://www.geonames.org/ontology#H.POOL" ); + + public static final Resource H_POOLI = M_MODEL.createResource( "http://www.geonames.org/ontology#H.POOLI" ); + + public static final Resource H_RCH = M_MODEL.createResource( "http://www.geonames.org/ontology#H.RCH" ); + + public static final Resource H_RDGG = M_MODEL.createResource( "http://www.geonames.org/ontology#H.RDGG" ); + + public static final Resource H_RDST = M_MODEL.createResource( "http://www.geonames.org/ontology#H.RDST" ); + + public static final Resource H_RF = M_MODEL.createResource( "http://www.geonames.org/ontology#H.RF" ); + + public static final Resource H_RFC = M_MODEL.createResource( "http://www.geonames.org/ontology#H.RFC" ); + + public static final Resource H_RFX = M_MODEL.createResource( "http://www.geonames.org/ontology#H.RFX" ); + + public static final Resource H_RPDS = M_MODEL.createResource( "http://www.geonames.org/ontology#H.RPDS" ); + + public static final Resource H_RSV = M_MODEL.createResource( "http://www.geonames.org/ontology#H.RSV" ); + + public static final Resource H_RSVI = M_MODEL.createResource( "http://www.geonames.org/ontology#H.RSVI" ); + + public static final Resource H_RSVT = M_MODEL.createResource( "http://www.geonames.org/ontology#H.RSVT" ); + + public static final Resource H_RVN = M_MODEL.createResource( "http://www.geonames.org/ontology#H.RVN" ); + + public static final Resource H_SBKH = M_MODEL.createResource( "http://www.geonames.org/ontology#H.SBKH" ); + + public static final Resource H_SD = M_MODEL.createResource( "http://www.geonames.org/ontology#H.SD" ); + + public static final Resource H_SEA = M_MODEL.createResource( "http://www.geonames.org/ontology#H.SEA" ); + + public static final Resource H_SHOL = M_MODEL.createResource( "http://www.geonames.org/ontology#H.SHOL" ); + + public static final Resource H_SILL = M_MODEL.createResource( "http://www.geonames.org/ontology#H.SILL" ); + + public static final Resource H_SPNG = M_MODEL.createResource( "http://www.geonames.org/ontology#H.SPNG" ); + + public static final Resource H_SPNS = M_MODEL.createResource( "http://www.geonames.org/ontology#H.SPNS" ); + + public static final Resource H_SPNT = M_MODEL.createResource( "http://www.geonames.org/ontology#H.SPNT" ); + + public static final Resource H_STM = M_MODEL.createResource( "http://www.geonames.org/ontology#H.STM" ); + + public static final Resource H_STMA = M_MODEL.createResource( "http://www.geonames.org/ontology#H.STMA" ); + + public static final Resource H_STMB = M_MODEL.createResource( "http://www.geonames.org/ontology#H.STMB" ); + + public static final Resource H_STMC = M_MODEL.createResource( "http://www.geonames.org/ontology#H.STMC" ); + + public static final Resource H_STMD = M_MODEL.createResource( "http://www.geonames.org/ontology#H.STMD" ); + + public static final Resource H_STMH = M_MODEL.createResource( "http://www.geonames.org/ontology#H.STMH" ); + + public static final Resource H_STMI = M_MODEL.createResource( "http://www.geonames.org/ontology#H.STMI" ); + + public static final Resource H_STMIX = M_MODEL.createResource( "http://www.geonames.org/ontology#H.STMIX" ); + + public static final Resource H_STMM = M_MODEL.createResource( "http://www.geonames.org/ontology#H.STMM" ); + + public static final Resource H_STMQ = M_MODEL.createResource( "http://www.geonames.org/ontology#H.STMQ" ); + + public static final Resource H_STMS = M_MODEL.createResource( "http://www.geonames.org/ontology#H.STMS" ); + + public static final Resource H_STMSB = M_MODEL.createResource( "http://www.geonames.org/ontology#H.STMSB" ); + + public static final Resource H_STMX = M_MODEL.createResource( "http://www.geonames.org/ontology#H.STMX" ); + + public static final Resource H_STRT = M_MODEL.createResource( "http://www.geonames.org/ontology#H.STRT" ); + + public static final Resource H_SWMP = M_MODEL.createResource( "http://www.geonames.org/ontology#H.SWMP" ); + + public static final Resource H_SYSI = M_MODEL.createResource( "http://www.geonames.org/ontology#H.SYSI" ); + + public static final Resource H_TNLC = M_MODEL.createResource( "http://www.geonames.org/ontology#H.TNLC" ); + + public static final Resource H_WAD = M_MODEL.createResource( "http://www.geonames.org/ontology#H.WAD" ); + + public static final Resource H_WADB = M_MODEL.createResource( "http://www.geonames.org/ontology#H.WADB" ); + + public static final Resource H_WADJ = M_MODEL.createResource( "http://www.geonames.org/ontology#H.WADJ" ); + + public static final Resource H_WADM = M_MODEL.createResource( "http://www.geonames.org/ontology#H.WADM" ); + + public static final Resource H_WADS = M_MODEL.createResource( "http://www.geonames.org/ontology#H.WADS" ); + + public static final Resource H_WADX = M_MODEL.createResource( "http://www.geonames.org/ontology#H.WADX" ); + + public static final Resource H_WHRL = M_MODEL.createResource( "http://www.geonames.org/ontology#H.WHRL" ); + + public static final Resource H_WLL = M_MODEL.createResource( "http://www.geonames.org/ontology#H.WLL" ); + + public static final Resource H_WLLQ = M_MODEL.createResource( "http://www.geonames.org/ontology#H.WLLQ" ); + + public static final Resource H_WLLS = M_MODEL.createResource( "http://www.geonames.org/ontology#H.WLLS" ); + + public static final Resource H_WTLD = M_MODEL.createResource( "http://www.geonames.org/ontology#H.WTLD" ); + + public static final Resource H_WTLDI = M_MODEL.createResource( "http://www.geonames.org/ontology#H.WTLDI" ); + + public static final Resource H_WTRC = M_MODEL.createResource( "http://www.geonames.org/ontology#H.WTRC" ); + + public static final Resource H_WTRH = M_MODEL.createResource( "http://www.geonames.org/ontology#H.WTRH" ); + + /**

parks,area, ...

*/ + public static final Resource L = M_MODEL.createResource( "http://www.geonames.org/ontology#L" ); + + public static final Resource L_AGRC = M_MODEL.createResource( "http://www.geonames.org/ontology#L.AGRC" ); + + public static final Resource L_AMUS = M_MODEL.createResource( "http://www.geonames.org/ontology#L.AMUS" ); + + public static final Resource L_AREA = M_MODEL.createResource( "http://www.geonames.org/ontology#L.AREA" ); + + public static final Resource L_BSND = M_MODEL.createResource( "http://www.geonames.org/ontology#L.BSND" ); + + public static final Resource L_BSNP = M_MODEL.createResource( "http://www.geonames.org/ontology#L.BSNP" ); + + public static final Resource L_BTL = M_MODEL.createResource( "http://www.geonames.org/ontology#L.BTL" ); + + public static final Resource L_CLG = M_MODEL.createResource( "http://www.geonames.org/ontology#L.CLG" ); + + public static final Resource L_CMN = M_MODEL.createResource( "http://www.geonames.org/ontology#L.CMN" ); + + public static final Resource L_CNS = M_MODEL.createResource( "http://www.geonames.org/ontology#L.CNS" ); + + public static final Resource L_COLF = M_MODEL.createResource( "http://www.geonames.org/ontology#L.COLF" ); + + public static final Resource L_CONT = M_MODEL.createResource( "http://www.geonames.org/ontology#L.CONT" ); + + public static final Resource L_CST = M_MODEL.createResource( "http://www.geonames.org/ontology#L.CST" ); + + public static final Resource L_CTRB = M_MODEL.createResource( "http://www.geonames.org/ontology#L.CTRB" ); + + public static final Resource L_DEVH = M_MODEL.createResource( "http://www.geonames.org/ontology#L.DEVH" ); + + public static final Resource L_FLD = M_MODEL.createResource( "http://www.geonames.org/ontology#L.FLD" ); + + public static final Resource L_FLDI = M_MODEL.createResource( "http://www.geonames.org/ontology#L.FLDI" ); + + public static final Resource L_GASF = M_MODEL.createResource( "http://www.geonames.org/ontology#L.GASF" ); + + public static final Resource L_GRAZ = M_MODEL.createResource( "http://www.geonames.org/ontology#L.GRAZ" ); + + public static final Resource L_GVL = M_MODEL.createResource( "http://www.geonames.org/ontology#L.GVL" ); + + public static final Resource L_INDS = M_MODEL.createResource( "http://www.geonames.org/ontology#L.INDS" ); + + public static final Resource L_LAND = M_MODEL.createResource( "http://www.geonames.org/ontology#L.LAND" ); + + public static final Resource L_LCTY = M_MODEL.createResource( "http://www.geonames.org/ontology#L.LCTY" ); + + public static final Resource L_MILB = M_MODEL.createResource( "http://www.geonames.org/ontology#L.MILB" ); + + public static final Resource L_MNA = M_MODEL.createResource( "http://www.geonames.org/ontology#L.MNA" ); + + public static final Resource L_MVA = M_MODEL.createResource( "http://www.geonames.org/ontology#L.MVA" ); + + public static final Resource L_NVB = M_MODEL.createResource( "http://www.geonames.org/ontology#L.NVB" ); + + public static final Resource L_OAS = M_MODEL.createResource( "http://www.geonames.org/ontology#L.OAS" ); + + public static final Resource L_OILF = M_MODEL.createResource( "http://www.geonames.org/ontology#L.OILF" ); + + public static final Resource L_PEAT = M_MODEL.createResource( "http://www.geonames.org/ontology#L.PEAT" ); + + public static final Resource L_PRK = M_MODEL.createResource( "http://www.geonames.org/ontology#L.PRK" ); + + public static final Resource L_PRT = M_MODEL.createResource( "http://www.geonames.org/ontology#L.PRT" ); + + public static final Resource L_QCKS = M_MODEL.createResource( "http://www.geonames.org/ontology#L.QCKS" ); + + public static final Resource L_REP = M_MODEL.createResource( "http://www.geonames.org/ontology#L.REP" ); + + public static final Resource L_RES = M_MODEL.createResource( "http://www.geonames.org/ontology#L.RES" ); + + public static final Resource L_RESA = M_MODEL.createResource( "http://www.geonames.org/ontology#L.RESA" ); + + public static final Resource L_RESF = M_MODEL.createResource( "http://www.geonames.org/ontology#L.RESF" ); + + public static final Resource L_RESH = M_MODEL.createResource( "http://www.geonames.org/ontology#L.RESH" ); + + public static final Resource L_RESN = M_MODEL.createResource( "http://www.geonames.org/ontology#L.RESN" ); + + public static final Resource L_RESP = M_MODEL.createResource( "http://www.geonames.org/ontology#L.RESP" ); + + public static final Resource L_RESV = M_MODEL.createResource( "http://www.geonames.org/ontology#L.RESV" ); + + public static final Resource L_RESW = M_MODEL.createResource( "http://www.geonames.org/ontology#L.RESW" ); + + public static final Resource L_RGN = M_MODEL.createResource( "http://www.geonames.org/ontology#L.RGN" ); + + public static final Resource L_RGNE = M_MODEL.createResource( "http://www.geonames.org/ontology#L.RGNE" ); + + public static final Resource L_RGNH = M_MODEL.createResource( "http://www.geonames.org/ontology#L.RGNH" ); + + public static final Resource L_RGNL = M_MODEL.createResource( "http://www.geonames.org/ontology#L.RGNL" ); + + public static final Resource L_RNGA = M_MODEL.createResource( "http://www.geonames.org/ontology#L.RNGA" ); + + public static final Resource L_SALT = M_MODEL.createResource( "http://www.geonames.org/ontology#L.SALT" ); + + public static final Resource L_SNOW = M_MODEL.createResource( "http://www.geonames.org/ontology#L.SNOW" ); + + public static final Resource L_TRB = M_MODEL.createResource( "http://www.geonames.org/ontology#L.TRB" ); + + public static final Resource L_ZZZZZ = M_MODEL.createResource( "http://www.geonames.org/ontology#L.ZZZZZ" ); + + /**

city, village,...

*/ + public static final Resource P = M_MODEL.createResource( "http://www.geonames.org/ontology#P" ); + + public static final Resource P_PPL = M_MODEL.createResource( "http://www.geonames.org/ontology#P.PPL" ); + + public static final Resource P_PPLA = M_MODEL.createResource( "http://www.geonames.org/ontology#P.PPLA" ); + + public static final Resource P_PPLA2 = M_MODEL.createResource( "http://www.geonames.org/ontology#P.PPLA2" ); + + public static final Resource P_PPLA3 = M_MODEL.createResource( "http://www.geonames.org/ontology#P.PPLA3" ); + + public static final Resource P_PPLA4 = M_MODEL.createResource( "http://www.geonames.org/ontology#P.PPLA4" ); + + public static final Resource P_PPLC = M_MODEL.createResource( "http://www.geonames.org/ontology#P.PPLC" ); + + public static final Resource P_PPLF = M_MODEL.createResource( "http://www.geonames.org/ontology#P.PPLF" ); + + public static final Resource P_PPLG = M_MODEL.createResource( "http://www.geonames.org/ontology#P.PPLG" ); + + public static final Resource P_PPLL = M_MODEL.createResource( "http://www.geonames.org/ontology#P.PPLL" ); + + public static final Resource P_PPLQ = M_MODEL.createResource( "http://www.geonames.org/ontology#P.PPLQ" ); + + public static final Resource P_PPLR = M_MODEL.createResource( "http://www.geonames.org/ontology#P.PPLR" ); + + public static final Resource P_PPLS = M_MODEL.createResource( "http://www.geonames.org/ontology#P.PPLS" ); + + public static final Resource P_PPLW = M_MODEL.createResource( "http://www.geonames.org/ontology#P.PPLW" ); + + public static final Resource P_PPLX = M_MODEL.createResource( "http://www.geonames.org/ontology#P.PPLX" ); + + public static final Resource P_STLMT = M_MODEL.createResource( "http://www.geonames.org/ontology#P.STLMT" ); + + /**

road, railroad, ...

*/ + public static final Resource R = M_MODEL.createResource( "http://www.geonames.org/ontology#R" ); + + public static final Resource R_CSWY = M_MODEL.createResource( "http://www.geonames.org/ontology#R.CSWY" ); + + public static final Resource R_CSWYQ = M_MODEL.createResource( "http://www.geonames.org/ontology#R.CSWYQ" ); + + public static final Resource R_OILP = M_MODEL.createResource( "http://www.geonames.org/ontology#R.OILP" ); + + public static final Resource R_PRMN = M_MODEL.createResource( "http://www.geonames.org/ontology#R.PRMN" ); + + public static final Resource R_PTGE = M_MODEL.createResource( "http://www.geonames.org/ontology#R.PTGE" ); + + public static final Resource R_RD = M_MODEL.createResource( "http://www.geonames.org/ontology#R.RD" ); + + public static final Resource R_RDA = M_MODEL.createResource( "http://www.geonames.org/ontology#R.RDA" ); + + public static final Resource R_RDB = M_MODEL.createResource( "http://www.geonames.org/ontology#R.RDB" ); + + public static final Resource R_RDCUT = M_MODEL.createResource( "http://www.geonames.org/ontology#R.RDCUT" ); + + public static final Resource R_RDJCT = M_MODEL.createResource( "http://www.geonames.org/ontology#R.RDJCT" ); + + public static final Resource R_RJCT = M_MODEL.createResource( "http://www.geonames.org/ontology#R.RJCT" ); + + public static final Resource R_RR = M_MODEL.createResource( "http://www.geonames.org/ontology#R.RR" ); + + public static final Resource R_RRQ = M_MODEL.createResource( "http://www.geonames.org/ontology#R.RRQ" ); + + public static final Resource R_RTE = M_MODEL.createResource( "http://www.geonames.org/ontology#R.RTE" ); + + public static final Resource R_RYD = M_MODEL.createResource( "http://www.geonames.org/ontology#R.RYD" ); + + public static final Resource R_ST = M_MODEL.createResource( "http://www.geonames.org/ontology#R.ST" ); + + public static final Resource R_STKR = M_MODEL.createResource( "http://www.geonames.org/ontology#R.STKR" ); + + public static final Resource R_TNL = M_MODEL.createResource( "http://www.geonames.org/ontology#R.TNL" ); + + public static final Resource R_TNLN = M_MODEL.createResource( "http://www.geonames.org/ontology#R.TNLN" ); + + public static final Resource R_TNLRD = M_MODEL.createResource( "http://www.geonames.org/ontology#R.TNLRD" ); + + public static final Resource R_TNLRR = M_MODEL.createResource( "http://www.geonames.org/ontology#R.TNLRR" ); + + public static final Resource R_TNLS = M_MODEL.createResource( "http://www.geonames.org/ontology#R.TNLS" ); + + public static final Resource R_TRL = M_MODEL.createResource( "http://www.geonames.org/ontology#R.TRL" ); + + /**

spot, building, farm, ...

*/ + public static final Resource S = M_MODEL.createResource( "http://www.geonames.org/ontology#S" ); + + public static final Resource S_ADMF = M_MODEL.createResource( "http://www.geonames.org/ontology#S.ADMF" ); + + public static final Resource S_AGRF = M_MODEL.createResource( "http://www.geonames.org/ontology#S.AGRF" ); + + public static final Resource S_AIRB = M_MODEL.createResource( "http://www.geonames.org/ontology#S.AIRB" ); + + public static final Resource S_AIRF = M_MODEL.createResource( "http://www.geonames.org/ontology#S.AIRF" ); + + public static final Resource S_AIRH = M_MODEL.createResource( "http://www.geonames.org/ontology#S.AIRH" ); + + public static final Resource S_AIRP = M_MODEL.createResource( "http://www.geonames.org/ontology#S.AIRP" ); + + public static final Resource S_AIRQ = M_MODEL.createResource( "http://www.geonames.org/ontology#S.AIRQ" ); + + public static final Resource S_AMTH = M_MODEL.createResource( "http://www.geonames.org/ontology#S.AMTH" ); + + public static final Resource S_ANS = M_MODEL.createResource( "http://www.geonames.org/ontology#S.ANS" ); + + public static final Resource S_AQC = M_MODEL.createResource( "http://www.geonames.org/ontology#S.AQC" ); + + public static final Resource S_ARCH = M_MODEL.createResource( "http://www.geonames.org/ontology#S.ARCH" ); + + public static final Resource S_ASTR = M_MODEL.createResource( "http://www.geonames.org/ontology#S.ASTR" ); + + public static final Resource S_ASYL = M_MODEL.createResource( "http://www.geonames.org/ontology#S.ASYL" ); + + public static final Resource S_ATHF = M_MODEL.createResource( "http://www.geonames.org/ontology#S.ATHF" ); + + public static final Resource S_ATM = M_MODEL.createResource( "http://www.geonames.org/ontology#S.ATM" ); + + public static final Resource S_BANK = M_MODEL.createResource( "http://www.geonames.org/ontology#S.BANK" ); + + public static final Resource S_BCN = M_MODEL.createResource( "http://www.geonames.org/ontology#S.BCN" ); + + public static final Resource S_BDG = M_MODEL.createResource( "http://www.geonames.org/ontology#S.BDG" ); + + public static final Resource S_BDGQ = M_MODEL.createResource( "http://www.geonames.org/ontology#S.BDGQ" ); + + public static final Resource S_BLDG = M_MODEL.createResource( "http://www.geonames.org/ontology#S.BLDG" ); + + public static final Resource S_BLDO = M_MODEL.createResource( "http://www.geonames.org/ontology#S.BLDO" ); + + public static final Resource S_BP = M_MODEL.createResource( "http://www.geonames.org/ontology#S.BP" ); + + public static final Resource S_BRKS = M_MODEL.createResource( "http://www.geonames.org/ontology#S.BRKS" ); + + public static final Resource S_BRKW = M_MODEL.createResource( "http://www.geonames.org/ontology#S.BRKW" ); + + public static final Resource S_BSTN = M_MODEL.createResource( "http://www.geonames.org/ontology#S.BSTN" ); + + public static final Resource S_BTYD = M_MODEL.createResource( "http://www.geonames.org/ontology#S.BTYD" ); + + public static final Resource S_BUR = M_MODEL.createResource( "http://www.geonames.org/ontology#S.BUR" ); + + public static final Resource S_BUSTN = M_MODEL.createResource( "http://www.geonames.org/ontology#S.BUSTN" ); + + public static final Resource S_BUSTP = M_MODEL.createResource( "http://www.geonames.org/ontology#S.BUSTP" ); + + public static final Resource S_CARN = M_MODEL.createResource( "http://www.geonames.org/ontology#S.CARN" ); + + public static final Resource S_CAVE = M_MODEL.createResource( "http://www.geonames.org/ontology#S.CAVE" ); + + public static final Resource S_CCL = M_MODEL.createResource( "http://www.geonames.org/ontology#S.CCL" ); + + public static final Resource S_CH = M_MODEL.createResource( "http://www.geonames.org/ontology#S.CH" ); + + public static final Resource S_CMP = M_MODEL.createResource( "http://www.geonames.org/ontology#S.CMP" ); + + public static final Resource S_CMPL = M_MODEL.createResource( "http://www.geonames.org/ontology#S.CMPL" ); + + public static final Resource S_CMPLA = M_MODEL.createResource( "http://www.geonames.org/ontology#S.CMPLA" ); + + public static final Resource S_CMPMN = M_MODEL.createResource( "http://www.geonames.org/ontology#S.CMPMN" ); + + public static final Resource S_CMPO = M_MODEL.createResource( "http://www.geonames.org/ontology#S.CMPO" ); + + public static final Resource S_CMPQ = M_MODEL.createResource( "http://www.geonames.org/ontology#S.CMPQ" ); + + public static final Resource S_CMPRF = M_MODEL.createResource( "http://www.geonames.org/ontology#S.CMPRF" ); + + public static final Resource S_CMTY = M_MODEL.createResource( "http://www.geonames.org/ontology#S.CMTY" ); + + public static final Resource S_COMC = M_MODEL.createResource( "http://www.geonames.org/ontology#S.COMC" ); + + public static final Resource S_CRRL = M_MODEL.createResource( "http://www.geonames.org/ontology#S.CRRL" ); + + public static final Resource S_CSNO = M_MODEL.createResource( "http://www.geonames.org/ontology#S.CSNO" ); + + public static final Resource S_CSTL = M_MODEL.createResource( "http://www.geonames.org/ontology#S.CSTL" ); + + public static final Resource S_CSTM = M_MODEL.createResource( "http://www.geonames.org/ontology#S.CSTM" ); + + public static final Resource S_CTHSE = M_MODEL.createResource( "http://www.geonames.org/ontology#S.CTHSE" ); + + public static final Resource S_CTRA = M_MODEL.createResource( "http://www.geonames.org/ontology#S.CTRA" ); + + public static final Resource S_CTRCM = M_MODEL.createResource( "http://www.geonames.org/ontology#S.CTRCM" ); + + public static final Resource S_CTRF = M_MODEL.createResource( "http://www.geonames.org/ontology#S.CTRF" ); + + public static final Resource S_CTRM = M_MODEL.createResource( "http://www.geonames.org/ontology#S.CTRM" ); + + public static final Resource S_CTRR = M_MODEL.createResource( "http://www.geonames.org/ontology#S.CTRR" ); + + public static final Resource S_CTRS = M_MODEL.createResource( "http://www.geonames.org/ontology#S.CTRS" ); + + public static final Resource S_CVNT = M_MODEL.createResource( "http://www.geonames.org/ontology#S.CVNT" ); + + public static final Resource S_DAM = M_MODEL.createResource( "http://www.geonames.org/ontology#S.DAM" ); + + public static final Resource S_DAMQ = M_MODEL.createResource( "http://www.geonames.org/ontology#S.DAMQ" ); + + public static final Resource S_DAMSB = M_MODEL.createResource( "http://www.geonames.org/ontology#S.DAMSB" ); + + public static final Resource S_DARY = M_MODEL.createResource( "http://www.geonames.org/ontology#S.DARY" ); + + public static final Resource S_DCKD = M_MODEL.createResource( "http://www.geonames.org/ontology#S.DCKD" ); + + public static final Resource S_DCKY = M_MODEL.createResource( "http://www.geonames.org/ontology#S.DCKY" ); + + public static final Resource S_DIKE = M_MODEL.createResource( "http://www.geonames.org/ontology#S.DIKE" ); + + public static final Resource S_DIP = M_MODEL.createResource( "http://www.geonames.org/ontology#S.DIP" ); + + public static final Resource S_DPOF = M_MODEL.createResource( "http://www.geonames.org/ontology#S.DPOF" ); + + public static final Resource S_EST = M_MODEL.createResource( "http://www.geonames.org/ontology#S.EST" ); + + public static final Resource S_ESTB = M_MODEL.createResource( "http://www.geonames.org/ontology#S.ESTB" ); + + public static final Resource S_ESTC = M_MODEL.createResource( "http://www.geonames.org/ontology#S.ESTC" ); + + public static final Resource S_ESTO = M_MODEL.createResource( "http://www.geonames.org/ontology#S.ESTO" ); + + public static final Resource S_ESTR = M_MODEL.createResource( "http://www.geonames.org/ontology#S.ESTR" ); + + public static final Resource S_ESTSG = M_MODEL.createResource( "http://www.geonames.org/ontology#S.ESTSG" ); + + public static final Resource S_ESTSL = M_MODEL.createResource( "http://www.geonames.org/ontology#S.ESTSL" ); + + public static final Resource S_ESTT = M_MODEL.createResource( "http://www.geonames.org/ontology#S.ESTT" ); + + public static final Resource S_ESTX = M_MODEL.createResource( "http://www.geonames.org/ontology#S.ESTX" ); + + public static final Resource S_FCL = M_MODEL.createResource( "http://www.geonames.org/ontology#S.FCL" ); + + public static final Resource S_FNDY = M_MODEL.createResource( "http://www.geonames.org/ontology#S.FNDY" ); + + public static final Resource S_FRM = M_MODEL.createResource( "http://www.geonames.org/ontology#S.FRM" ); + + public static final Resource S_FRMQ = M_MODEL.createResource( "http://www.geonames.org/ontology#S.FRMQ" ); + + public static final Resource S_FRMS = M_MODEL.createResource( "http://www.geonames.org/ontology#S.FRMS" ); + + public static final Resource S_FRMT = M_MODEL.createResource( "http://www.geonames.org/ontology#S.FRMT" ); + + public static final Resource S_FT = M_MODEL.createResource( "http://www.geonames.org/ontology#S.FT" ); + + public static final Resource S_FY = M_MODEL.createResource( "http://www.geonames.org/ontology#S.FY" ); + + public static final Resource S_GATE = M_MODEL.createResource( "http://www.geonames.org/ontology#S.GATE" ); + + public static final Resource S_GDN = M_MODEL.createResource( "http://www.geonames.org/ontology#S.GDN" ); + + public static final Resource S_GHAT = M_MODEL.createResource( "http://www.geonames.org/ontology#S.GHAT" ); + + public static final Resource S_GHSE = M_MODEL.createResource( "http://www.geonames.org/ontology#S.GHSE" ); + + public static final Resource S_GOSP = M_MODEL.createResource( "http://www.geonames.org/ontology#S.GOSP" ); + + public static final Resource S_GOVL = M_MODEL.createResource( "http://www.geonames.org/ontology#S.GOVL" ); + + public static final Resource S_GRVE = M_MODEL.createResource( "http://www.geonames.org/ontology#S.GRVE" ); + + public static final Resource S_HERM = M_MODEL.createResource( "http://www.geonames.org/ontology#S.HERM" ); + + public static final Resource S_HLT = M_MODEL.createResource( "http://www.geonames.org/ontology#S.HLT" ); + + public static final Resource S_HMSD = M_MODEL.createResource( "http://www.geonames.org/ontology#S.HMSD" ); + + public static final Resource S_HSE = M_MODEL.createResource( "http://www.geonames.org/ontology#S.HSE" ); + + public static final Resource S_HSEC = M_MODEL.createResource( "http://www.geonames.org/ontology#S.HSEC" ); + + public static final Resource S_HSP = M_MODEL.createResource( "http://www.geonames.org/ontology#S.HSP" ); + + public static final Resource S_HSPC = M_MODEL.createResource( "http://www.geonames.org/ontology#S.HSPC" ); + + public static final Resource S_HSPD = M_MODEL.createResource( "http://www.geonames.org/ontology#S.HSPD" ); + + public static final Resource S_HSPL = M_MODEL.createResource( "http://www.geonames.org/ontology#S.HSPL" ); + + public static final Resource S_HSTS = M_MODEL.createResource( "http://www.geonames.org/ontology#S.HSTS" ); + + public static final Resource S_HTL = M_MODEL.createResource( "http://www.geonames.org/ontology#S.HTL" ); + + public static final Resource S_HUT = M_MODEL.createResource( "http://www.geonames.org/ontology#S.HUT" ); + + public static final Resource S_HUTS = M_MODEL.createResource( "http://www.geonames.org/ontology#S.HUTS" ); + + public static final Resource S_INSM = M_MODEL.createResource( "http://www.geonames.org/ontology#S.INSM" ); + + public static final Resource S_ITTR = M_MODEL.createResource( "http://www.geonames.org/ontology#S.ITTR" ); + + public static final Resource S_JTY = M_MODEL.createResource( "http://www.geonames.org/ontology#S.JTY" ); + + public static final Resource S_LDNG = M_MODEL.createResource( "http://www.geonames.org/ontology#S.LDNG" ); + + public static final Resource S_LEPC = M_MODEL.createResource( "http://www.geonames.org/ontology#S.LEPC" ); + + public static final Resource S_LIBR = M_MODEL.createResource( "http://www.geonames.org/ontology#S.LIBR" ); + + public static final Resource S_LNDF = M_MODEL.createResource( "http://www.geonames.org/ontology#S.LNDF" ); + + public static final Resource S_LOCK = M_MODEL.createResource( "http://www.geonames.org/ontology#S.LOCK" ); + + public static final Resource S_LTHSE = M_MODEL.createResource( "http://www.geonames.org/ontology#S.LTHSE" ); + + public static final Resource S_MALL = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MALL" ); + + public static final Resource S_MAR = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MAR" ); + + public static final Resource S_MFG = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MFG" ); + + public static final Resource S_MFGB = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MFGB" ); + + public static final Resource S_MFGC = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MFGC" ); + + public static final Resource S_MFGCU = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MFGCU" ); + + public static final Resource S_MFGLM = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MFGLM" ); + + public static final Resource S_MFGM = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MFGM" ); + + public static final Resource S_MFGPH = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MFGPH" ); + + public static final Resource S_MFGQ = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MFGQ" ); + + public static final Resource S_MFGSG = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MFGSG" ); + + public static final Resource S_MKT = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MKT" ); + + public static final Resource S_ML = M_MODEL.createResource( "http://www.geonames.org/ontology#S.ML" ); + + public static final Resource S_MLM = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MLM" ); + + public static final Resource S_MLO = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MLO" ); + + public static final Resource S_MLSG = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MLSG" ); + + public static final Resource S_MLSGQ = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MLSGQ" ); + + public static final Resource S_MLSW = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MLSW" ); + + public static final Resource S_MLWND = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MLWND" ); + + public static final Resource S_MLWTR = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MLWTR" ); + + public static final Resource S_MN = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MN" ); + + public static final Resource S_MNAU = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MNAU" ); + + public static final Resource S_MNC = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MNC" ); + + public static final Resource S_MNCR = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MNCR" ); + + public static final Resource S_MNCU = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MNCU" ); + + public static final Resource S_MNDT = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MNDT" ); + + public static final Resource S_MNFE = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MNFE" ); + + public static final Resource S_MNMT = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MNMT" ); + + public static final Resource S_MNN = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MNN" ); + + public static final Resource S_MNNI = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MNNI" ); + + public static final Resource S_MNPB = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MNPB" ); + + public static final Resource S_MNPL = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MNPL" ); + + public static final Resource S_MNQ = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MNQ" ); + + public static final Resource S_MNQR = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MNQR" ); + + public static final Resource S_MNSN = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MNSN" ); + + public static final Resource S_MOLE = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MOLE" ); + + public static final Resource S_MSQE = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MSQE" ); + + public static final Resource S_MSSN = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MSSN" ); + + public static final Resource S_MSSNQ = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MSSNQ" ); + + public static final Resource S_MSTY = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MSTY" ); + + public static final Resource S_MTRO = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MTRO" ); + + public static final Resource S_MUS = M_MODEL.createResource( "http://www.geonames.org/ontology#S.MUS" ); + + public static final Resource S_NOV = M_MODEL.createResource( "http://www.geonames.org/ontology#S.NOV" ); + + public static final Resource S_NSY = M_MODEL.createResource( "http://www.geonames.org/ontology#S.NSY" ); + + public static final Resource S_OBPT = M_MODEL.createResource( "http://www.geonames.org/ontology#S.OBPT" ); + + public static final Resource S_OBS = M_MODEL.createResource( "http://www.geonames.org/ontology#S.OBS" ); + + public static final Resource S_OBSR = M_MODEL.createResource( "http://www.geonames.org/ontology#S.OBSR" ); + + public static final Resource S_OILJ = M_MODEL.createResource( "http://www.geonames.org/ontology#S.OILJ" ); + + public static final Resource S_OILQ = M_MODEL.createResource( "http://www.geonames.org/ontology#S.OILQ" ); + + public static final Resource S_OILR = M_MODEL.createResource( "http://www.geonames.org/ontology#S.OILR" ); + + public static final Resource S_OILT = M_MODEL.createResource( "http://www.geonames.org/ontology#S.OILT" ); + + public static final Resource S_OILW = M_MODEL.createResource( "http://www.geonames.org/ontology#S.OILW" ); + + public static final Resource S_OPRA = M_MODEL.createResource( "http://www.geonames.org/ontology#S.OPRA" ); + + public static final Resource S_PAL = M_MODEL.createResource( "http://www.geonames.org/ontology#S.PAL" ); + + public static final Resource S_PGDA = M_MODEL.createResource( "http://www.geonames.org/ontology#S.PGDA" ); + + public static final Resource S_PIER = M_MODEL.createResource( "http://www.geonames.org/ontology#S.PIER" ); + + public static final Resource S_PKLT = M_MODEL.createResource( "http://www.geonames.org/ontology#S.PKLT" ); + + public static final Resource S_PMPO = M_MODEL.createResource( "http://www.geonames.org/ontology#S.PMPO" ); + + public static final Resource S_PMPW = M_MODEL.createResource( "http://www.geonames.org/ontology#S.PMPW" ); + + public static final Resource S_PO = M_MODEL.createResource( "http://www.geonames.org/ontology#S.PO" ); + + public static final Resource S_PP = M_MODEL.createResource( "http://www.geonames.org/ontology#S.PP" ); + + public static final Resource S_PPQ = M_MODEL.createResource( "http://www.geonames.org/ontology#S.PPQ" ); + + public static final Resource S_PRKGT = M_MODEL.createResource( "http://www.geonames.org/ontology#S.PRKGT" ); + + public static final Resource S_PRKHQ = M_MODEL.createResource( "http://www.geonames.org/ontology#S.PRKHQ" ); + + public static final Resource S_PRN = M_MODEL.createResource( "http://www.geonames.org/ontology#S.PRN" ); + + public static final Resource S_PRNJ = M_MODEL.createResource( "http://www.geonames.org/ontology#S.PRNJ" ); + + public static final Resource S_PRNQ = M_MODEL.createResource( "http://www.geonames.org/ontology#S.PRNQ" ); + + public static final Resource S_PS = M_MODEL.createResource( "http://www.geonames.org/ontology#S.PS" ); + + public static final Resource S_PSH = M_MODEL.createResource( "http://www.geonames.org/ontology#S.PSH" ); + + public static final Resource S_PSTB = M_MODEL.createResource( "http://www.geonames.org/ontology#S.PSTB" ); + + public static final Resource S_PSTC = M_MODEL.createResource( "http://www.geonames.org/ontology#S.PSTC" ); + + public static final Resource S_PSTP = M_MODEL.createResource( "http://www.geonames.org/ontology#S.PSTP" ); + + public static final Resource S_PYR = M_MODEL.createResource( "http://www.geonames.org/ontology#S.PYR" ); + + public static final Resource S_PYRS = M_MODEL.createResource( "http://www.geonames.org/ontology#S.PYRS" ); + + public static final Resource S_QUAY = M_MODEL.createResource( "http://www.geonames.org/ontology#S.QUAY" ); + + public static final Resource S_RDCR = M_MODEL.createResource( "http://www.geonames.org/ontology#S.RDCR" ); + + public static final Resource S_RECG = M_MODEL.createResource( "http://www.geonames.org/ontology#S.RECG" ); + + public static final Resource S_RECR = M_MODEL.createResource( "http://www.geonames.org/ontology#S.RECR" ); + + public static final Resource S_REST = M_MODEL.createResource( "http://www.geonames.org/ontology#S.REST" ); + + public static final Resource S_RET = M_MODEL.createResource( "http://www.geonames.org/ontology#S.RET" ); + + public static final Resource S_RHSE = M_MODEL.createResource( "http://www.geonames.org/ontology#S.RHSE" ); + + public static final Resource S_RKRY = M_MODEL.createResource( "http://www.geonames.org/ontology#S.RKRY" ); + + public static final Resource S_RLG = M_MODEL.createResource( "http://www.geonames.org/ontology#S.RLG" ); + + public static final Resource S_RLGR = M_MODEL.createResource( "http://www.geonames.org/ontology#S.RLGR" ); + + public static final Resource S_RNCH = M_MODEL.createResource( "http://www.geonames.org/ontology#S.RNCH" ); + + public static final Resource S_RSD = M_MODEL.createResource( "http://www.geonames.org/ontology#S.RSD" ); + + public static final Resource S_RSGNL = M_MODEL.createResource( "http://www.geonames.org/ontology#S.RSGNL" ); + + public static final Resource S_RSRT = M_MODEL.createResource( "http://www.geonames.org/ontology#S.RSRT" ); + + public static final Resource S_RSTN = M_MODEL.createResource( "http://www.geonames.org/ontology#S.RSTN" ); + + public static final Resource S_RSTNQ = M_MODEL.createResource( "http://www.geonames.org/ontology#S.RSTNQ" ); + + public static final Resource S_RSTP = M_MODEL.createResource( "http://www.geonames.org/ontology#S.RSTP" ); + + public static final Resource S_RSTPQ = M_MODEL.createResource( "http://www.geonames.org/ontology#S.RSTPQ" ); + + public static final Resource S_RUIN = M_MODEL.createResource( "http://www.geonames.org/ontology#S.RUIN" ); + + public static final Resource S_SCH = M_MODEL.createResource( "http://www.geonames.org/ontology#S.SCH" ); + + public static final Resource S_SCHA = M_MODEL.createResource( "http://www.geonames.org/ontology#S.SCHA" ); + + public static final Resource S_SCHC = M_MODEL.createResource( "http://www.geonames.org/ontology#S.SCHC" ); + + public static final Resource S_SCHD = M_MODEL.createResource( "http://www.geonames.org/ontology#S.SCHD" ); + + public static final Resource S_SCHL = M_MODEL.createResource( "http://www.geonames.org/ontology#S.SCHL" ); + + public static final Resource S_SCHM = M_MODEL.createResource( "http://www.geonames.org/ontology#S.SCHM" ); + + public static final Resource S_SCHN = M_MODEL.createResource( "http://www.geonames.org/ontology#S.SCHN" ); + + public static final Resource S_SCHT = M_MODEL.createResource( "http://www.geonames.org/ontology#S.SCHT" ); + + public static final Resource S_SECP = M_MODEL.createResource( "http://www.geonames.org/ontology#S.SECP" ); + + public static final Resource S_SHPF = M_MODEL.createResource( "http://www.geonames.org/ontology#S.SHPF" ); + + public static final Resource S_SHRN = M_MODEL.createResource( "http://www.geonames.org/ontology#S.SHRN" ); + + public static final Resource S_SHSE = M_MODEL.createResource( "http://www.geonames.org/ontology#S.SHSE" ); + + public static final Resource S_SLCE = M_MODEL.createResource( "http://www.geonames.org/ontology#S.SLCE" ); + + public static final Resource S_SNTR = M_MODEL.createResource( "http://www.geonames.org/ontology#S.SNTR" ); + + public static final Resource S_SPA = M_MODEL.createResource( "http://www.geonames.org/ontology#S.SPA" ); + + public static final Resource S_SPLY = M_MODEL.createResource( "http://www.geonames.org/ontology#S.SPLY" ); + + public static final Resource S_SQR = M_MODEL.createResource( "http://www.geonames.org/ontology#S.SQR" ); + + public static final Resource S_STBL = M_MODEL.createResource( "http://www.geonames.org/ontology#S.STBL" ); + + public static final Resource S_STDM = M_MODEL.createResource( "http://www.geonames.org/ontology#S.STDM" ); + + public static final Resource S_STNB = M_MODEL.createResource( "http://www.geonames.org/ontology#S.STNB" ); + + public static final Resource S_STNC = M_MODEL.createResource( "http://www.geonames.org/ontology#S.STNC" ); + + public static final Resource S_STNE = M_MODEL.createResource( "http://www.geonames.org/ontology#S.STNE" ); + + public static final Resource S_STNF = M_MODEL.createResource( "http://www.geonames.org/ontology#S.STNF" ); + + public static final Resource S_STNI = M_MODEL.createResource( "http://www.geonames.org/ontology#S.STNI" ); + + public static final Resource S_STNM = M_MODEL.createResource( "http://www.geonames.org/ontology#S.STNM" ); + + public static final Resource S_STNR = M_MODEL.createResource( "http://www.geonames.org/ontology#S.STNR" ); + + public static final Resource S_STNS = M_MODEL.createResource( "http://www.geonames.org/ontology#S.STNS" ); + + public static final Resource S_STNW = M_MODEL.createResource( "http://www.geonames.org/ontology#S.STNW" ); + + public static final Resource S_STPS = M_MODEL.createResource( "http://www.geonames.org/ontology#S.STPS" ); + + public static final Resource S_SWT = M_MODEL.createResource( "http://www.geonames.org/ontology#S.SWT" ); + + public static final Resource S_THTR = M_MODEL.createResource( "http://www.geonames.org/ontology#S.THTR" ); + + public static final Resource S_TMB = M_MODEL.createResource( "http://www.geonames.org/ontology#S.TMB" ); + + public static final Resource S_TMPL = M_MODEL.createResource( "http://www.geonames.org/ontology#S.TMPL" ); + + public static final Resource S_TNKD = M_MODEL.createResource( "http://www.geonames.org/ontology#S.TNKD" ); + + public static final Resource S_TOWR = M_MODEL.createResource( "http://www.geonames.org/ontology#S.TOWR" ); + + public static final Resource S_TRANT = M_MODEL.createResource( "http://www.geonames.org/ontology#S.TRANT" ); + + public static final Resource S_TRIG = M_MODEL.createResource( "http://www.geonames.org/ontology#S.TRIG" ); + + public static final Resource S_TRMO = M_MODEL.createResource( "http://www.geonames.org/ontology#S.TRMO" ); + + public static final Resource S_TWO = M_MODEL.createResource( "http://www.geonames.org/ontology#S.TWO" ); + + public static final Resource S_UNIO = M_MODEL.createResource( "http://www.geonames.org/ontology#S.UNIO" ); + + public static final Resource S_UNIP = M_MODEL.createResource( "http://www.geonames.org/ontology#S.UNIP" ); + + public static final Resource S_UNIV = M_MODEL.createResource( "http://www.geonames.org/ontology#S.UNIV" ); + + public static final Resource S_USGE = M_MODEL.createResource( "http://www.geonames.org/ontology#S.USGE" ); + + public static final Resource S_VETF = M_MODEL.createResource( "http://www.geonames.org/ontology#S.VETF" ); + + public static final Resource S_WALL = M_MODEL.createResource( "http://www.geonames.org/ontology#S.WALL" ); + + public static final Resource S_WALLA = M_MODEL.createResource( "http://www.geonames.org/ontology#S.WALLA" ); + + public static final Resource S_WEIR = M_MODEL.createResource( "http://www.geonames.org/ontology#S.WEIR" ); + + public static final Resource S_WHRF = M_MODEL.createResource( "http://www.geonames.org/ontology#S.WHRF" ); + + public static final Resource S_WRCK = M_MODEL.createResource( "http://www.geonames.org/ontology#S.WRCK" ); + + public static final Resource S_WTRW = M_MODEL.createResource( "http://www.geonames.org/ontology#S.WTRW" ); + + public static final Resource S_ZNF = M_MODEL.createResource( "http://www.geonames.org/ontology#S.ZNF" ); + + public static final Resource S_ZOO = M_MODEL.createResource( "http://www.geonames.org/ontology#S.ZOO" ); + + /**

mountain, hill, rock, ...

*/ + public static final Resource T = M_MODEL.createResource( "http://www.geonames.org/ontology#T" ); + + public static final Resource T_ASPH = M_MODEL.createResource( "http://www.geonames.org/ontology#T.ASPH" ); + + public static final Resource T_ATOL = M_MODEL.createResource( "http://www.geonames.org/ontology#T.ATOL" ); + + public static final Resource T_BAR = M_MODEL.createResource( "http://www.geonames.org/ontology#T.BAR" ); + + public static final Resource T_BCH = M_MODEL.createResource( "http://www.geonames.org/ontology#T.BCH" ); + + public static final Resource T_BCHS = M_MODEL.createResource( "http://www.geonames.org/ontology#T.BCHS" ); + + public static final Resource T_BDLD = M_MODEL.createResource( "http://www.geonames.org/ontology#T.BDLD" ); + + public static final Resource T_BLDR = M_MODEL.createResource( "http://www.geonames.org/ontology#T.BLDR" ); + + public static final Resource T_BLHL = M_MODEL.createResource( "http://www.geonames.org/ontology#T.BLHL" ); + + public static final Resource T_BLOW = M_MODEL.createResource( "http://www.geonames.org/ontology#T.BLOW" ); + + public static final Resource T_BNCH = M_MODEL.createResource( "http://www.geonames.org/ontology#T.BNCH" ); + + public static final Resource T_BUTE = M_MODEL.createResource( "http://www.geonames.org/ontology#T.BUTE" ); + + public static final Resource T_CAPE = M_MODEL.createResource( "http://www.geonames.org/ontology#T.CAPE" ); + + public static final Resource T_CFT = M_MODEL.createResource( "http://www.geonames.org/ontology#T.CFT" ); + + public static final Resource T_CLDA = M_MODEL.createResource( "http://www.geonames.org/ontology#T.CLDA" ); + + public static final Resource T_CLF = M_MODEL.createResource( "http://www.geonames.org/ontology#T.CLF" ); + + public static final Resource T_CNYN = M_MODEL.createResource( "http://www.geonames.org/ontology#T.CNYN" ); + + public static final Resource T_CONE = M_MODEL.createResource( "http://www.geonames.org/ontology#T.CONE" ); + + public static final Resource T_CRDR = M_MODEL.createResource( "http://www.geonames.org/ontology#T.CRDR" ); + + public static final Resource T_CRQ = M_MODEL.createResource( "http://www.geonames.org/ontology#T.CRQ" ); + + public static final Resource T_CRQS = M_MODEL.createResource( "http://www.geonames.org/ontology#T.CRQS" ); + + public static final Resource T_CRTR = M_MODEL.createResource( "http://www.geonames.org/ontology#T.CRTR" ); + + public static final Resource T_CUET = M_MODEL.createResource( "http://www.geonames.org/ontology#T.CUET" ); + + public static final Resource T_DLTA = M_MODEL.createResource( "http://www.geonames.org/ontology#T.DLTA" ); + + public static final Resource T_DPR = M_MODEL.createResource( "http://www.geonames.org/ontology#T.DPR" ); + + public static final Resource T_DSRT = M_MODEL.createResource( "http://www.geonames.org/ontology#T.DSRT" ); + + public static final Resource T_DUNE = M_MODEL.createResource( "http://www.geonames.org/ontology#T.DUNE" ); + + public static final Resource T_DVD = M_MODEL.createResource( "http://www.geonames.org/ontology#T.DVD" ); + + public static final Resource T_ERG = M_MODEL.createResource( "http://www.geonames.org/ontology#T.ERG" ); + + public static final Resource T_FAN = M_MODEL.createResource( "http://www.geonames.org/ontology#T.FAN" ); + + public static final Resource T_FORD = M_MODEL.createResource( "http://www.geonames.org/ontology#T.FORD" ); + + public static final Resource T_FSR = M_MODEL.createResource( "http://www.geonames.org/ontology#T.FSR" ); + + public static final Resource T_GAP = M_MODEL.createResource( "http://www.geonames.org/ontology#T.GAP" ); + + public static final Resource T_GRGE = M_MODEL.createResource( "http://www.geonames.org/ontology#T.GRGE" ); + + public static final Resource T_HDLD = M_MODEL.createResource( "http://www.geonames.org/ontology#T.HDLD" ); + + public static final Resource T_HLL = M_MODEL.createResource( "http://www.geonames.org/ontology#T.HLL" ); + + public static final Resource T_HLLS = M_MODEL.createResource( "http://www.geonames.org/ontology#T.HLLS" ); + + public static final Resource T_HMCK = M_MODEL.createResource( "http://www.geonames.org/ontology#T.HMCK" ); + + public static final Resource T_HMDA = M_MODEL.createResource( "http://www.geonames.org/ontology#T.HMDA" ); + + public static final Resource T_INTF = M_MODEL.createResource( "http://www.geonames.org/ontology#T.INTF" ); + + public static final Resource T_ISL = M_MODEL.createResource( "http://www.geonames.org/ontology#T.ISL" ); + + public static final Resource T_ISLET = M_MODEL.createResource( "http://www.geonames.org/ontology#T.ISLET" ); + + public static final Resource T_ISLF = M_MODEL.createResource( "http://www.geonames.org/ontology#T.ISLF" ); + + public static final Resource T_ISLM = M_MODEL.createResource( "http://www.geonames.org/ontology#T.ISLM" ); + + public static final Resource T_ISLS = M_MODEL.createResource( "http://www.geonames.org/ontology#T.ISLS" ); + + public static final Resource T_ISLT = M_MODEL.createResource( "http://www.geonames.org/ontology#T.ISLT" ); + + public static final Resource T_ISLX = M_MODEL.createResource( "http://www.geonames.org/ontology#T.ISLX" ); + + public static final Resource T_ISTH = M_MODEL.createResource( "http://www.geonames.org/ontology#T.ISTH" ); + + public static final Resource T_KRST = M_MODEL.createResource( "http://www.geonames.org/ontology#T.KRST" ); + + public static final Resource T_LAVA = M_MODEL.createResource( "http://www.geonames.org/ontology#T.LAVA" ); + + public static final Resource T_LEV = M_MODEL.createResource( "http://www.geonames.org/ontology#T.LEV" ); + + public static final Resource T_MESA = M_MODEL.createResource( "http://www.geonames.org/ontology#T.MESA" ); + + public static final Resource T_MND = M_MODEL.createResource( "http://www.geonames.org/ontology#T.MND" ); + + public static final Resource T_MRN = M_MODEL.createResource( "http://www.geonames.org/ontology#T.MRN" ); + + public static final Resource T_MT = M_MODEL.createResource( "http://www.geonames.org/ontology#T.MT" ); + + public static final Resource T_MTS = M_MODEL.createResource( "http://www.geonames.org/ontology#T.MTS" ); + + public static final Resource T_NKM = M_MODEL.createResource( "http://www.geonames.org/ontology#T.NKM" ); + + public static final Resource T_NTK = M_MODEL.createResource( "http://www.geonames.org/ontology#T.NTK" ); + + public static final Resource T_NTKS = M_MODEL.createResource( "http://www.geonames.org/ontology#T.NTKS" ); + + public static final Resource T_PAN = M_MODEL.createResource( "http://www.geonames.org/ontology#T.PAN" ); + + public static final Resource T_PANS = M_MODEL.createResource( "http://www.geonames.org/ontology#T.PANS" ); + + public static final Resource T_PASS = M_MODEL.createResource( "http://www.geonames.org/ontology#T.PASS" ); + + public static final Resource T_PEN = M_MODEL.createResource( "http://www.geonames.org/ontology#T.PEN" ); + + public static final Resource T_PENX = M_MODEL.createResource( "http://www.geonames.org/ontology#T.PENX" ); + + public static final Resource T_PK = M_MODEL.createResource( "http://www.geonames.org/ontology#T.PK" ); + + public static final Resource T_PKS = M_MODEL.createResource( "http://www.geonames.org/ontology#T.PKS" ); + + public static final Resource T_PLAT = M_MODEL.createResource( "http://www.geonames.org/ontology#T.PLAT" ); + + public static final Resource T_PLATX = M_MODEL.createResource( "http://www.geonames.org/ontology#T.PLATX" ); + + public static final Resource T_PLDR = M_MODEL.createResource( "http://www.geonames.org/ontology#T.PLDR" ); + + public static final Resource T_PLN = M_MODEL.createResource( "http://www.geonames.org/ontology#T.PLN" ); + + public static final Resource T_PLNX = M_MODEL.createResource( "http://www.geonames.org/ontology#T.PLNX" ); + + public static final Resource T_PROM = M_MODEL.createResource( "http://www.geonames.org/ontology#T.PROM" ); + + public static final Resource T_PT = M_MODEL.createResource( "http://www.geonames.org/ontology#T.PT" ); + + public static final Resource T_PTS = M_MODEL.createResource( "http://www.geonames.org/ontology#T.PTS" ); + + public static final Resource T_RDGB = M_MODEL.createResource( "http://www.geonames.org/ontology#T.RDGB" ); + + public static final Resource T_RDGE = M_MODEL.createResource( "http://www.geonames.org/ontology#T.RDGE" ); + + public static final Resource T_REG = M_MODEL.createResource( "http://www.geonames.org/ontology#T.REG" ); + + public static final Resource T_RK = M_MODEL.createResource( "http://www.geonames.org/ontology#T.RK" ); + + public static final Resource T_RKFL = M_MODEL.createResource( "http://www.geonames.org/ontology#T.RKFL" ); + + public static final Resource T_RKS = M_MODEL.createResource( "http://www.geonames.org/ontology#T.RKS" ); + + public static final Resource T_SAND = M_MODEL.createResource( "http://www.geonames.org/ontology#T.SAND" ); + + public static final Resource T_SBED = M_MODEL.createResource( "http://www.geonames.org/ontology#T.SBED" ); + + public static final Resource T_SCRP = M_MODEL.createResource( "http://www.geonames.org/ontology#T.SCRP" ); + + public static final Resource T_SDL = M_MODEL.createResource( "http://www.geonames.org/ontology#T.SDL" ); + + public static final Resource T_SHOR = M_MODEL.createResource( "http://www.geonames.org/ontology#T.SHOR" ); + + public static final Resource T_SINK = M_MODEL.createResource( "http://www.geonames.org/ontology#T.SINK" ); + + public static final Resource T_SLID = M_MODEL.createResource( "http://www.geonames.org/ontology#T.SLID" ); + + public static final Resource T_SLP = M_MODEL.createResource( "http://www.geonames.org/ontology#T.SLP" ); + + public static final Resource T_SPIT = M_MODEL.createResource( "http://www.geonames.org/ontology#T.SPIT" ); + + public static final Resource T_SPUR = M_MODEL.createResource( "http://www.geonames.org/ontology#T.SPUR" ); + + public static final Resource T_TAL = M_MODEL.createResource( "http://www.geonames.org/ontology#T.TAL" ); + + public static final Resource T_TRGD = M_MODEL.createResource( "http://www.geonames.org/ontology#T.TRGD" ); + + public static final Resource T_TRR = M_MODEL.createResource( "http://www.geonames.org/ontology#T.TRR" ); + + public static final Resource T_UPLD = M_MODEL.createResource( "http://www.geonames.org/ontology#T.UPLD" ); + + public static final Resource T_VAL = M_MODEL.createResource( "http://www.geonames.org/ontology#T.VAL" ); + + public static final Resource T_VALG = M_MODEL.createResource( "http://www.geonames.org/ontology#T.VALG" ); + + public static final Resource T_VALS = M_MODEL.createResource( "http://www.geonames.org/ontology#T.VALS" ); + + public static final Resource T_VALX = M_MODEL.createResource( "http://www.geonames.org/ontology#T.VALX" ); + + public static final Resource T_VLC = M_MODEL.createResource( "http://www.geonames.org/ontology#T.VLC" ); + + /**

undersea

*/ + public static final Resource U = M_MODEL.createResource( "http://www.geonames.org/ontology#U" ); + + public static final Resource U_APNU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.APNU" ); + + public static final Resource U_ARCU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.ARCU" ); + + public static final Resource U_ARRU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.ARRU" ); + + public static final Resource U_BDLU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.BDLU" ); + + public static final Resource U_BKSU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.BKSU" ); + + public static final Resource U_BNCU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.BNCU" ); + + public static final Resource U_BNKU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.BNKU" ); + + public static final Resource U_BSNU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.BSNU" ); + + public static final Resource U_CDAU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.CDAU" ); + + public static final Resource U_CNSU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.CNSU" ); + + public static final Resource U_CNYU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.CNYU" ); + + public static final Resource U_CRSU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.CRSU" ); + + public static final Resource U_DEPU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.DEPU" ); + + public static final Resource U_EDGU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.EDGU" ); + + public static final Resource U_ESCU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.ESCU" ); + + public static final Resource U_FANU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.FANU" ); + + public static final Resource U_FLTU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.FLTU" ); + + public static final Resource U_FRKU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.FRKU" ); + + public static final Resource U_FRSU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.FRSU" ); + + public static final Resource U_FRZU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.FRZU" ); + + public static final Resource U_FURU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.FURU" ); + + public static final Resource U_GAPU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.GAPU" ); + + public static final Resource U_GLYU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.GLYU" ); + + public static final Resource U_HLLU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.HLLU" ); + + public static final Resource U_HLSU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.HLSU" ); + + public static final Resource U_HOLU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.HOLU" ); + + public static final Resource U_KNLU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.KNLU" ); + + public static final Resource U_KNSU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.KNSU" ); + + public static final Resource U_LDGU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.LDGU" ); + + public static final Resource U_LEVU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.LEVU" ); + + public static final Resource U_MDVU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.MDVU" ); + + public static final Resource U_MESU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.MESU" ); + + public static final Resource U_MNDU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.MNDU" ); + + public static final Resource U_MOTU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.MOTU" ); + + public static final Resource U_MTSU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.MTSU" ); + + public static final Resource U_MTU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.MTU" ); + + public static final Resource U_PKSU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.PKSU" ); + + public static final Resource U_PKU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.PKU" ); + + public static final Resource U_PLFU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.PLFU" ); + + public static final Resource U_PLNU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.PLNU" ); + + public static final Resource U_PLTU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.PLTU" ); + + public static final Resource U_PNLU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.PNLU" ); + + public static final Resource U_PRVU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.PRVU" ); + + public static final Resource U_RAVU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.RAVU" ); + + public static final Resource U_RDGU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.RDGU" ); + + public static final Resource U_RDSU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.RDSU" ); + + public static final Resource U_RFSU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.RFSU" ); + + public static final Resource U_RFU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.RFU" ); + + public static final Resource U_RISU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.RISU" ); + + public static final Resource U_RMPU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.RMPU" ); + + public static final Resource U_RNGU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.RNGU" ); + + public static final Resource U_SCNU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.SCNU" ); + + public static final Resource U_SCSU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.SCSU" ); + + public static final Resource U_SDLU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.SDLU" ); + + public static final Resource U_SHFU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.SHFU" ); + + public static final Resource U_SHLU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.SHLU" ); + + public static final Resource U_SHSU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.SHSU" ); + + public static final Resource U_SHVU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.SHVU" ); + + public static final Resource U_SILU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.SILU" ); + + public static final Resource U_SLPU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.SLPU" ); + + public static final Resource U_SMSU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.SMSU" ); + + public static final Resource U_SMU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.SMU" ); + + public static final Resource U_SPRU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.SPRU" ); + + public static final Resource U_TERU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.TERU" ); + + public static final Resource U_TMSU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.TMSU" ); + + public static final Resource U_TMTU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.TMTU" ); + + public static final Resource U_TNGU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.TNGU" ); + + public static final Resource U_TRGU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.TRGU" ); + + public static final Resource U_TRNU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.TRNU" ); + + public static final Resource U_VALU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.VALU" ); + + public static final Resource U_VLSU = M_MODEL.createResource( "http://www.geonames.org/ontology#U.VLSU" ); + + /**

forest, heath, ...

*/ + public static final Resource V = M_MODEL.createResource( "http://www.geonames.org/ontology#V" ); + + public static final Resource V_BUSH = M_MODEL.createResource( "http://www.geonames.org/ontology#V.BUSH" ); + + public static final Resource V_CULT = M_MODEL.createResource( "http://www.geonames.org/ontology#V.CULT" ); + + public static final Resource V_FRST = M_MODEL.createResource( "http://www.geonames.org/ontology#V.FRST" ); + + public static final Resource V_FRSTF = M_MODEL.createResource( "http://www.geonames.org/ontology#V.FRSTF" ); + + public static final Resource V_GRSLD = M_MODEL.createResource( "http://www.geonames.org/ontology#V.GRSLD" ); + + public static final Resource V_GRVC = M_MODEL.createResource( "http://www.geonames.org/ontology#V.GRVC" ); + + public static final Resource V_GRVO = M_MODEL.createResource( "http://www.geonames.org/ontology#V.GRVO" ); + + public static final Resource V_GRVP = M_MODEL.createResource( "http://www.geonames.org/ontology#V.GRVP" ); + + public static final Resource V_GRVPN = M_MODEL.createResource( "http://www.geonames.org/ontology#V.GRVPN" ); + + public static final Resource V_HTH = M_MODEL.createResource( "http://www.geonames.org/ontology#V.HTH" ); + + public static final Resource V_MDW = M_MODEL.createResource( "http://www.geonames.org/ontology#V.MDW" ); + + public static final Resource V_OCH = M_MODEL.createResource( "http://www.geonames.org/ontology#V.OCH" ); + + public static final Resource V_SCRB = M_MODEL.createResource( "http://www.geonames.org/ontology#V.SCRB" ); + + public static final Resource V_TREE = M_MODEL.createResource( "http://www.geonames.org/ontology#V.TREE" ); + + public static final Resource V_TUND = M_MODEL.createResource( "http://www.geonames.org/ontology#V.TUND" ); + + public static final Resource V_VIN = M_MODEL.createResource( "http://www.geonames.org/ontology#V.VIN" ); + + public static final Resource V_VINS = M_MODEL.createResource( "http://www.geonames.org/ontology#V.VINS" ); + +} diff --git a/orcid-api-common/src/main/java/org/orcid/api/common/writer/rdf/PAV.java b/orcid-api-common/src/main/java/org/orcid/api/common/writer/rdf/PAV.java new file mode 100644 index 00000000000..7525af5ce2a --- /dev/null +++ b/orcid-api-common/src/main/java/org/orcid/api/common/writer/rdf/PAV.java @@ -0,0 +1,273 @@ +/* CVS $Id: $ */ +package org.orcid.api.common.writer.rdf.vocabs; +import org.apache.jena.rdf.model.*; + +/** + * Vocabulary definitions from C:\Users\angel\Documents\ORCID\development\tmp\deleteme\ORCID-Source\orcid-api-common/src/main/vocabs/pav.rdf + * @author Auto-generated by schemagen on 26 Mar 2026 15:47 + */ +public class PAV { + /**

The RDF model that holds the vocabulary terms

*/ + private static final Model M_MODEL = ModelFactory.createDefaultModel(); + + /**

The namespace of the vocabulary as a string

*/ + public static final String NS = "http://purl.org/pav/"; + + /**

The namespace of the vocabulary as a string

+ * @return namespace as String + * @see #NS */ + public static String getURI() {return NS;} + + /**

The namespace of the vocabulary as a resource

*/ + public static final Resource NAMESPACE = M_MODEL.createResource( NS ); + + /**

The ontology's owl:versionInfo as a string

*/ + public static final String VERSION_INFO = "2.1.1"; + + /**

An agent that originated or gave existence to the work that is expressed by + * the digital resource. The author of the content of a resource may be different + * from the creator of the resource representation (although they are often the + * same). See pav:createdBy for a discussion. The date of authoring can be expressed + * using pav:authoredOn - note however in the case of multiple authors that there + * is no relationship in PAV identifying which agent contributed when or what. + * If capturing such lineage is desired, it should be additionally expressed + * using activity-centric provenance vocabularies, for instance with prov:wasGeneratedBy + * and prov:qualifiedAssocation.

+ */ + public static final Property authoredBy = M_MODEL.createProperty( "http://purl.org/pav/authoredBy" ); + + /**

The date this resource was authored. pav:authoredBy gives the authoring agent. + * Note that pav:authoredOn is different from pav:createdOn, although they are + * often the same. See pav:createdBy for a discussion.

+ */ + public static final Property authoredOn = M_MODEL.createProperty( "http://purl.org/pav/authoredOn" ); + + /**

The resource was contributed to by the given agent. The agent provided any + * sort of help in conceiving the work that is expressed by the digital artifact. + * Superproperty of pav:authoredBy and pav:curatedBy. Note that as pav:contributedBy + * identifies only agents that contributed to the work, knowledge or intellectual + * property, and not agents that made the digital artifact or representation + * (pav:createdBy), thus this property can be considered more precise than dct:contributor. + * See pav:createdBy for a discussion. The date of contribution can be expressed + * using pav:contributedOn - note however in the case of multiple contributors + * that there is no relationship in PAV identifying which agent contributed when + * or what. If capturing such lineage is desired, it should be additionally expressed + * using activity-centric provenance vocabularies, for instance with prov:wasGeneratedBy + * and prov:qualifiedAssocation.

+ */ + public static final Property contributedBy = M_MODEL.createProperty( "http://purl.org/pav/contributedBy" ); + + /**

The date this resource was contributed to. pav:contributedBy provides the + * agent(s) that contributed.

+ */ + public static final Property contributedOn = M_MODEL.createProperty( "http://purl.org/pav/contributedOn" ); + + /**

The geo-location of the agents when creating the resource (pav:createdBy). + * For instance a photographer takes a picture of the Eiffel Tower while standing + * in front of it.

+ */ + public static final Property createdAt = M_MODEL.createProperty( "http://purl.org/pav/createdAt" ); + + /**

An agent primary responsible for making the digital artifact or resource representation. + * This property is distinct from forming the content, which is indicated with + * pav:contributedBy or its subproperties; pav:authoredBy, which identifies who + * authored the knowledge expressed by this resource; and pav:curatedBy, which + * identifies who curated the knowledge into its current form. pav:createdBy + * is more specific than dct:createdBy - which might or might not be interpreted + * to cover this creator. For instance, the author wrote 'this species has bigger + * wings than normal' in his log book. The curator, going through the log book + * and identifying important knowledge, formalizes this as 'locus perculus has + * wingspan > 0.5m'. The creator enters this knowledge as a digital resource + * in the knowledge system, thus creating the digital artifact (say as JSON, + * RDF, XML or HTML). A different example is a news article. pav:authoredBy indicates + * the journalist who wrote the article. pav:contributedBy can indicate the artist + * who added an illustration. pav:curatedBy can indicate the editor who made + * the article conform to the news paper's style. pav:createdBy can indicate + * who put the article on the web site. The software tool used by the creator + * to make the digital resource (say Protege, Wordpress or OpenOffice) can be + * indicated with pav:createdWith. The date the digital resource was created + * can be indicated with pav:createdOn. The location the agent was at when creating + * the digital resource can be made using pav:createdAt.

+ */ + public static final Property createdBy = M_MODEL.createProperty( "http://purl.org/pav/createdBy" ); + + /**

The date of creation of the resource. pav:createdBy provides the agent(s) + * that created the resource.

+ */ + public static final Property createdOn = M_MODEL.createProperty( "http://purl.org/pav/createdOn" ); + + /**

The software/tool used by the creator (pav:createdBy) when making the digital + * resource, for instance a word processor or an annotation tool. A more independent + * software agent that creates the resource without direct interaction by a human + * creator should instead should instead by indicated using pav:createdBy.

+ */ + public static final Property createdWith = M_MODEL.createProperty( "http://purl.org/pav/createdWith" ); + + /**

An agent specialist responsible for shaping the expression in an appropriate + * format. Often the primary agent responsible for ensuring the quality of the + * representation. The curator may be different from the creator of the author + * (pav:authoredBy) and the creator of the digital resource (pav:createdBy). + * The date of curating can be expressed using pav:curatedOn - note however in + * the case of multiple curators that there is no relationship in PAV identifying + * which agent contributed when or what. If capturing such lineage is desired, + * it should be additionally expressed using activity-centric provenance vocabularies, + * for instance with prov:wasGeneratedBy and prov:qualifiedAssocation.

+ */ + public static final Property curatedBy = M_MODEL.createProperty( "http://purl.org/pav/curatedBy" ); + + /**

The date this resource was curated. pav:curatedBy gives the agent(s) that + * performed the curation.

+ */ + public static final Property curatedOn = M_MODEL.createProperty( "http://purl.org/pav/curatedOn" ); + + /**

Provided for backwards compatibility with PAV 1.2 only. Use instead the inverse + * pav:curatedBy.

+ */ + public static final Property curates = M_MODEL.createProperty( "http://purl.org/pav/curates" ); + + /**

Derived from a different resource. Derivation conserns itself with derived + * knowledge. If this resource has the same content as the other resource, but + * has simply been transcribed to fit a different model (like XML -> RDF or + * SQL -> CVS), use pav:importedFrom. If a resource was simply retrieved, + * use pav:retrievedFrom. If the content has however been further refined or + * modified, pav:derivedFrom should be used. Details about who performed the + * derivation may be indicated with pav:contributedBy and its subproperties.

+ */ + public static final Property derivedFrom = M_MODEL.createProperty( "http://purl.org/pav/derivedFrom" ); + + /**

An entity responsible for importing the data. The importer is usually a software + * entity which has done the transcription from the original source. Note that + * pav:importedBy may overlap with pav:createdWith. The source for the import + * should be given with pav:importedFrom. The time of the import should be given + * with pav:importedOn. See pav:importedFrom for a discussion of import vs. retrieve + * vs. derived.

+ */ + public static final Property importedBy = M_MODEL.createProperty( "http://purl.org/pav/importedBy" ); + + /**

The original source of imported information. Import means that the content + * has been preserved, but transcribed somehow, for instance to fit a different + * representation model. Examples of import are when the original was JSON and + * the current resource is RDF, or where the original was an document scan, and + * this resource is the plain text found through OCR. The imported resource does + * not have to be complete, but should be consistent with the knowledge conveyed + * by the original resource. If additional knowledge has been contributed, pav:derivedFrom + * would be more appropriate. If the resource has been copied verbatim from the + * original representation (e.g. downloaded), use pav:retrievedFrom. To indicate + * which agent(s) performed the import, use pav:importedBy. Use pav:importedOn + * to indicate when it happened.

+ */ + public static final Property importedFrom = M_MODEL.createProperty( "http://purl.org/pav/importedFrom" ); + + /**

The date this resource was imported from a source (pav:importedFrom). Note + * that pav:importedOn may overlap with pav:createdOn, but in cases where they + * differ, the import time indicates the time of the retrieval and transcription + * of the original source, while the creation time indicates when the final resource + * was made, for instance after user approval. If the source is later reimported, + * this should be indicated with pav:lastRefreshedOn. The source of the import + * should be given with pav:importedFrom. The agent that performed the import + * should be given with pav:importedBy. See pav:importedFrom for a discussion + * about import vs. retrieval.

+ */ + public static final Property importedOn = M_MODEL.createProperty( "http://purl.org/pav/importedOn" ); + + /**

The date of the last re-import of the resource. This property is used in addition + * to pav:importedOn if this version has been updated due to a re-import. If + * the re-import created a new resource rather than refreshing an existing, then + * pav:importedOn should be used together with pav:previousVersion.

+ */ + public static final Property lastRefreshedOn = M_MODEL.createProperty( "http://purl.org/pav/lastRefreshedOn" ); + + /**

The date of the last update of the resource. An update is a change which did + * not warrant making a new resource related using pav:previousVersion, for instance + * correcting a spelling mistake.

+ */ + public static final Property lastUpdateOn = M_MODEL.createProperty( "http://purl.org/pav/lastUpdateOn" ); + + /**

The previous version of a resource in a lineage. For instance a news article + * updated to correct factual information would point to the previous version + * of the article with pav:previousVersion. If however the content has significantly + * changed so that the two resources no longer share lineage (say a new news + * article that talks about the same facts), they should be related using pav:derivedFrom. + * A version number of this resource can be provided using the data property + * pav:version.

+ */ + public static final Property previousVersion = M_MODEL.createProperty( "http://purl.org/pav/previousVersion" ); + + /**

The original provider of the encoded information (e.g. PubMed, UniProt, Science + * Commons). The provider might not coincide with the dct:publisher, which would + * describe the current publisher of the resource. For instance if the resource + * was retrieved, imported or derived from a source, that source was published + * by the original provider. pav:providedBy provides a shortcut to indicate the + * original provider on the new resource.

+ */ + public static final Property providedBy = M_MODEL.createProperty( "http://purl.org/pav/providedBy" ); + + /**

An entity responsible for retrieving the data from an external source. The + * retrieving agent is usually a software entity, which has done the retrieval + * from the original source without performing any transcription. The source + * that was retrieved should be given with pav:retrievedFrom. The time of the + * retrieval should be indicated using pav:retrievedOn. See pav:importedFrom + * for a discussion of import vs. retrieve vs. derived.

+ */ + public static final Property retrievedBy = M_MODEL.createProperty( "http://purl.org/pav/retrievedBy" ); + + /**

The URI where a resource has been retrieved from. Retrieval indicates that + * this resource has the same representation as the original resource. If the + * resource has been somewhat transformed, use pav:importedFrom instead. The + * time of the retrieval should be indicated using pav:retrievedOn. The agent + * may be indicated with pav:retrievedBy.

+ */ + public static final Property retrievedFrom = M_MODEL.createProperty( "http://purl.org/pav/retrievedFrom" ); + + /**

The date the source for this resource was retrieved. The source that was retrieved + * should be indicated with pav:retrievedFrom. The agent that performed the retrieval + * may be specified with pav:retrievedBy.

+ */ + public static final Property retrievedOn = M_MODEL.createProperty( "http://purl.org/pav/retrievedOn" ); + + /**

The resource is related to a given source which was accessed or consulted + * (but not retrieved, imported or derived from). This access can be detailed + * with pav:sourceAccessedBy and pav:sourceAccessedOn. For instance, a curator + * (pav:curatedBy) might have consulted figures in a published paper to confirm + * that a dataset was correctly pav:importedFrom the paper's supplementary CSV + * file.

+ */ + public static final Property sourceAccessedAt = M_MODEL.createProperty( "http://purl.org/pav/sourceAccessedAt" ); + + /**

The resource is related to a source which was accessed or consulted by the + * given agent. The source(s) should be specified using pav:sourceAccessedAt, + * and the time with pav:sourceAccessedOn. For instance, the given agent could + * be a curator (also pav:curatedBy) which consulted figures in a published paper + * to confirm that a dataset was correctly pav:importedFrom the paper's supplementary + * CSV file.

+ */ + public static final Property sourceAccessedBy = M_MODEL.createProperty( "http://purl.org/pav/sourceAccessedBy" ); + + /**

The resource is related to a source which was originally accessed or consulted + * on the given date as part of creating or authoring the resource. The source(s) + * should be specified using pav:sourceAccessedAt. If the source is subsequently + * checked again (say to verify validity), this should be indicated with pav:sourceLastAccessedOn. + * In the case multiple sources being accessed at different times or by different + * agents, PAV does not distinguish who accessed when what. If such details are + * required, they may be provided by additionally using prov:qualifiedInfluence.

+ */ + public static final Property sourceAccessedOn = M_MODEL.createProperty( "http://purl.org/pav/sourceAccessedOn" ); + + /**

The resource is related to a source which was last accessed or consulted on + * the given date. The source(s) should be specified using pav:sourceAccessedAt. + * Usage of this property indicates that the source has been checked previously, + * which the initial time should be indicated with pav:sourceAccessedOn. This + * property can be useful together with pav:lastRefreshedOn or pav:lastUpdateOn + * in order to indicate a re-import or update, but could also be used alone, + * for instance when a source was simply verified and no further action was taken + * for the resource,

+ */ + public static final Property sourceLastAccessedOn = M_MODEL.createProperty( "http://purl.org/pav/sourceLastAccessedOn" ); + + /**

The version number of a resource. This is a freetext string, typical values + * are "1.5" or "21". The URI identifying the previous version can be provided + * using prov:previousVersion.

+ */ + public static final Property version = M_MODEL.createProperty( "http://purl.org/pav/version" ); + +} diff --git a/orcid-api-common/src/main/java/org/orcid/api/common/writer/rdf/PROV.java b/orcid-api-common/src/main/java/org/orcid/api/common/writer/rdf/PROV.java new file mode 100644 index 00000000000..3d27d7e47ac --- /dev/null +++ b/orcid-api-common/src/main/java/org/orcid/api/common/writer/rdf/PROV.java @@ -0,0 +1,503 @@ +/* CVS $Id: $ */ +package org.orcid.api.common.writer.rdf.vocabs; +import org.apache.jena.rdf.model.*; + +/** + * Vocabulary definitions from C:\Users\angel\Documents\ORCID\development\tmp\deleteme\ORCID-Source\orcid-api-common/src/main/vocabs/prov-o.rdf + * @author Auto-generated by schemagen on 26 Mar 2026 15:47 + */ +public class PROV { + /**

The RDF model that holds the vocabulary terms

*/ + private static final Model M_MODEL = ModelFactory.createDefaultModel(); + + /**

The namespace of the vocabulary as a string

*/ + public static final String NS = "http://www.w3.org/ns/prov#"; + + /**

The namespace of the vocabulary as a string

+ * @return namespace as String + * @see #NS */ + public static String getURI() {return NS;} + + /**

The namespace of the vocabulary as a resource

*/ + public static final Resource NAMESPACE = M_MODEL.createResource( NS ); + + /**

An object property to express the accountability of an agent towards another + * agent. The subordinate agent acted on behalf of the responsible agent in an + * actual activity.

+ */ + public static final Property actedOnBehalfOf = M_MODEL.createProperty( "http://www.w3.org/ns/prov#actedOnBehalfOf" ); + + public static final Property activity = M_MODEL.createProperty( "http://www.w3.org/ns/prov#activity" ); + + public static final Property agent = M_MODEL.createProperty( "http://www.w3.org/ns/prov#agent" ); + + public static final Property alternateOf = M_MODEL.createProperty( "http://www.w3.org/ns/prov#alternateOf" ); + + public static final Property aq = M_MODEL.createProperty( "http://www.w3.org/ns/prov#aq" ); + + /**

The Location of any resource.This property has multiple RDFS domains to suit + * multiple OWL Profiles. See <a href="#owl-profile">PROV-O OWL Profile</a>.

+ */ + public static final Property atLocation = M_MODEL.createProperty( "http://www.w3.org/ns/prov#atLocation" ); + + /**

The time at which an InstantaneousEvent occurred, in the form of xsd:dateTime.

*/ + public static final Property atTime = M_MODEL.createProperty( "http://www.w3.org/ns/prov#atTime" ); + + /**

Classify prov-o terms into three categories, including 'starting-point', 'qualifed', + * and 'extended'. This classification is used by the prov-o html document to + * gently introduce prov-o terms to its users.

+ */ + public static final Property category = M_MODEL.createProperty( "http://www.w3.org/ns/prov#category" ); + + /**

Classify prov-o terms into six components according to prov-dm, including + * 'agents-responsibility', 'alternate', 'annotations', 'collections', 'derivations', + * and 'entities-activities'. This classification is used so that readers of + * prov-o specification can find its correspondence with the prov-dm specification.

+ */ + public static final Property component = M_MODEL.createProperty( "http://www.w3.org/ns/prov#component" ); + + /**

A reference to the principal section of the PROV-CONSTRAINTS document that + * describes this concept.

+ */ + public static final Property constraints = M_MODEL.createProperty( "http://www.w3.org/ns/prov#constraints" ); + + /**

A definition quoted from PROV-DM or PROV-CONSTRAINTS that describes the concept + * expressed with this OWL term.

+ */ + public static final Property definition = M_MODEL.createProperty( "http://www.w3.org/ns/prov#definition" ); + + /**

A reference to the principal section of the PROV-DM document that describes + * this concept.

+ */ + public static final Property dm = M_MODEL.createProperty( "http://www.w3.org/ns/prov#dm" ); + + /**

A note by the OWL development team about how this term expresses the PROV-DM + * concept, or how it should be used in context of semantic web or linked data.

+ */ + public static final Property editorialNote = M_MODEL.createProperty( "http://www.w3.org/ns/prov#editorialNote" ); + + /**

When the prov-o term does not have a definition drawn from prov-dm, and the + * prov-o editor provides one.

+ */ + public static final Property editorsDefinition = M_MODEL.createProperty( "http://www.w3.org/ns/prov#editorsDefinition" ); + + /**

The time at which an activity ended. See also prov:startedAtTime.

*/ + public static final Property endedAtTime = M_MODEL.createProperty( "http://www.w3.org/ns/prov#endedAtTime" ); + + public static final Property entity = M_MODEL.createProperty( "http://www.w3.org/ns/prov#entity" ); + + public static final Property generated = M_MODEL.createProperty( "http://www.w3.org/ns/prov#generated" ); + + /**

The time at which an entity was completely created and is available for use.

*/ + public static final Property generatedAtTime = M_MODEL.createProperty( "http://www.w3.org/ns/prov#generatedAtTime" ); + + /**

This property has multiple RDFS domains to suit multiple OWL Profiles. See + * <a href="#owl-profile">PROV-O OWL Profile</a>.The _optional_ Activity + * of an Influence, which used, generated, invalidated, or was the responsibility + * of some Entity. This property is _not_ used by ActivityInfluence (use prov:activity + * instead).

+ */ + public static final Property hadActivity = M_MODEL.createProperty( "http://www.w3.org/ns/prov#hadActivity" ); + + /**

The _optional_ Generation involved in an Entity's Derivation.

*/ + public static final Property hadGeneration = M_MODEL.createProperty( "http://www.w3.org/ns/prov#hadGeneration" ); + + public static final Property hadMember = M_MODEL.createProperty( "http://www.w3.org/ns/prov#hadMember" ); + + /**

The _optional_ Plan adopted by an Agent in Association with some Activity. + * Plan specifications are out of the scope of this specification.

+ */ + public static final Property hadPlan = M_MODEL.createProperty( "http://www.w3.org/ns/prov#hadPlan" ); + + public static final Property hadPrimarySource = M_MODEL.createProperty( "http://www.w3.org/ns/prov#hadPrimarySource" ); + + /**

The _optional_ Role that an Entity assumed in the context of an Activity. + * For example, :baking prov:used :spoon; prov:qualified [ a prov:Usage; prov:entity + * :spoon; prov:hadRole roles:mixing_implement ].This property has multiple RDFS + * domains to suit multiple OWL Profiles. See <a href="#owl-profile">PROV-O + * OWL Profile</a>.

+ */ + public static final Property hadRole = M_MODEL.createProperty( "http://www.w3.org/ns/prov#hadRole" ); + + /**

The _optional_ Usage involved in an Entity's Derivation.

*/ + public static final Property hadUsage = M_MODEL.createProperty( "http://www.w3.org/ns/prov#hadUsage" ); + + public static final Property influenced = M_MODEL.createProperty( "http://www.w3.org/ns/prov#influenced" ); + + /**

Subproperties of prov:influencer are used to cite the object of an unqualified + * PROV-O triple whose predicate is a subproperty of prov:wasInfluencedBy (e.g. + * prov:used, prov:wasGeneratedBy). prov:influencer is used much like rdf:object + * is used.

+ */ + public static final Property influencer = M_MODEL.createProperty( "http://www.w3.org/ns/prov#influencer" ); + + public static final Property invalidated = M_MODEL.createProperty( "http://www.w3.org/ns/prov#invalidated" ); + + /**

The time at which an entity was invalidated (i.e., no longer usable).

*/ + public static final Property invalidatedAtTime = M_MODEL.createProperty( "http://www.w3.org/ns/prov#invalidatedAtTime" ); + + /**

PROV-O does not define all property inverses. The directionalities defined + * in PROV-O should be given preference over those not defined. However, if users + * wish to name the inverse of a PROV-O property, the local name given by prov:inverse + * should be used.

+ */ + public static final Property inverse = M_MODEL.createProperty( "http://www.w3.org/ns/prov#inverse" ); + + /**

A reference to the principal section of the PROV-DM document that describes + * this concept.

+ */ + public static final Property n = M_MODEL.createProperty( "http://www.w3.org/ns/prov#n" ); + + /**

If this Activity prov:wasAssociatedWith Agent :ag, then it can qualify the + * Association using prov:qualifiedAssociation [ a prov:Association; prov:agent + * :ag; :foo :bar ].

+ */ + public static final Property qualifiedAssociation = M_MODEL.createProperty( "http://www.w3.org/ns/prov#qualifiedAssociation" ); + + /**

If this Entity prov:wasAttributedTo Agent :ag, then it can qualify how it + * was influenced using prov:qualifiedAttribution [ a prov:Attribution; prov:agent + * :ag; :foo :bar ].

+ */ + public static final Property qualifiedAttribution = M_MODEL.createProperty( "http://www.w3.org/ns/prov#qualifiedAttribution" ); + + /**

If this Activity prov:wasInformedBy Activity :a, then it can qualify how it + * was influenced using prov:qualifiedCommunication [ a prov:Communication; prov:activity + * :a; :foo :bar ].

+ */ + public static final Property qualifiedCommunication = M_MODEL.createProperty( "http://www.w3.org/ns/prov#qualifiedCommunication" ); + + /**

If this Agent prov:actedOnBehalfOf Agent :ag, then it can qualify how with + * prov:qualifiedResponsibility [ a prov:Responsibility; prov:agent :ag; :foo + * :bar ].

+ */ + public static final Property qualifiedDelegation = M_MODEL.createProperty( "http://www.w3.org/ns/prov#qualifiedDelegation" ); + + /**

If this Entity prov:wasDerivedFrom Entity :e, then it can qualify how it was + * derived using prov:qualifiedDerivation [ a prov:Derivation; prov:entity :e; + * :foo :bar ].

+ */ + public static final Property qualifiedDerivation = M_MODEL.createProperty( "http://www.w3.org/ns/prov#qualifiedDerivation" ); + + /**

If this Activity prov:wasEndedBy Entity :e1, then it can qualify how it was + * ended using prov:qualifiedEnd [ a prov:End; prov:entity :e1; :foo :bar ].

+ */ + public static final Property qualifiedEnd = M_MODEL.createProperty( "http://www.w3.org/ns/prov#qualifiedEnd" ); + + /**

This annotation property links a subproperty of prov:wasInfluencedBy with + * the subclass of prov:Influence and the qualifying property that are used to + * qualify it. Example annotation: prov:wasGeneratedBy prov:qualifiedForm prov:qualifiedGeneration, + * prov:Generation . Then this unqualified assertion: :entity1 prov:wasGeneratedBy + * :activity1 . can be qualified by adding: :entity1 prov:qualifiedGeneration + * :entity1Gen . :entity1Gen a prov:Generation, prov:Influence; prov:activity + * :activity1; :customValue 1337 . Note how the value of the unqualified influence + * (prov:wasGeneratedBy :activity1) is mirrored as the value of the prov:activity + * (or prov:entity, or prov:agent) property on the influence class.

+ */ + public static final Property qualifiedForm = M_MODEL.createProperty( "http://www.w3.org/ns/prov#qualifiedForm" ); + + /**

If this Activity prov:generated Entity :e, then it can qualify how it performed + * the Generation using prov:qualifiedGeneration [ a prov:Generation; prov:entity + * :e; :foo :bar ].

+ */ + public static final Property qualifiedGeneration = M_MODEL.createProperty( "http://www.w3.org/ns/prov#qualifiedGeneration" ); + + /**

Because prov:qualifiedInfluence is a broad relation, the more specific relations + * (qualifiedCommunication, qualifiedDelegation, qualifiedEnd, etc.) should be + * used when applicable.

+ */ + public static final Property qualifiedInfluence = M_MODEL.createProperty( "http://www.w3.org/ns/prov#qualifiedInfluence" ); + + /**

If this Entity prov:wasInvalidatedBy Activity :a, then it can qualify how + * it was invalidated using prov:qualifiedInvalidation [ a prov:Invalidation; + * prov:activity :a; :foo :bar ].

+ */ + public static final Property qualifiedInvalidation = M_MODEL.createProperty( "http://www.w3.org/ns/prov#qualifiedInvalidation" ); + + /**

If this Entity prov:hadPrimarySource Entity :e, then it can qualify how using + * prov:qualifiedPrimarySource [ a prov:PrimarySource; prov:entity :e; :foo :bar + * ].

+ */ + public static final Property qualifiedPrimarySource = M_MODEL.createProperty( "http://www.w3.org/ns/prov#qualifiedPrimarySource" ); + + /**

If this Entity prov:wasQuotedFrom Entity :e, then it can qualify how using + * prov:qualifiedQuotation [ a prov:Quotation; prov:entity :e; :foo :bar ].

+ */ + public static final Property qualifiedQuotation = M_MODEL.createProperty( "http://www.w3.org/ns/prov#qualifiedQuotation" ); + + /**

If this Entity prov:wasRevisionOf Entity :e, then it can qualify how it was + * revised using prov:qualifiedRevision [ a prov:Revision; prov:entity :e; :foo + * :bar ].

+ */ + public static final Property qualifiedRevision = M_MODEL.createProperty( "http://www.w3.org/ns/prov#qualifiedRevision" ); + + /**

If this Activity prov:wasStartedBy Entity :e1, then it can qualify how it + * was started using prov:qualifiedStart [ a prov:Start; prov:entity :e1; :foo + * :bar ].

+ */ + public static final Property qualifiedStart = M_MODEL.createProperty( "http://www.w3.org/ns/prov#qualifiedStart" ); + + /**

If this Activity prov:used Entity :e, then it can qualify how it used it using + * prov:qualifiedUsage [ a prov:Usage; prov:entity :e; :foo :bar ].

+ */ + public static final Property qualifiedUsage = M_MODEL.createProperty( "http://www.w3.org/ns/prov#qualifiedUsage" ); + + public static final Property sharesDefinitionWith = M_MODEL.createProperty( "http://www.w3.org/ns/prov#sharesDefinitionWith" ); + + public static final Property specializationOf = M_MODEL.createProperty( "http://www.w3.org/ns/prov#specializationOf" ); + + /**

The time at which an activity started. See also prov:endedAtTime.

*/ + public static final Property startedAtTime = M_MODEL.createProperty( "http://www.w3.org/ns/prov#startedAtTime" ); + + /**

Classes and properties used to qualify relationships are annotated with prov:unqualifiedForm + * to indicate the property used to assert an unqualified provenance relation.

+ */ + public static final Property unqualifiedForm = M_MODEL.createProperty( "http://www.w3.org/ns/prov#unqualifiedForm" ); + + /**

A prov:Entity that was used by this prov:Activity. For example, :baking prov:used + * :spoon, :egg, :oven .

+ */ + public static final Property used = M_MODEL.createProperty( "http://www.w3.org/ns/prov#used" ); + + public static final Property value = M_MODEL.createProperty( "http://www.w3.org/ns/prov#value" ); + + /**

An prov:Agent that had some (unspecified) responsibility for the occurrence + * of this prov:Activity.

+ */ + public static final Property wasAssociatedWith = M_MODEL.createProperty( "http://www.w3.org/ns/prov#wasAssociatedWith" ); + + /**

Attribution is the ascribing of an entity to an agent.

*/ + public static final Property wasAttributedTo = M_MODEL.createProperty( "http://www.w3.org/ns/prov#wasAttributedTo" ); + + /**

The more specific subproperties of prov:wasDerivedFrom (i.e., prov:wasQuotedFrom, + * prov:wasRevisionOf, prov:hadPrimarySource) should be used when applicable.

+ */ + public static final Property wasDerivedFrom = M_MODEL.createProperty( "http://www.w3.org/ns/prov#wasDerivedFrom" ); + + /**

End is when an activity is deemed to have ended. An end may refer to an entity, + * known as trigger, that terminated the activity.

+ */ + public static final Property wasEndedBy = M_MODEL.createProperty( "http://www.w3.org/ns/prov#wasEndedBy" ); + + public static final Property wasGeneratedBy = M_MODEL.createProperty( "http://www.w3.org/ns/prov#wasGeneratedBy" ); + + /**

This property has multiple RDFS domains to suit multiple OWL Profiles. See + * <a href="#owl-profile">PROV-O OWL Profile</a>.Because prov:wasInfluencedBy + * is a broad relation, its more specific subproperties (e.g. prov:wasInformedBy, + * prov:actedOnBehalfOf, prov:wasEndedBy, etc.) should be used when applicable.

+ */ + public static final Property wasInfluencedBy = M_MODEL.createProperty( "http://www.w3.org/ns/prov#wasInfluencedBy" ); + + /**

An activity a2 is dependent on or informed by another activity a1, by way + * of some unspecified entity that is generated by a1 and used by a2.

+ */ + public static final Property wasInformedBy = M_MODEL.createProperty( "http://www.w3.org/ns/prov#wasInformedBy" ); + + public static final Property wasInvalidatedBy = M_MODEL.createProperty( "http://www.w3.org/ns/prov#wasInvalidatedBy" ); + + /**

An entity is derived from an original entity by copying, or 'quoting', some + * or all of it.

+ */ + public static final Property wasQuotedFrom = M_MODEL.createProperty( "http://www.w3.org/ns/prov#wasQuotedFrom" ); + + /**

A revision is a derivation that revises an entity into a revised version.

*/ + public static final Property wasRevisionOf = M_MODEL.createProperty( "http://www.w3.org/ns/prov#wasRevisionOf" ); + + /**

Start is when an activity is deemed to have started. A start may refer to + * an entity, known as trigger, that initiated the activity.

+ */ + public static final Property wasStartedBy = M_MODEL.createProperty( "http://www.w3.org/ns/prov#wasStartedBy" ); + + public static final Resource Activity = M_MODEL.createResource( "http://www.w3.org/ns/prov#Activity" ); + + /**

It is not recommended that the type ActivityInfluence be asserted without + * also asserting one of its more specific subclasses.ActivityInfluence provides + * additional descriptions of an Activity's binary influence upon any other kind + * of resource. Instances of ActivityInfluence use the prov:activity property + * to cite the influencing Activity.

+ */ + public static final Resource ActivityInfluence = M_MODEL.createResource( "http://www.w3.org/ns/prov#ActivityInfluence" ); + + public static final Resource Agent = M_MODEL.createResource( "http://www.w3.org/ns/prov#Agent" ); + + /**

It is not recommended that the type AgentInfluence be asserted without also + * asserting one of its more specific subclasses.AgentInfluence provides additional + * descriptions of an Agent's binary influence upon any other kind of resource. + * Instances of AgentInfluence use the prov:agent property to cite the influencing + * Agent.

+ */ + public static final Resource AgentInfluence = M_MODEL.createResource( "http://www.w3.org/ns/prov#AgentInfluence" ); + + /**

An instance of prov:Association provides additional descriptions about the + * binary prov:wasAssociatedWith relation from an prov:Activity to some prov:Agent + * that had some responsiblity for it. For example, :baking prov:wasAssociatedWith + * :baker; prov:qualifiedAssociation [ a prov:Association; prov:agent :baker; + * :foo :bar ].

+ */ + public static final Resource Association = M_MODEL.createResource( "http://www.w3.org/ns/prov#Association" ); + + /**

An instance of prov:Attribution provides additional descriptions about the + * binary prov:wasAttributedTo relation from an prov:Entity to some prov:Agent + * that had some responsible for it. For example, :cake prov:wasAttributedTo + * :baker; prov:qualifiedAttribution [ a prov:Attribution; prov:entity :baker; + * :foo :bar ].

+ */ + public static final Resource Attribution = M_MODEL.createResource( "http://www.w3.org/ns/prov#Attribution" ); + + /**

Note that there are kinds of bundles (e.g. handwritten letters, audio recordings, + * etc.) that are not expressed in PROV-O, but can be still be described by PROV-O.

+ */ + public static final Resource Bundle = M_MODEL.createResource( "http://www.w3.org/ns/prov#Bundle" ); + + public static final Resource Collection = M_MODEL.createResource( "http://www.w3.org/ns/prov#Collection" ); + + /**

An instance of prov:Communication provides additional descriptions about the + * binary prov:wasInformedBy relation from an informed prov:Activity to the prov:Activity + * that informed it. For example, :you_jumping_off_bridge prov:wasInformedBy + * :everyone_else_jumping_off_bridge; prov:qualifiedCommunication [ a prov:Communication; + * prov:activity :everyone_else_jumping_off_bridge; :foo :bar ].

+ */ + public static final Resource Communication = M_MODEL.createResource( "http://www.w3.org/ns/prov#Communication" ); + + /**

An instance of prov:Delegation provides additional descriptions about the + * binary prov:actedOnBehalfOf relation from a performing prov:Agent to some + * prov:Agent for whom it was performed. For example, :mixing prov:wasAssociatedWith + * :toddler . :toddler prov:actedOnBehalfOf :mother; prov:qualifiedDelegation + * [ a prov:Delegation; prov:entity :mother; :foo :bar ].

+ */ + public static final Resource Delegation = M_MODEL.createResource( "http://www.w3.org/ns/prov#Delegation" ); + + /**

An instance of prov:Derivation provides additional descriptions about the + * binary prov:wasDerivedFrom relation from some derived prov:Entity to another + * prov:Entity from which it was derived. For example, :chewed_bubble_gum prov:wasDerivedFrom + * :unwrapped_bubble_gum; prov:qualifiedDerivation [ a prov:Derivation; prov:entity + * :unwrapped_bubble_gum; :foo :bar ].The more specific forms of prov:Derivation + * (i.e., prov:Revision, prov:Quotation, prov:PrimarySource) should be asserted + * if they apply.

+ */ + public static final Resource Derivation = M_MODEL.createResource( "http://www.w3.org/ns/prov#Derivation" ); + + public static final Resource EmptyCollection = M_MODEL.createResource( "http://www.w3.org/ns/prov#EmptyCollection" ); + + /**

An instance of prov:End provides additional descriptions about the binary + * prov:wasEndedBy relation from some ended prov:Activity to an prov:Entity that + * ended it. For example, :ball_game prov:wasEndedBy :buzzer; prov:qualifiedEnd + * [ a prov:End; prov:entity :buzzer; :foo :bar; prov:atTime '2012-03-09T08:05:08-05:00'^^xsd:dateTime + * ].

+ */ + public static final Resource End = M_MODEL.createResource( "http://www.w3.org/ns/prov#End" ); + + public static final Resource Entity = M_MODEL.createResource( "http://www.w3.org/ns/prov#Entity" ); + + /**

It is not recommended that the type EntityInfluence be asserted without also + * asserting one of its more specific subclasses.EntityInfluence provides additional + * descriptions of an Entity's binary influence upon any other kind of resource. + * Instances of EntityInfluence use the prov:entity property to cite the influencing + * Entity.

+ */ + public static final Resource EntityInfluence = M_MODEL.createResource( "http://www.w3.org/ns/prov#EntityInfluence" ); + + /**

An instance of prov:Generation provides additional descriptions about the + * binary prov:wasGeneratedBy relation from a generated prov:Entity to the prov:Activity + * that generated it. For example, :cake prov:wasGeneratedBy :baking; prov:qualifiedGeneration + * [ a prov:Generation; prov:activity :baking; :foo :bar ].

+ */ + public static final Resource Generation = M_MODEL.createResource( "http://www.w3.org/ns/prov#Generation" ); + + /**

Because prov:Influence is a broad relation, its most specific subclasses (e.g. + * prov:Communication, prov:Delegation, prov:End, prov:Revision, etc.) should + * be used when applicable.An instance of prov:Influence provides additional + * descriptions about the binary prov:wasInfluencedBy relation from some influenced + * Activity, Entity, or Agent to the influencing Activity, Entity, or Agent. + * For example, :stomach_ache prov:wasInfluencedBy :spoon; prov:qualifiedInfluence + * [ a prov:Influence; prov:entity :spoon; :foo :bar ] . Because prov:Influence + * is a broad relation, the more specific relations (Communication, Delegation, + * End, etc.) should be used when applicable.

+ */ + public static final Resource Influence = M_MODEL.createResource( "http://www.w3.org/ns/prov#Influence" ); + + /**

An instantaneous event, or event for short, happens in the world and marks + * a change in the world, in its activities and in its entities. The term 'event' + * is commonly used in process algebra with a similar meaning. Events represent + * communications or interactions; they are assumed to be atomic and instantaneous.

+ */ + public static final Resource InstantaneousEvent = M_MODEL.createResource( "http://www.w3.org/ns/prov#InstantaneousEvent" ); + + /**

An instance of prov:Invalidation provides additional descriptions about the + * binary prov:wasInvalidatedBy relation from an invalidated prov:Entity to the + * prov:Activity that invalidated it. For example, :uncracked_egg prov:wasInvalidatedBy + * :baking; prov:qualifiedInvalidation [ a prov:Invalidation; prov:activity :baking; + * :foo :bar ].

+ */ + public static final Resource Invalidation = M_MODEL.createResource( "http://www.w3.org/ns/prov#Invalidation" ); + + public static final Resource Location = M_MODEL.createResource( "http://www.w3.org/ns/prov#Location" ); + + public static final Resource Organization = M_MODEL.createResource( "http://www.w3.org/ns/prov#Organization" ); + + public static final Resource Person = M_MODEL.createResource( "http://www.w3.org/ns/prov#Person" ); + + /**

There exist no prescriptive requirement on the nature of plans, their representation, + * the actions or steps they consist of, or their intended goals. Since plans + * may evolve over time, it may become necessary to track their provenance, so + * plans themselves are entities. Representing the plan explicitly in the provenance + * can be useful for various tasks: for example, to validate the execution as + * represented in the provenance record, to manage expectation failures, or to + * provide explanations.

+ */ + public static final Resource Plan = M_MODEL.createResource( "http://www.w3.org/ns/prov#Plan" ); + + /**

An instance of prov:PrimarySource provides additional descriptions about the + * binary prov:hadPrimarySource relation from some secondary prov:Entity to an + * earlier, primary prov:Entity. For example, :blog prov:hadPrimarySource :newsArticle; + * prov:qualifiedPrimarySource [ a prov:PrimarySource; prov:entity :newsArticle; + * :foo :bar ] .

+ */ + public static final Resource PrimarySource = M_MODEL.createResource( "http://www.w3.org/ns/prov#PrimarySource" ); + + /**

An instance of prov:Quotation provides additional descriptions about the binary + * prov:wasQuotedFrom relation from some taken prov:Entity from an earlier, larger + * prov:Entity. For example, :here_is_looking_at_you_kid prov:wasQuotedFrom :casablanca_script; + * prov:qualifiedQuotation [ a prov:Quotation; prov:entity :casablanca_script; + * :foo :bar ].

+ */ + public static final Resource Quotation = M_MODEL.createResource( "http://www.w3.org/ns/prov#Quotation" ); + + /**

An instance of prov:Revision provides additional descriptions about the binary + * prov:wasRevisionOf relation from some newer prov:Entity to an earlier prov:Entity. + * For example, :draft_2 prov:wasRevisionOf :draft_1; prov:qualifiedRevision + * [ a prov:Revision; prov:entity :draft_1; :foo :bar ].

+ */ + public static final Resource Revision = M_MODEL.createResource( "http://www.w3.org/ns/prov#Revision" ); + + public static final Resource Role = M_MODEL.createResource( "http://www.w3.org/ns/prov#Role" ); + + public static final Resource SoftwareAgent = M_MODEL.createResource( "http://www.w3.org/ns/prov#SoftwareAgent" ); + + /**

An instance of prov:Start provides additional descriptions about the binary + * prov:wasStartedBy relation from some started prov:Activity to an prov:Entity + * that started it. For example, :foot_race prov:wasStartedBy :bang; prov:qualifiedStart + * [ a prov:Start; prov:entity :bang; :foo :bar; prov:atTime '2012-03-09T08:05:08-05:00'^^xsd:dateTime + * ] .

+ */ + public static final Resource Start = M_MODEL.createResource( "http://www.w3.org/ns/prov#Start" ); + + /**

An instance of prov:Usage provides additional descriptions about the binary + * prov:used relation from some prov:Activity to an prov:Entity that it used. + * For example, :keynote prov:used :podium; prov:qualifiedUsage [ a prov:Usage; + * prov:entity :podium; :foo :bar ].

+ */ + public static final Resource Usage = M_MODEL.createResource( "http://www.w3.org/ns/prov#Usage" ); + + public static final Resource prov_o = M_MODEL.createResource( "http://www.w3.org/ns/prov-o" ); + + /**

This document is published by the Provenance Working Group (http://www.w3.org/2011/prov/wiki/Main_Page). + * If you wish to make comments regarding this document, please send them to + * public-prov-comments@w3.org (subscribe public-prov-comments-request@w3.org, + * archives http://lists.w3.org/Archives/Public/public-prov-comments/). All feedback + * is welcome.

+ */ + public static final Resource __ = M_MODEL.createResource( "http://www.w3.org/ns/prov-o#" ); + + public static final Resource prov_o_20120312 = M_MODEL.createResource( "http://www.w3.org/ns/prov-o-20120312" ); + +} diff --git a/orcid-api-common/src/main/resources/orcid-oauth2-api-common-config.xml b/orcid-api-common/src/main/resources/orcid-oauth2-api-common-config.xml index 83f001f8a76..5eb39c9a220 100644 --- a/orcid-api-common/src/main/resources/orcid-oauth2-api-common-config.xml +++ b/orcid-api-common/src/main/resources/orcid-oauth2-api-common-config.xml @@ -57,4 +57,5 @@
+ \ No newline at end of file diff --git a/orcid-api-web/src/main/java/org/orcid/api/notificationsV2/server/delegator/impl/NotificationsApiServiceDelegatorImpl.java b/orcid-api-web/src/main/java/org/orcid/api/notificationsV2/server/delegator/impl/NotificationsApiServiceDelegatorImpl.java index 0e80bada0a4..a0ce3f0dd07 100644 --- a/orcid-api-web/src/main/java/org/orcid/api/notificationsV2/server/delegator/impl/NotificationsApiServiceDelegatorImpl.java +++ b/orcid-api-web/src/main/java/org/orcid/api/notificationsV2/server/delegator/impl/NotificationsApiServiceDelegatorImpl.java @@ -25,6 +25,7 @@ import org.orcid.core.manager.ProfileEntityCacheManager; import org.orcid.core.manager.ProfileEntityManager; import org.orcid.core.manager.SourceManager; +import org.orcid.core.oauth.OrcidBearerTokenAuthentication; import org.orcid.core.security.visibility.aop.AccessControl; import org.orcid.jaxb.model.message.ScopePathType; import org.orcid.jaxb.model.notification.permission_v2.NotificationPermissions; @@ -35,8 +36,6 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.security.oauth2.provider.OAuth2Request; import org.springframework.stereotype.Component; /** @@ -79,9 +78,9 @@ public Response findPermissionNotifications(String orcid) { // Get the client profile information Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); String clientId = null; - if (OAuth2Authentication.class.isAssignableFrom(authentication.getClass())) { - OAuth2Request authorizationRequest = ((OAuth2Authentication) authentication).getOAuth2Request(); - clientId = authorizationRequest.getClientId(); + if (OrcidBearerTokenAuthentication.class.isAssignableFrom(authentication.getClass())) { + OrcidBearerTokenAuthentication authDetails = (OrcidBearerTokenAuthentication) authentication; + clientId = authDetails.getClientId(); } NotificationPermissions notifications = notificationManager.findPermissionsByOrcidAndClient(orcid, clientId, 0, MAX_NOTIFICATIONS_AVAILABLE); diff --git a/orcid-api-web/src/main/java/org/orcid/api/notificationsV3/server/delegator/impl/NotificationsApiServiceDelegatorImpl.java b/orcid-api-web/src/main/java/org/orcid/api/notificationsV3/server/delegator/impl/NotificationsApiServiceDelegatorImpl.java index 35708ce47a4..b946ff05bf0 100644 --- a/orcid-api-web/src/main/java/org/orcid/api/notificationsV3/server/delegator/impl/NotificationsApiServiceDelegatorImpl.java +++ b/orcid-api-web/src/main/java/org/orcid/api/notificationsV3/server/delegator/impl/NotificationsApiServiceDelegatorImpl.java @@ -12,6 +12,7 @@ import org.orcid.core.manager.v3.NotificationValidationManager; import org.orcid.core.manager.v3.OrcidSecurityManager; import org.orcid.core.manager.v3.ProfileEntityManager; +import org.orcid.core.oauth.OrcidBearerTokenAuthentication; import org.orcid.core.security.visibility.aop.AccessControl; import org.orcid.jaxb.model.message.ScopePathType; import org.orcid.jaxb.model.v3.release.notification.Notification; @@ -20,8 +21,6 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.security.oauth2.provider.OAuth2Request; import org.springframework.stereotype.Component; import javax.annotation.Resource; @@ -81,9 +80,9 @@ public Response findPermissionNotifications(String orcid) { // Get the client profile information Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); String clientId = null; - if (OAuth2Authentication.class.isAssignableFrom(authentication.getClass())) { - OAuth2Request authorizationRequest = ((OAuth2Authentication) authentication).getOAuth2Request(); - clientId = authorizationRequest.getClientId(); + if (OrcidBearerTokenAuthentication.class.isAssignableFrom(authentication.getClass())) { + OrcidBearerTokenAuthentication authDetails = (OrcidBearerTokenAuthentication) authentication; + clientId = authDetails.getClientId(); } NotificationPermissions notifications = notificationManager.findPermissionsByOrcidAndClient(orcid, clientId, 0, MAX_NOTIFICATIONS_AVAILABLE); diff --git a/orcid-api-web/src/main/resources/orcid-api-security-context.xml b/orcid-api-web/src/main/resources/orcid-api-security-context.xml index 118e07a14ff..82e441238a5 100644 --- a/orcid-api-web/src/main/resources/orcid-api-security-context.xml +++ b/orcid-api-web/src/main/resources/orcid-api-security-context.xml @@ -29,7 +29,6 @@ - @@ -38,49 +37,33 @@ - - - - + - - - - + - + - - - - + - - - - + + + + - - - - - - - - - + + diff --git a/orcid-api-web/src/main/resources/orcid-api-web-context.xml b/orcid-api-web/src/main/resources/orcid-api-web-context.xml index a5e21fbb4e8..2c8855ad949 100644 --- a/orcid-api-web/src/main/resources/orcid-api-web-context.xml +++ b/orcid-api-web/src/main/resources/orcid-api-web-context.xml @@ -39,7 +39,6 @@ - diff --git a/orcid-core/src/main/java/org/orcid/core/manager/v3/impl/ProfileEntityManagerImpl.java b/orcid-core/src/main/java/org/orcid/core/manager/v3/impl/ProfileEntityManagerImpl.java index ef7abcf029d..49547e81a6f 100644 --- a/orcid-core/src/main/java/org/orcid/core/manager/v3/impl/ProfileEntityManagerImpl.java +++ b/orcid-core/src/main/java/org/orcid/core/manager/v3/impl/ProfileEntityManagerImpl.java @@ -44,6 +44,7 @@ import org.orcid.pojo.ajaxForm.Text; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.cache.annotation.Cacheable; import org.springframework.context.NoSuchMessageException; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.annotation.Transactional; diff --git a/orcid-core/src/main/java/org/orcid/core/manager/v3/read_only/ProfileEntityManagerReadOnly.java b/orcid-core/src/main/java/org/orcid/core/manager/v3/read_only/ProfileEntityManagerReadOnly.java index 4c0d563f5c3..8235234bc4a 100644 --- a/orcid-core/src/main/java/org/orcid/core/manager/v3/read_only/ProfileEntityManagerReadOnly.java +++ b/orcid-core/src/main/java/org/orcid/core/manager/v3/read_only/ProfileEntityManagerReadOnly.java @@ -13,4 +13,6 @@ public interface ProfileEntityManagerReadOnly extends ManagerReadOnlyBase { Boolean isOrcidValidAsDelegate(String orcid); Boolean haveMemberPushedWorksOrAffiliationsToRecord(String orcid, String clientId); + + Boolean hasToken(String userName, long lastModified); } \ No newline at end of file diff --git a/orcid-core/src/main/java/org/orcid/core/manager/v3/read_only/impl/ProfileEntityManagerReadOnlyImpl.java b/orcid-core/src/main/java/org/orcid/core/manager/v3/read_only/impl/ProfileEntityManagerReadOnlyImpl.java index 02ec8989336..17cf0aae4b3 100644 --- a/orcid-core/src/main/java/org/orcid/core/manager/v3/read_only/impl/ProfileEntityManagerReadOnlyImpl.java +++ b/orcid-core/src/main/java/org/orcid/core/manager/v3/read_only/impl/ProfileEntityManagerReadOnlyImpl.java @@ -3,11 +3,18 @@ import java.text.SimpleDateFormat; import org.orcid.core.manager.v3.read_only.ProfileEntityManagerReadOnly; +import org.orcid.persistence.dao.OrcidOauth2TokenDetailDao; import org.orcid.persistence.dao.ProfileDao; import org.orcid.persistence.jpa.entities.ProfileEntity; import org.orcid.pojo.ajaxForm.PojoUtil; +import org.springframework.cache.annotation.Cacheable; -public class ProfileEntityManagerReadOnlyImpl extends ManagerReadOnlyBaseImpl implements ProfileEntityManagerReadOnly { +import javax.annotation.Resource; + +public class ProfileEntityManagerReadOnlyImpl extends ManagerReadOnlyBaseImpl implements ProfileEntityManagerReadOnly { + + @Resource(name="orcidOauth2TokenDetailDaoReadOnly") + private OrcidOauth2TokenDetailDao orcidOauth2TokenDetailDaoReadOnly; protected ProfileDao profileDao; @@ -68,4 +75,10 @@ public Boolean haveMemberPushedWorksOrAffiliationsToRecord(String orcid, String } return profileDao.haveMemberPushedWorksOrAffiliationsToRecord(orcid, clientId); } + + @Override + @Cacheable(value = "count-tokens", key = "#userName.concat('-').concat(#lastModified)") + public Boolean hasToken(String userName, long lastModified) { + return orcidOauth2TokenDetailDaoReadOnly.hasToken(userName); + } } diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/OrcidBearerTokenAuthentication.java b/orcid-core/src/main/java/org/orcid/core/oauth/OrcidBearerTokenAuthentication.java index aa7ccb9c396..5ac29a59814 100644 --- a/orcid-core/src/main/java/org/orcid/core/oauth/OrcidBearerTokenAuthentication.java +++ b/orcid-core/src/main/java/org/orcid/core/oauth/OrcidBearerTokenAuthentication.java @@ -24,10 +24,6 @@ public class OrcidBearerTokenAuthentication implements Authentication { private boolean authenticated = false; private String oboClientId; - private OrcidBearerTokenAuthentication() { - - } - private OrcidBearerTokenAuthentication(Builder builder) { this.clientId = builder.clientId; this.userOrcid = builder.userOrcid; diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/OrcidMultiSecretAuthenticationProvider.java b/orcid-core/src/main/java/org/orcid/core/oauth/OrcidMultiSecretAuthenticationProvider.java new file mode 100644 index 00000000000..1498f8c7377 --- /dev/null +++ b/orcid-core/src/main/java/org/orcid/core/oauth/OrcidMultiSecretAuthenticationProvider.java @@ -0,0 +1,51 @@ +package org.orcid.core.oauth; + +import javax.annotation.Resource; + +import org.orcid.core.manager.ClientDetailsManager; +import org.orcid.core.manager.EncryptionManager; +import org.orcid.persistence.jpa.entities.ClientDetailsEntity; +import org.orcid.persistence.jpa.entities.ClientSecretEntity; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.userdetails.UserDetails; + +/** + * + * @author Will Simpson + * + */ +public class OrcidMultiSecretAuthenticationProvider extends DaoAuthenticationProvider { + + @Resource + private ClientDetailsManager clientDetailsManager; + + @Resource + private EncryptionManager encryptionManager; + + @SuppressWarnings("deprecation") + @Override + protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { + if (authentication.getCredentials() == null) { + logger.debug("Authentication failed: no credentials provided"); + throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); + } + + String presentedPassword = authentication.getCredentials().toString(); + + ClientDetailsEntity clientDetailsEntity = clientDetailsManager.findByClientId(userDetails.getUsername()); + for (ClientSecretEntity clientSecretEntity : clientDetailsEntity.getClientSecrets()) { + if(!presentedPassword.startsWith("{noop}")){ + presentedPassword = "{noop}" + presentedPassword; + } + if (getPasswordEncoder().matches(encryptionManager.decryptForInternalUse(clientSecretEntity.getClientSecret()), presentedPassword)) { + return; + } + } + logger.debug("Authentication failed: password does not match any value"); + throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); + } + +} \ No newline at end of file diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/openid/OpenIDConnectDiscoveryService.java b/orcid-core/src/main/java/org/orcid/core/oauth/openid/OpenIDConnectDiscoveryService.java new file mode 100644 index 00000000000..90d586e8525 --- /dev/null +++ b/orcid-core/src/main/java/org/orcid/core/oauth/openid/OpenIDConnectDiscoveryService.java @@ -0,0 +1,125 @@ +package org.orcid.core.oauth.openid; + +import java.util.List; + +import org.springframework.beans.factory.annotation.Value; + +import com.google.common.collect.Lists; + +public class OpenIDConnectDiscoveryService { + + private OpenIDConnectDiscoveryServiceConfig config; + + public static class OpenIDConnectDiscoveryServiceConfig{ + @Value("${org.orcid.core.baseUri}") + private String path; + + private List token_endpoint_auth_signing_alg_values_supported = Lists.newArrayList("RS256"); + private List id_token_signing_alg_values_supported = Lists.newArrayList("RS256"); + private String userinfo_endpoint = "/oauth/userinfo"; + private String authorization_endpoint = "/oauth/authorize"; + private String token_endpoint = "/oauth/token"; + private String jwks_uri = "/oauth/jwks"; + private List claims_supported = Lists.newArrayList("family_name","given_name","name","auth_time","iss","sub"); + private List scopes_supported = Lists.newArrayList("openid"); + private List subject_types_supported = Lists.newArrayList("public"); + private List response_types_supported = Lists.newArrayList("code","id_token","id_token token"); + private Boolean claims_parameter_supported = false; + private List token_endpoint_auth_methods_supported = Lists.newArrayList("client_secret_post"); + private List grant_types_supported = Lists.newArrayList("authorization_code","implicit","refresh_token"); + public String getIssuer() { + return path; + } + public List getToken_endpoint_auth_signing_alg_values_supported() { + return token_endpoint_auth_signing_alg_values_supported; + } + public List getId_token_signing_alg_values_supported() { + return id_token_signing_alg_values_supported; + } + public String getUserinfo_endpoint() { + return path+userinfo_endpoint; + } + public String getAuthorization_endpoint() { + return path+authorization_endpoint; + } + public String getToken_endpoint() { + return path+token_endpoint; + } + public String getJwks_uri() { + return path+jwks_uri; + } + public List getClaims_supported() { + return claims_supported; + } + public List getScopes_supported() { + return scopes_supported; + } + public List getSubject_types_supported() { + return subject_types_supported; + } + public List getResponse_types_supported() { + return response_types_supported; + } + public Boolean getClaims_parameter_supported() { + return claims_parameter_supported; + } + public List getToken_endpoint_auth_methods_supported() { + return token_endpoint_auth_methods_supported; + } + public List getGrant_types_supported() { + return grant_types_supported; + } + + /* example: + * "provider_info":{ + "token_endpoint_auth_signing_alg_values_supported":[ + "RS256" + ], + "userinfo_endpoint":"https://qa.orcid.org/oauth/userinfo", + "authorization_endpoint":"https://qa.orcid.org/oauth/authorize", + "claims_supported":[ + "family_name", + "given_name", + "name", + "auth_time", + "iss", + "sub" + ], + "scopes_supported":[ + "openid" + ], + "grant_types_supported":[ + "authorization_code" + ], + "token_endpoint":"https://qa.orcid.org/oauth/token", + "id_token_signing_alg_values_supported":[ + "RS256" + ], + "subject_types_supported":[ + "public" + ], + "response_types_supported":[ + "code" + ], + "jwks_uri":"https://qa.orcid.org/oauth/jwks", + "claims_parameter_supported":"false", + "token_endpoint_auth_methods_supported":[ + "client_secret_basic" + ], + "claim_types_supported":[ + "normal" + ], + "issuer":"https://orcid.org" + } + + */ + } + + public OpenIDConnectDiscoveryService(OpenIDConnectDiscoveryServiceConfig config){ + this.config = config; + } + + public OpenIDConnectDiscoveryServiceConfig getConfig(){ + return config; + } +} diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/openid/OpenIDConnectKeyService.java b/orcid-core/src/main/java/org/orcid/core/oauth/openid/OpenIDConnectKeyService.java new file mode 100644 index 00000000000..28e46200f25 --- /dev/null +++ b/orcid-core/src/main/java/org/orcid/core/oauth/openid/OpenIDConnectKeyService.java @@ -0,0 +1,128 @@ +package org.orcid.core.oauth.openid; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.security.NoSuchAlgorithmException; +import java.text.ParseException; + +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.JWSSigner; +import com.nimbusds.jose.JWSVerifier; +import com.nimbusds.jose.crypto.RSASSASigner; +import com.nimbusds.jose.crypto.RSASSAVerifier; +import com.nimbusds.jose.jwk.JWKSet; +import com.nimbusds.jose.jwk.RSAKey; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; + +public class OpenIDConnectKeyService { + + private final String keyID; + private final RSAKey publicJWK; + private final RSAKey privateJWK; + private final JWSAlgorithm defaultAlg = JWSAlgorithm.RS256; + + public static class OpenIDConnectKeyServiceConfig{ + String jwksLocation; + public String getJwksLocation() { + return jwksLocation; + } + public void setJwksLocation(String jwksLocation) { + this.jwksLocation = jwksLocation; + } + public String getJsonKey() { + return jsonKey; + } + public void setJsonKey(String jsonKey) { + this.jsonKey = jsonKey; + } + public String getKeyName() { + return keyName; + } + public void setKeyName(String keyName) { + this.keyName = keyName; + } + String jsonKey; + String keyName; + } + + /** Use a configured key ${org.orcid.openid.jwksLocation} or ${org.orcid.openid.jwksTestKey} + ${org.orcid.openid.jwksKeyName} + * + * New keys can be generated using this: https://mkjwk.org/ or a command line tool found here: https://connect2id.com/products/nimbus-jose-jwt/generator + * @throws NoSuchAlgorithmException + * @throws ParseException + * @throws IOException + * @throws URISyntaxException + */ + public OpenIDConnectKeyService(OpenIDConnectKeyServiceConfig config) throws NoSuchAlgorithmException, IOException, ParseException, URISyntaxException{ + if (config.jwksLocation !=null && !config.jwksLocation.isEmpty() && config.keyName!=null && !config.keyName.isEmpty()){ + //use a configured key. + this.keyID = config.keyName; + JWKSet keys = JWKSet.load(new File(config.jwksLocation)); + privateJWK = (RSAKey) keys.getKeyByKeyId(keyID); + publicJWK = privateJWK.toPublicJWK(); + }else if (config.jsonKey!=null){ + //use a key embedded in the properties file + this.keyID = config.keyName; + JWKSet keys = JWKSet.parse(config.jsonKey); + privateJWK = (RSAKey) keys.getKeyByKeyId(keyID); + publicJWK = privateJWK.toPublicJWK(); + }else + throw new RuntimeException("OpenID jwks not configured!"); + } + + /** Get the private key for signing + * + * @return + * @throws JOSEException + */ + public SignedJWT sign(JWTClaimsSet claims) throws JOSEException{ + JWSSigner signer = new RSASSASigner(privateJWK); + JWSHeader.Builder head = new JWSHeader.Builder(defaultAlg); + head.keyID(getDefaultKeyID()); + SignedJWT signedJWT = new SignedJWT(head.build(), claims); + signedJWT.sign(signer); + return signedJWT; + + /* For HMAC we could do the following. This may be useful for the implicit flow: + ClientDetailsEntity clientEntity = clientDetailsEntityCacheManager.retrieve(authentication.getOAuth2Request().getClientId()); + JWSSigner signer = new MACSigner(StringUtils.rightPad(clientEntity.getDecryptedClientSecret(), 32, "#").getBytes()); + signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.HS256), claims.build()); + signedJWT.sign(signer); + */ + } + + /** Get the key ID we'll be using + * + * @return + */ + public String getDefaultKeyID(){ + return keyID; + } + + /** get the Json Web Key representation of the public key + * + * @return a JWK. use .toString() to generate JSON representation. + */ + public JWKSet getPublicJWK(){ + return new JWKSet(publicJWK); + } + + /** verify an id_token was signed by us + * + * @param signed + * @return + */ + public boolean verify(SignedJWT signed) { + try { + JWSVerifier verifier = new RSASSAVerifier(publicJWK); + return signed.verify(verifier); + } catch (JOSEException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/openid/OpenIDConnectUserInfo.java b/orcid-core/src/main/java/org/orcid/core/oauth/openid/OpenIDConnectUserInfo.java new file mode 100644 index 00000000000..6db95f308e5 --- /dev/null +++ b/orcid-core/src/main/java/org/orcid/core/oauth/openid/OpenIDConnectUserInfo.java @@ -0,0 +1,67 @@ +package org.orcid.core.oauth.openid; + +import org.orcid.jaxb.model.v3.release.record.Person; +import org.orcid.jaxb.model.v3.release.record.PersonalDetails; + +public class OpenIDConnectUserInfo { + + private String id; + private String sub; + private String name; + private String family_name; + private String given_name; + + public OpenIDConnectUserInfo(){ + + } + + public OpenIDConnectUserInfo(String orcid, Person person, String path) { + this.id = path+"/"+orcid; + this.sub = orcid; + if (person.getName() != null){ + if (person.getName().getCreditName() != null){ + this.name = person.getName().getCreditName().getContent(); + } + if (person.getName().getFamilyName() != null){ + this.family_name = person.getName().getFamilyName().getContent(); + } + if (person.getName().getGivenNames() != null){ + this.given_name = person.getName().getGivenNames().getContent(); + } + } + } + public OpenIDConnectUserInfo(String orcid, PersonalDetails person, String path) { + this.id = path+"/"+orcid; + this.sub = orcid; + if (person.getName() != null){ + if (person.getName().getCreditName() != null){ + this.name = person.getName().getCreditName().getContent(); + } + if (person.getName().getFamilyName() != null){ + this.family_name = person.getName().getFamilyName().getContent(); + } + if (person.getName().getGivenNames() != null){ + this.given_name = person.getName().getGivenNames().getContent(); + } + } + } + public String getId() { + return id; + } + public void setId(String id) { + this.id = id; + } + public String getName() { + return name; + } + public String getSub() { + return sub; + } + public String getFamily_name() { + return family_name; + } + public String getGiven_name() { + return given_name; + } + +} diff --git a/orcid-core/src/test/java/org/orcid/core/manager/InstitutionalSignInManagerTest.java b/orcid-core/src/test/java/org/orcid/core/manager/InstitutionalSignInManagerTest.java index d38317dc33f..53f347dc5fe 100644 --- a/orcid-core/src/test/java/org/orcid/core/manager/InstitutionalSignInManagerTest.java +++ b/orcid-core/src/test/java/org/orcid/core/manager/InstitutionalSignInManagerTest.java @@ -25,7 +25,6 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.orcid.core.oauth.OrcidOauth2TokenDetailService; import org.orcid.core.utils.JsonUtils; import org.orcid.persistence.dao.UserConnectionDao; import org.orcid.persistence.jpa.entities.ClientDetailsEntity; @@ -52,9 +51,6 @@ public class InstitutionalSignInManagerTest { @Mock private NotificationManager mock_notificationManager; - @Mock - private OrcidOauth2TokenDetailService mock_orcidOauth2TokenDetailService; - @Resource private UserConnectionDao userConnectionDao; @@ -64,9 +60,6 @@ public class InstitutionalSignInManagerTest { @Resource private NotificationManager notificationManager; - @Resource - private OrcidOauth2TokenDetailService orcidOauth2TokenDetailService; - @Resource private InstitutionalSignInManager institutionalSignInManager; @@ -76,7 +69,6 @@ public void before() { TargetProxyHelper.injectIntoProxy(institutionalSignInManager, "userConnectionDao", mock_userConnectionDao); TargetProxyHelper.injectIntoProxy(institutionalSignInManager, "clientDetailsEntityCacheManager", mock_clientDetailsEntityCacheManager); TargetProxyHelper.injectIntoProxy(institutionalSignInManager, "notificationManager", mock_notificationManager); - TargetProxyHelper.injectIntoProxy(institutionalSignInManager, "orcidOauth2TokenDetailService", mock_orcidOauth2TokenDetailService); } @After @@ -85,7 +77,6 @@ public void after() { TargetProxyHelper.injectIntoProxy(institutionalSignInManager, "userConnectionDao", userConnectionDao); TargetProxyHelper.injectIntoProxy(institutionalSignInManager, "clientDetailsEntityCacheManager", clientDetailsEntityCacheManager); TargetProxyHelper.injectIntoProxy(institutionalSignInManager, "notificationManager", notificationManager); - TargetProxyHelper.injectIntoProxy(institutionalSignInManager, "orcidOauth2TokenDetailService", orcidOauth2TokenDetailService); } @Test @@ -94,7 +85,6 @@ public void testCreateUserConnectionAndNotify() throws UnsupportedEncodingExcept when(mock_userConnectionDao.findByProviderIdAndProviderUserIdAndIdType(anyString(), anyString(), anyString())).thenReturn(null); when(mock_clientDetailsEntityCacheManager.retrieveByIdP(anyString())).thenReturn(testClient); - when(mock_orcidOauth2TokenDetailService.doesClientKnowUser(anyString(), anyString())).thenReturn(false); institutionalSignInManager.createUserConnectionAndNotify("idType", "remoteUserId", "displayName", "providerId", userOrcid, Collections. emptyMap()); @@ -109,7 +99,6 @@ public void testDontSendNotificationIfClientKnowUser() throws UnsupportedEncodin when(mock_userConnectionDao.findByProviderIdAndProviderUserIdAndIdType(anyString(), anyString(), anyString())).thenReturn(null); when(mock_clientDetailsEntityCacheManager.retrieveByIdP(anyString())).thenReturn(testClient); - when(mock_orcidOauth2TokenDetailService.doesClientKnowUser(anyString(), anyString())).thenReturn(true); institutionalSignInManager.createUserConnectionAndNotify("idType", "remoteUserId", "displayName", "providerId", userOrcid, Collections. emptyMap()); @@ -122,7 +111,6 @@ public void testDontSendNotificationIfClientKnowUser() throws UnsupportedEncodin public void testDontSendNotificationIfIdPNotLinkedToClient() throws UnsupportedEncodingException { when(mock_userConnectionDao.findByProviderIdAndProviderUserIdAndIdType(anyString(), anyString(), anyString())).thenReturn(null); when(mock_clientDetailsEntityCacheManager.retrieveByIdP(anyString())).thenThrow(new IllegalArgumentException()); - when(mock_orcidOauth2TokenDetailService.doesClientKnowUser(anyString(), anyString())).thenReturn(false); institutionalSignInManager.createUserConnectionAndNotify("idType", "remoteUserId", "displayName", "providerId", userOrcid, Collections. emptyMap()); @@ -136,7 +124,6 @@ public void testDontPersistIfUserConnectionAlreadyExists() throws UnsupportedEnc ClientDetailsEntity testClient = new ClientDetailsEntity(clientId); when(mock_userConnectionDao.findByProviderIdAndProviderUserIdAndIdType(anyString(), anyString(), anyString())).thenReturn(new UserconnectionEntity()); when(mock_clientDetailsEntityCacheManager.retrieveByIdP(anyString())).thenReturn(testClient); - when(mock_orcidOauth2TokenDetailService.doesClientKnowUser(anyString(), anyString())).thenReturn(false); institutionalSignInManager.createUserConnectionAndNotify("idType", "remoteUserId", "displayName", "providerId", userOrcid, Collections. emptyMap()); @@ -149,7 +136,6 @@ public void testDontPersistIfUserConnectionAlreadyExists() throws UnsupportedEnc public void testDontPersistAndDontNotify() throws UnsupportedEncodingException { when(mock_userConnectionDao.findByProviderIdAndProviderUserIdAndIdType(anyString(), anyString(), anyString())).thenReturn(new UserconnectionEntity()); when(mock_clientDetailsEntityCacheManager.retrieveByIdP(anyString())).thenThrow(new IllegalArgumentException()); - when(mock_orcidOauth2TokenDetailService.doesClientKnowUser(anyString(), anyString())).thenReturn(true); institutionalSignInManager.createUserConnectionAndNotify("idType", "remoteUserId", "displayName", "providerId", userOrcid, Collections. emptyMap()); @@ -211,5 +197,4 @@ public void testCheckHeaders() throws IOException { assertEquals("myself@testshib.org", mismatch.getOriginalValue()); assertEquals("myself@testshib.org;someoneelse@testshib.org", mismatch.getCurrentValue()); } - } diff --git a/orcid-core/src/test/java/org/orcid/core/manager/NotificationManagerTest.java b/orcid-core/src/test/java/org/orcid/core/manager/NotificationManagerTest.java index 0271780d64d..e508905beb9 100644 --- a/orcid-core/src/test/java/org/orcid/core/manager/NotificationManagerTest.java +++ b/orcid-core/src/test/java/org/orcid/core/manager/NotificationManagerTest.java @@ -41,7 +41,6 @@ import org.orcid.core.api.OrcidApiConstants; import org.orcid.core.manager.impl.NotificationManagerImpl; import org.orcid.core.manager.read_only.EmailManagerReadOnly; -import org.orcid.core.oauth.OrcidOauth2TokenDetailService; import org.orcid.jaxb.model.common_v2.Locale; import org.orcid.jaxb.model.common_v2.Source; import org.orcid.jaxb.model.notification.amended_v2.AmendedSection; @@ -52,14 +51,12 @@ import org.orcid.jaxb.model.notification_v2.NotificationType; import org.orcid.model.notification.institutional_sign_in_v2.NotificationInstitutionalConnection; import org.orcid.persistence.dao.ClientDetailsDao; -import org.orcid.persistence.dao.GenericDao; import org.orcid.persistence.dao.NotificationDao; import org.orcid.persistence.dao.ProfileDao; import org.orcid.persistence.dao.ProfileEventDao; import org.orcid.persistence.dao.impl.NotificationDaoImpl; import org.orcid.persistence.jpa.entities.ClientDetailsEntity; import org.orcid.persistence.jpa.entities.NotificationEntity; -import org.orcid.persistence.jpa.entities.ProfileEventEntity; import org.orcid.persistence.jpa.entities.SourceEntity; import org.orcid.test.DBUnitTest; import org.orcid.test.OrcidJUnit4ClassRunner; @@ -89,9 +86,6 @@ public class NotificationManagerTest extends DBUnitTest { @Mock private NotificationDao mockNotificationDao; - @Mock - private OrcidOauth2TokenDetailService mockOrcidOauth2TokenDetailService; - @Mock private ProfileEntityCacheManager mockProfileEntityCacheManager; @@ -148,9 +142,6 @@ public void initMocks() throws Exception { TargetProxyHelper.injectIntoProxy(notificationManager, "encryptionManager", encryptionManager); TargetProxyHelper.injectIntoProxy(notificationManager, "profileEventDao", profileEventDao); TargetProxyHelper.injectIntoProxy(notificationManager, "sourceManager", sourceManager); - TargetProxyHelper.injectIntoProxy(notificationManager, "orcidOauth2TokenDetailService", mockOrcidOauth2TokenDetailService); - - when(mockOrcidOauth2TokenDetailService.doesClientKnowUser(Matchers.anyString(), Matchers.anyString())).thenReturn(true); } @After diff --git a/orcid-core/src/test/java/org/orcid/core/manager/impl/ProfileEntityManagerImplTest.java b/orcid-core/src/test/java/org/orcid/core/manager/impl/ProfileEntityManagerImplTest.java index 90065a85469..06069f3a5d1 100644 --- a/orcid-core/src/test/java/org/orcid/core/manager/impl/ProfileEntityManagerImplTest.java +++ b/orcid-core/src/test/java/org/orcid/core/manager/impl/ProfileEntityManagerImplTest.java @@ -17,7 +17,6 @@ import org.orcid.core.manager.ProfileEntityManager; import org.orcid.core.manager.RecordNameManager; import org.orcid.core.manager.read_only.EmailManagerReadOnly; -import org.orcid.core.oauth.OrcidOauth2TokenDetailService; import org.orcid.persistence.dao.UserConnectionDao; import org.orcid.persistence.jpa.entities.ProfileEntity; import org.orcid.test.DBUnitTest; @@ -33,9 +32,6 @@ public class ProfileEntityManagerImplTest extends DBUnitTest { private static final String CLIENT_ID_1 = "APP-5555555555555555"; private static final String USER_ORCID = "0000-0000-0000-0001"; - @Resource - private OrcidOauth2TokenDetailService orcidOauth2TokenDetailService; - @Resource private ProfileEntityManager profileEntityManager; diff --git a/orcid-core/src/test/java/org/orcid/core/manager/impl/RegistrationManagerImplTest.java b/orcid-core/src/test/java/org/orcid/core/manager/impl/RegistrationManagerImplTest.java index 89b4b6768ed..63fb888d090 100644 --- a/orcid-core/src/test/java/org/orcid/core/manager/impl/RegistrationManagerImplTest.java +++ b/orcid-core/src/test/java/org/orcid/core/manager/impl/RegistrationManagerImplTest.java @@ -53,7 +53,6 @@ import org.orcid.test.OrcidJUnit4ClassRunner; import org.orcid.test.TargetProxyHelper; import org.orcid.utils.OrcidStringUtils; -import org.springframework.security.oauth2.common.exceptions.InvalidRequestException; import org.springframework.test.context.ContextConfiguration; @RunWith(OrcidJUnit4ClassRunner.class) @@ -181,7 +180,7 @@ public void testCreateMinimalRegistrationWithExistingClaimedEmail() { try { registrationManager.createMinimalRegistration(form, true, java.util.Locale.ENGLISH, "0.0.0.0"); fail(); - } catch(InvalidRequestException e) { + } catch(IllegalArgumentException e) { assertEquals("Unable to register user due: Email " + email + " already exists and is claimed, so, it can't be used again", e.getMessage()); } catch(Exception e) { fail(); @@ -192,7 +191,7 @@ public void testCreateMinimalRegistrationWithExistingClaimedEmail() { try { registrationManager.createMinimalRegistration(form, true, java.util.Locale.ENGLISH, "0.0.0.0"); fail(); - } catch(InvalidRequestException e) { + } catch(IllegalArgumentException e) { assertEquals("Unable to register user due: Email " + email.toLowerCase() + " already exists and is claimed, so, it can't be used again", e.getMessage()); } catch(Exception e) { fail(); @@ -204,7 +203,7 @@ public void testCreateMinimalRegistrationWithExistingClaimedEmail() { try { registrationManager.createMinimalRegistration(form, true, java.util.Locale.ENGLISH, "0.0.0.0"); fail(); - } catch(InvalidRequestException e) { + } catch(IllegalArgumentException e) { assertEquals("Unable to register user due: Email " + spacedEmail + " already exists and is claimed, so, it can't be used again", e.getMessage()); } catch(Exception e) { fail(); @@ -230,7 +229,7 @@ public void testCreateMinimalRegistrationWithExistingClaimedEmailAdditional() { try { registrationManager.createMinimalRegistration(form, true, java.util.Locale.ENGLISH, "0.0.0.0"); fail(); - } catch(InvalidRequestException e) { + } catch(IllegalArgumentException e) { assertEquals("Unable to register user due: Email " + email + " already exists and is claimed, so, it can't be used again", e.getMessage()); } catch(Exception e) { fail(); @@ -248,7 +247,7 @@ public void testCreateMinimalRegistrationWithExistingUnclaimedEmailNotAutoDeprec entity.setClaimed(false); entity.setSource(new SourceEntity(new ClientDetailsEntity(CLIENT_ID_AUTODEPRECATE_DISABLED))); profileDao.merge(entity); - } catch(InvalidRequestException e) { + } catch(IllegalArgumentException e) { fail(); } catch(Exception e) { fail(); @@ -258,7 +257,7 @@ public void testCreateMinimalRegistrationWithExistingUnclaimedEmailNotAutoDeprec Registration form = createRegistrationForm(email, true); registrationManager.createMinimalRegistration(form, true, java.util.Locale.ENGLISH, "0.0.0.0"); fail(); - } catch(InvalidRequestException e) { + } catch(IllegalArgumentException e) { assertEquals("Unable to register user due: Autodeprecate is not enabled for " + email, e.getMessage()); } catch(Exception e) { fail(); @@ -278,7 +277,7 @@ public void testCreateMinimalRegistrationWithExistingUnclaimedEmailAdditionalNot entity.setClaimed(false); entity.setSource(new SourceEntity(new ClientDetailsEntity(CLIENT_ID_AUTODEPRECATE_DISABLED))); profileDao.merge(entity); - } catch(InvalidRequestException e) { + } catch(IllegalArgumentException e) { fail(); } catch(Exception e) { fail(); @@ -293,7 +292,7 @@ public void testCreateMinimalRegistrationWithExistingUnclaimedEmailAdditionalNot Registration form = createRegistrationFormMultipleEmails(email2, emailsAdditionalList, true); registrationManager.createMinimalRegistration(form, true, java.util.Locale.ENGLISH, "0.0.0.0"); fail(); - } catch(InvalidRequestException e) { + } catch(IllegalArgumentException e) { assertEquals("Unable to register user due: Autodeprecate is not enabled for " + email, e.getMessage()); } catch(Exception e) { fail(); @@ -313,7 +312,7 @@ public void testCreateMinimalRegistrationWithExistingEmailThatCanBeAutoDeprecate entity.setClaimed(false); entity.setSource(new SourceEntity(new ClientDetailsEntity(CLIENT_ID_AUTODEPRECATE_ENABLED))); profileDao.merge(entity); - } catch(InvalidRequestException e) { + } catch(IllegalArgumentException e) { fail(); } catch(Exception e) { fail(); @@ -347,7 +346,7 @@ public void testCreateMinimalRegistrationWithExistingEmailAdditionalThatCanBeAut entity.setClaimed(false); entity.setSource(new SourceEntity(new ClientDetailsEntity(CLIENT_ID_AUTODEPRECATE_ENABLED))); profileDao.merge(entity); - } catch(InvalidRequestException e) { + } catch(IllegalArgumentException e) { fail(); } catch(Exception e) { fail(); @@ -389,7 +388,7 @@ public void testCreateMinimalRegistrationWithExistingTwoEmailsAdditionalThatCanB entity.setClaimed(false); entity.setSource(new SourceEntity(new ClientDetailsEntity(CLIENT_ID_AUTODEPRECATE_ENABLED))); profileDao.merge(entity); - } catch(InvalidRequestException e) { + } catch(IllegalArgumentException e) { fail(); } catch(Exception e) { fail(); @@ -411,7 +410,7 @@ public void testCreateMinimalRegistrationWithExistingTwoEmailsAdditionalThatCanB entity.setClaimed(false); entity.setSource(new SourceEntity(new ClientDetailsEntity(CLIENT_ID_AUTODEPRECATE_ENABLED))); profileDao.merge(entity); - } catch(InvalidRequestException e) { + } catch(IllegalArgumentException e) { fail(); } catch(Exception e) { fail(); @@ -433,7 +432,7 @@ public void testCreateMinimalRegistrationWithExistingTwoEmailsAdditionalThatCanB Registration form = createRegistrationFormMultipleEmails(email3, emailsAdditionalList, true); registrationManager.createMinimalRegistration(form, true, java.util.Locale.ENGLISH, "0.0.0.0"); fail(); - } catch(InvalidRequestException e) { + } catch(IllegalArgumentException e) { assertEquals("Unable to register user due: More than 2 duplicate emails", e.getMessage()); } catch(Exception e) { fail(); diff --git a/orcid-core/src/test/java/org/orcid/core/manager/v3/FindMyStuffManagerTest.java b/orcid-core/src/test/java/org/orcid/core/manager/v3/FindMyStuffManagerTest.java index 1971f89f8c9..bae964f9356 100644 --- a/orcid-core/src/test/java/org/orcid/core/manager/v3/FindMyStuffManagerTest.java +++ b/orcid-core/src/test/java/org/orcid/core/manager/v3/FindMyStuffManagerTest.java @@ -21,7 +21,6 @@ import org.orcid.core.manager.ClientDetailsEntityCacheManager; import org.orcid.core.manager.impl.OrcidUrlManager; import org.orcid.core.manager.v3.read_only.WorkManagerReadOnly; -import org.orcid.core.oauth.OrcidOauth2TokenDetailService; import org.orcid.core.utils.v3.identifiers.finders.DataciteFinder; import org.orcid.core.utils.v3.identifiers.finders.Finder; import org.orcid.jaxb.model.clientgroup.RedirectUriType; @@ -62,9 +61,6 @@ public class FindMyStuffManagerTest extends DBUnitTest{ @Mock private ClientDetailsEntityCacheManager clientDetailsEntityCacheManager; - @Mock - private OrcidOauth2TokenDetailService orcidOauth2TokenDetailService; - @Resource(name = "jpaJaxbNotificationAdapterV3") private JpaJaxbNotificationAdapter notificationAdapter; @@ -86,7 +82,6 @@ public void initMocks() throws Exception { TargetProxyHelper.injectIntoProxy(findMyStuffManager, "workManagerReadOnly", workManagerReadOnly); TargetProxyHelper.injectIntoProxy(findMyStuffManager, "notificationManager", notificationManager); TargetProxyHelper.injectIntoProxy(findMyStuffManager, "clientDetailsEntityCacheManager", clientDetailsEntityCacheManager); - TargetProxyHelper.injectIntoProxy(findMyStuffManager, "orcidOauth2TokenDetailService", orcidOauth2TokenDetailService); TargetProxyHelper.injectIntoProxy(findMyStuffManager, "findMyStuffHistoryDao", findMyStuffHistoryDao); //Finder mock @@ -122,7 +117,6 @@ public void initMocks() throws Exception { @Test public void testFindIfAppropriate(){ //new user - when(orcidOauth2TokenDetailService.doesClientKnowUser(Matchers.anyString(), Matchers.anyString())).thenReturn(false); when(workManagerReadOnly.getAllExternalIDs(Matchers.contains(""))).thenReturn(new ExternalIDs()); when(findMyStuffHistoryDao.findAll(Matchers.contains(""))).thenReturn(new ArrayList()); @@ -136,12 +130,10 @@ public void testFindIfAppropriate(){ //check history persisted verify(findMyStuffHistoryDao, times(1)).persist(Matchers.any()); //check notification created - } @Test public void testFindIfAppropriateExistingHistory(){ - when(orcidOauth2TokenDetailService.doesClientKnowUser(Matchers.anyString(), Matchers.anyString())).thenReturn(false); when(workManagerReadOnly.getAllExternalIDs(Matchers.contains(""))).thenReturn(new ExternalIDs()); //optedOut FindMyStuffHistoryEntity e = new FindMyStuffHistoryEntity(); @@ -160,7 +152,6 @@ public void testFindIfAppropriateExistingHistory(){ @Test public void testFindIfAppropriateOptedOut(){ - when(orcidOauth2TokenDetailService.doesClientKnowUser(Matchers.anyString(), Matchers.anyString())).thenReturn(false); when(workManagerReadOnly.getAllExternalIDs(Matchers.contains(""))).thenReturn(new ExternalIDs()); //optedOut FindMyStuffHistoryEntity e = new FindMyStuffHistoryEntity(); @@ -181,7 +172,6 @@ public void testFindIfAppropriateOptedOut(){ @Test public void testFindIfAppropriateExistingWorks(){ - when(orcidOauth2TokenDetailService.doesClientKnowUser(Matchers.anyString(), Matchers.anyString())).thenReturn(false); //user with existing works ExternalID id = new ExternalID(); ExternalIDs ids = new ExternalIDs(); @@ -199,7 +189,6 @@ public void testFindIfAppropriateExistingWorks(){ @Test public void testFindIfAppropriateExistingPerms(){ //user with existing permissions - when(orcidOauth2TokenDetailService.doesClientKnowUser(Matchers.anyString(), Matchers.anyString())).thenReturn(true); when(workManagerReadOnly.getAllExternalIDs(Matchers.contains(""))).thenReturn(new ExternalIDs()); when(findMyStuffHistoryDao.findAll(Matchers.contains(""))).thenReturn(new ArrayList()); @@ -217,7 +206,6 @@ public void testFindIfAppropriateExistingPerms(){ @Test public void testFindIfAppropriateExistingNotifications(){ //new user - when(orcidOauth2TokenDetailService.doesClientKnowUser(Matchers.anyString(), Matchers.anyString())).thenReturn(false); when(workManagerReadOnly.getAllExternalIDs(Matchers.contains(""))).thenReturn(new ExternalIDs()); when(findMyStuffHistoryDao.findAll(Matchers.contains(""))).thenReturn(new ArrayList()); NotificationFindMyStuff n = new NotificationFindMyStuff(); diff --git a/orcid-core/src/test/java/org/orcid/core/manager/v3/NotificationManagerTest.java b/orcid-core/src/test/java/org/orcid/core/manager/v3/NotificationManagerTest.java index 21e71b25680..160465907d4 100644 --- a/orcid-core/src/test/java/org/orcid/core/manager/v3/NotificationManagerTest.java +++ b/orcid-core/src/test/java/org/orcid/core/manager/v3/NotificationManagerTest.java @@ -14,7 +14,6 @@ import org.orcid.core.manager.EncryptionManager; import org.orcid.core.manager.ProfileEntityCacheManager; import org.orcid.core.manager.v3.impl.NotificationManagerImpl; -import org.orcid.core.oauth.OrcidOauth2TokenDetailService; import org.orcid.jaxb.model.common.AvailableLocales; import org.orcid.jaxb.model.v3.release.common.Source; import org.orcid.jaxb.model.v3.release.notification.Notification; @@ -69,9 +68,6 @@ public class NotificationManagerTest extends DBUnitTest { @Mock private NotificationDao mockNotificationDao; - @Mock - private OrcidOauth2TokenDetailService mockOrcidOauth2TokenDetailService; - @Mock private ProfileEntityCacheManager mockProfileEntityCacheManager; @@ -128,13 +124,11 @@ public void initMocks() throws Exception { TargetProxyHelper.injectIntoProxy(notificationManager, "encryptionManager", encryptionManager); TargetProxyHelper.injectIntoProxy(notificationManager, "profileEventDao", profileEventDao); TargetProxyHelper.injectIntoProxy(notificationManager, "sourceManager", mockSourceManager); - TargetProxyHelper.injectIntoProxy(notificationManager, "orcidOauth2TokenDetailService", mockOrcidOauth2TokenDetailService); TargetProxyHelper.injectIntoProxy(notificationManager, "profileEntityCacheManager", mockProfileEntityCacheManager); TargetProxyHelper.injectIntoProxy(notificationManager, "emailManager", mockEmailManager); TargetProxyHelper.injectIntoProxy(notificationManager, "profileDao", mockProfileDao); TargetProxyHelper.injectIntoProxy(notificationManager, "notificationDao", mockNotificationDao); TargetProxyHelper.injectIntoProxy(notificationManager, "notificationAdapter", mockNotificationAdapter); - when(mockOrcidOauth2TokenDetailService.doesClientKnowUser(Matchers.anyString(), Matchers.anyString())).thenReturn(true); } @After diff --git a/orcid-core/src/test/java/org/orcid/core/manager/v3/impl/ProfileEntityManagerImplTest.java b/orcid-core/src/test/java/org/orcid/core/manager/v3/impl/ProfileEntityManagerImplTest.java index 0b07089fc66..df6bc33a594 100644 --- a/orcid-core/src/test/java/org/orcid/core/manager/v3/impl/ProfileEntityManagerImplTest.java +++ b/orcid-core/src/test/java/org/orcid/core/manager/v3/impl/ProfileEntityManagerImplTest.java @@ -23,7 +23,6 @@ import org.orcid.core.manager.v3.ProfileKeywordManager; import org.orcid.core.manager.v3.RecordNameManager; import org.orcid.core.manager.v3.ResearcherUrlManager; -import org.orcid.core.oauth.OrcidOauth2TokenDetailService; import org.orcid.core.profile.history.ProfileHistoryEventType; import org.orcid.jaxb.model.common.AvailableLocales; import org.orcid.jaxb.model.message.ScopePathType; @@ -78,9 +77,6 @@ public class ProfileEntityManagerImplTest extends DBUnitTest { private static final String CLIENT_ID_2 = "APP-5555555555555556"; private static final String USER_ORCID = "0000-0000-0000-0001"; - @Resource - private OrcidOauth2TokenDetailService orcidOauth2TokenDetailService; - @Resource(name = "profileEntityManagerV3") private ProfileEntityManager profileEntityManager; diff --git a/orcid-core/src/test/java/org/orcid/core/oauth/OAuthErrorUtilsTest.java b/orcid-core/src/test/java/org/orcid/core/oauth/OAuthErrorUtilsTest.java index 6b94ee9ef15..7187cc49509 100644 --- a/orcid-core/src/test/java/org/orcid/core/oauth/OAuthErrorUtilsTest.java +++ b/orcid-core/src/test/java/org/orcid/core/oauth/OAuthErrorUtilsTest.java @@ -17,8 +17,6 @@ * ============================================================================= */ import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - import javax.ws.rs.core.Response.Status; import org.junit.Test; @@ -27,7 +25,6 @@ import org.orcid.core.exception.OrcidDeprecatedException; import org.orcid.core.exception.OrcidInvalidScopeException; import org.springframework.security.authentication.InsufficientAuthenticationException; -import org.springframework.security.oauth2.common.exceptions.UnsupportedGrantTypeException; public class OAuthErrorUtilsTest { @@ -41,7 +38,7 @@ public void testGetOAuthErrorForLockedException() { @Test public void testGetOAuthErrorForUnsupportedGrantTypeException() { - OAuthError error = OAuthErrorUtils.getOAuthError(new UnsupportedGrantTypeException("message here")); + OAuthError error = OAuthErrorUtils.getOAuthError(new IllegalArgumentException("message here")); assertEquals(OAuthError.UNSUPPORTED_GRANT_TYPE, error.getError()); assertEquals(Status.BAD_REQUEST, error.getResponseStatus()); assertEquals("message here", error.getErrorDescription()); diff --git a/orcid-core/src/test/java/org/orcid/core/oauth/OrcidClientCredentialsCheckerTest.java b/orcid-core/src/test/java/org/orcid/core/oauth/OrcidClientCredentialsCheckerTest.java deleted file mode 100644 index a3d5e89ad6b..00000000000 --- a/orcid-core/src/test/java/org/orcid/core/oauth/OrcidClientCredentialsCheckerTest.java +++ /dev/null @@ -1,115 +0,0 @@ -package org.orcid.core.oauth; - -import static org.mockito.Mockito.when; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.orcid.core.constants.OrcidOauth2Constants; -import org.orcid.core.manager.ClientDetailsEntityCacheManager; -import org.orcid.core.oauth.service.OrcidOAuth2RequestValidator; -import org.orcid.jaxb.model.message.ScopePathType; -import org.orcid.persistence.jpa.entities.ClientDetailsEntity; -import org.orcid.persistence.jpa.entities.ClientScopeEntity; -import org.orcid.persistence.jpa.entities.ProfileEntity; -import org.orcid.persistence.jpa.entities.keys.ClientScopePk; -import org.springframework.security.oauth2.common.exceptions.InvalidScopeException; -import org.springframework.security.oauth2.provider.ClientDetailsService; -import org.springframework.security.oauth2.provider.OAuth2RequestFactory; -import org.springframework.security.oauth2.provider.TokenRequest; -import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory; - -/** - * @author Declan Newman (declan) Date: 11/05/2012 - */ -public class OrcidClientCredentialsCheckerTest { - - @Mock - private ClientDetailsService clientDetailsService; - - @Mock - private ClientDetailsEntityCacheManager clientDetailsEntityCacheManager; - - @Mock - private OrcidOAuth2RequestValidator orcidOAuth2RequestValidator; - - private OAuth2RequestFactory oAuth2RequestFactory; - - private OrcidClientCredentialsChecker checker; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - oAuth2RequestFactory = new DefaultOAuth2RequestFactory(clientDetailsService); - checker = new OrcidClientCredentialsChecker(oAuth2RequestFactory); - checker.setClientDetailsEntityCacheManager(clientDetailsEntityCacheManager); - checker.setOrcidOAuth2RequestValidator(orcidOAuth2RequestValidator); - } - - @Test(expected = InvalidScopeException.class) - public void testInvalidCredentialsScopes() throws Exception { - String memberId = "2875-8158-1475-6194"; - String clientId = "APP-1"; - setupMocks(clientId, memberId); - Set requestedScopes = new HashSet(Arrays.asList(ScopePathType.FUNDING_CREATE.value())); - Map requestParams = new HashMap (); - requestParams.put(OrcidOauth2Constants.SCOPE_PARAM, ScopePathType.FUNDING_CREATE.value()); - - checker.validateCredentials("client_credentials", new TokenRequest(requestParams, clientId, requestedScopes, "client_credentials")); - } - - @Test - public void testValidCredentialsScopes() throws Exception { - String memberId = "2875-8158-1475-6194"; - String clientId = "APP-1"; - setupMocks(clientId, memberId); - Set requestedScopes = new HashSet(Arrays.asList(ScopePathType.READ_PUBLIC.value())); - Map requestParams = new HashMap (); - requestParams.put(OrcidOauth2Constants.SCOPE_PARAM, ScopePathType.READ_PUBLIC.value()); - checker.validateCredentials("client_credentials", new TokenRequest(requestParams, clientId, requestedScopes, "client_credentials")); - } - - @Test - public void testValidCredentialsScopesForClientOnly() throws Exception { - String memberId = "2875-8158-1475-6194"; - String clientId = "APP-1"; - setupMocks(clientId, memberId); - Set requestedScopes = new HashSet(Arrays.asList(ScopePathType.READ_PUBLIC.value())); - Map requestParams = new HashMap (); - requestParams.put(OrcidOauth2Constants.SCOPE_PARAM, ScopePathType.READ_PUBLIC.value()); - checker.validateCredentials("client_credentials", new TokenRequest(requestParams, clientId, requestedScopes, "client_credentials")); - } - - private void setupMocks(String clientId, String memberId) { - ClientDetailsEntity clientDetailsEntity = new ClientDetailsEntity(); - Set scopes = new HashSet(3); - ClientScopeEntity c1 = new ClientScopeEntity(); - c1.setClientId(clientId); - c1.setScopeType(ScopePathType.ORCID_WORKS_UPDATE.value()); - scopes.add(c1); - - ClientScopeEntity c2 = new ClientScopeEntity(); - c2.setClientId(clientId); - c2.setScopeType(ScopePathType.ORCID_BIO_READ_LIMITED.value()); - scopes.add(c2); - - ClientScopeEntity c3 = new ClientScopeEntity(); - c3.setClientId(clientId); - c3.setScopeType(ScopePathType.ORCID_PROFILE_CREATE.value()); - scopes.add(c3); - - clientDetailsEntity.setClientScopes(scopes); - clientDetailsEntity.setGroupProfileId(memberId); - ProfileEntity profile = new ProfileEntity(memberId); - profile.setRecordLocked(false); - when(clientDetailsService.loadClientByClientId(clientId)).thenReturn(clientDetailsEntity); - when(clientDetailsEntityCacheManager.retrieve(clientId)).thenReturn(clientDetailsEntity); - } -} diff --git a/orcid-core/src/test/java/org/orcid/core/oauth/OrcidRefreshTokenTokenGranterTest.java b/orcid-core/src/test/java/org/orcid/core/oauth/OrcidRefreshTokenTokenGranterTest.java deleted file mode 100644 index e93edb05829..00000000000 --- a/orcid-core/src/test/java/org/orcid/core/oauth/OrcidRefreshTokenTokenGranterTest.java +++ /dev/null @@ -1,441 +0,0 @@ -package org.orcid.core.oauth; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.util.Arrays; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Random; -import java.util.Set; - -import javax.annotation.Resource; -import javax.transaction.Transactional; - -import org.apache.commons.lang.StringUtils; -import org.apache.commons.math3.random.RandomDataGenerator; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.orcid.core.constants.OrcidOauth2Constants; -import org.orcid.persistence.dao.OrcidOauth2TokenDetailDao; -import org.orcid.persistence.jpa.entities.OrcidOauth2TokenDetail; -import org.orcid.pojo.ajaxForm.PojoUtil; -import org.orcid.test.DBUnitTest; -import org.orcid.test.OrcidJUnit4ClassRunner; -import org.springframework.security.oauth2.common.OAuth2AccessToken; -import org.springframework.security.oauth2.common.exceptions.InvalidScopeException; -import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; -import org.springframework.security.oauth2.common.util.OAuth2Utils; -import org.springframework.security.oauth2.provider.TokenRequest; -import org.springframework.test.context.ContextConfiguration; - -@RunWith(OrcidJUnit4ClassRunner.class) -@ContextConfiguration(locations = { "classpath:test-orcid-core-context.xml" }) -public class OrcidRefreshTokenTokenGranterTest extends DBUnitTest { - - private static final String CLIENT_ID_1 = "APP-5555555555555555"; - private static final String CLIENT_ID_2 = "APP-5555555555555556"; - private static final String USER_ORCID = "0000-0000-0000-0001"; - - @Resource - private OrcidOauth2TokenDetailService orcidOauth2TokenDetailService; - - @Resource - private OrcidRefreshTokenTokenGranter refreshTokenTokenGranter; - - @Resource(name="orcidOauth2TokenDetailDao") - private OrcidOauth2TokenDetailDao orcidOauth2TokenDetailDao; - - @BeforeClass - public static void initDBUnitData() throws Exception { - initDBUnitData(Arrays.asList("/data/SubjectEntityData.xml", "/data/SourceClientDetailsEntityData.xml", - "/data/ProfileEntityData.xml", "/data/RecordNameEntityData.xml")); - } - - @AfterClass - public static void removeDBUnitData() throws Exception { - removeDBUnitData(Arrays.asList("/data/RecordNameEntityData.xml", "/data/ProfileEntityData.xml", "/data/SourceClientDetailsEntityData.xml", "/data/SubjectEntityData.xml")); - } - - @Transactional - private OrcidOauth2TokenDetail createToken(String clientId, String userOrcid, String tokenValue, String refreshTokenValue, Date expirationDate, String scopes) { - OrcidOauth2TokenDetail token = new OrcidOauth2TokenDetail(); - token.setApproved(true); - token.setClientDetailsId(clientId); - token.setOrcid(userOrcid); - token.setScope(scopes); - token.setTokenDisabled(false); - token.setTokenExpiration(expirationDate); - token.setTokenType("bearer"); - token.setTokenValue(tokenValue); - token.setRefreshTokenValue(refreshTokenValue); - orcidOauth2TokenDetailDao.persist(token); - assertNotNull(token.getDateCreated()); - assertNotNull(token.getLastModified()); - return token; - } - - private OAuth2AccessToken generateRefreshToken(OrcidOauth2TokenDetail tokenDetails, String customClientId, Boolean revokeOld, Long expiresIn, String... scopesParam) { - Set scopes = null; - if (scopesParam != null) { - scopes = new HashSet(Arrays.asList(scopesParam)); - } - - Map authorizationParameters = new HashMap(); - String scopesString = scopes == null ? null : StringUtils.join(scopes, ' '); - String clientId = PojoUtil.isEmpty(customClientId) ? tokenDetails.getClientDetailsId() : customClientId; - String refreshTokenValue = tokenDetails.getRefreshTokenValue(); - - authorizationParameters.put(OAuth2Utils.CLIENT_ID, clientId); - authorizationParameters.put(OrcidOauth2Constants.IS_PERSISTENT, "true"); - authorizationParameters.put(OrcidOauth2Constants.AUTHORIZATION, tokenDetails.getTokenValue()); - authorizationParameters.put(OrcidOauth2Constants.REFRESH_TOKEN, refreshTokenValue); - authorizationParameters.put(OAuth2Utils.REDIRECT_URI, tokenDetails.getRedirectUri()); - - if (!PojoUtil.isEmpty(scopesString)) { - authorizationParameters.put(OAuth2Utils.SCOPE, scopesString); - } - - if (revokeOld != null) { - authorizationParameters.put(OrcidOauth2Constants.REVOKE_OLD, String.valueOf(revokeOld)); - } - - if (expiresIn != null) { - authorizationParameters.put(OrcidOauth2Constants.EXPIRES_IN, String.valueOf(expiresIn)); - } - - TokenRequest tokenRequest = new TokenRequest(authorizationParameters, clientId, scopes, OrcidOauth2Constants.REFRESH_TOKEN); - return refreshTokenTokenGranter.grant(OrcidOauth2Constants.REFRESH_TOKEN, tokenRequest); - } - - @Test - public void createRefreshTokenTest() { - // Create token, create refresh, parent should be disabled, scopes - // should be equal - long time = System.currentTimeMillis(); - String scope = "/activities/update"; - String tokenValue = "parent-token-" + getRandom(); - String refreshTokenValue = "refresh-token-" + getRandom(); - Boolean revokeOld = null; - Date parentTokenExpiration = new Date(time + 10000); - Long expireIn = null; - - OrcidOauth2TokenDetail parent = createToken(CLIENT_ID_1, USER_ORCID, tokenValue, refreshTokenValue, parentTokenExpiration, scope); - OAuth2AccessToken refresh = generateRefreshToken(parent, null, revokeOld, expireIn, scope); - assertNotNull(refresh); - - OrcidOauth2TokenDetail parentToken = orcidOauth2TokenDetailService.findIgnoringDisabledByTokenValue(parent.getTokenValue()); - assertNotNull(parentToken); - assertEquals(tokenValue, parentToken.getTokenValue()); - assertTrue(parentToken.getTokenDisabled()); - assertEquals(scope, parentToken.getScope()); - assertNotNull(parentToken.getTokenExpiration()); - - OrcidOauth2TokenDetail refreshToken = orcidOauth2TokenDetailService.findIgnoringDisabledByTokenValue(refresh.getValue()); - assertNotNull(refreshToken); - assertNotNull(refreshToken.getTokenValue()); - assertNotNull(refreshToken.getRefreshTokenValue()); - assertFalse(refreshToken.getTokenDisabled()); - assertEquals(scope, refreshToken.getScope()); - assertNotNull(refreshToken.getTokenExpiration()); - - assertEquals(parentToken.getTokenExpiration().getTime(), refreshToken.getTokenExpiration().getTime()); - } - - @Test - public void createRefreshTokenWithNarrowerScopesTest() { - // Create token, create refresh with narrower scopes, parent should be - // disabled, scopes should be narrower - long time = System.currentTimeMillis(); - String parentScope = "/activities/update"; - String refreshScope = "/orcid-works/create"; - String tokenValue = "parent-token-" + getRandom(); - String refreshTokenValue = "refresh-token-" + getRandom(); - Boolean revokeOld = true; - Date parentTokenExpiration = new Date(time + 10000); - Long expireIn = null; - - OrcidOauth2TokenDetail parent = createToken(CLIENT_ID_1, USER_ORCID, tokenValue, refreshTokenValue, parentTokenExpiration, parentScope); - OAuth2AccessToken refresh = generateRefreshToken(parent, null, revokeOld, expireIn, refreshScope); - assertNotNull(refresh); - - OrcidOauth2TokenDetail parentToken = orcidOauth2TokenDetailService.findIgnoringDisabledByTokenValue(parent.getTokenValue()); - assertNotNull(parentToken); - assertEquals(tokenValue, parentToken.getTokenValue()); - assertTrue(parentToken.getTokenDisabled()); - assertEquals(parentScope, parentToken.getScope()); - assertNotNull(parentToken.getTokenExpiration()); - - OrcidOauth2TokenDetail refreshToken = orcidOauth2TokenDetailService.findIgnoringDisabledByTokenValue(refresh.getValue()); - assertNotNull(refreshToken); - assertNotNull(refreshToken.getTokenValue()); - assertNotNull(refreshToken.getRefreshTokenValue()); - assertFalse(refreshToken.getTokenDisabled()); - assertEquals(refreshScope, refreshToken.getScope()); - assertNotNull(refreshToken.getTokenExpiration()); - - assertEquals(parentToken.getTokenExpiration().getTime(), refreshToken.getTokenExpiration().getTime()); - } - - @Test - public void createRefreshTokenWithoutRevokeParent() { - // Create token, create refresh without disabling parent token, parent - // should be enabled, refresh should be enabled - long time = System.currentTimeMillis(); - String parentScope = "/activities/update /read-limited"; - String tokenValue = "parent-token-" + getRandom(); - String refreshTokenValue = "refresh-token-" + getRandom(); - Boolean revokeOld = false; - Date parentTokenExpiration = new Date(time + 10000); - Long expireIn = null; - - OrcidOauth2TokenDetail parent = createToken(CLIENT_ID_1, USER_ORCID, tokenValue, refreshTokenValue, parentTokenExpiration, parentScope); - OAuth2AccessToken refresh = generateRefreshToken(parent, null, revokeOld, expireIn); - assertNotNull(refresh); - - OrcidOauth2TokenDetail parentToken = orcidOauth2TokenDetailService.findIgnoringDisabledByTokenValue(parent.getTokenValue()); - assertNotNull(parentToken); - assertEquals(tokenValue, parentToken.getTokenValue()); - assertFalse(parentToken.getTokenDisabled()); - assertNotNull(parentToken.getTokenExpiration()); - - OrcidOauth2TokenDetail refreshToken = orcidOauth2TokenDetailService.findIgnoringDisabledByTokenValue(refresh.getValue()); - assertNotNull(refreshToken); - assertNotNull(refreshToken.getTokenValue()); - assertNotNull(refreshToken.getRefreshTokenValue()); - assertFalse(refreshToken.getTokenDisabled()); - assertNotNull(refreshToken.getTokenExpiration()); - - assertEquals(parentToken.getTokenExpiration().getTime(), refreshToken.getTokenExpiration().getTime()); - - assertEquals(parentToken.getScope(), refreshToken.getScope()); - - Set tokenScopes = OAuth2Utils.parseParameterList(parentToken.getScope()); - Set originalScopes = OAuth2Utils.parseParameterList(parentScope); - assertEquals(originalScopes, tokenScopes); - } - - @Test - public void createRefreshTokenWithoutRevokeParentAndWithNarrowerScopes() { - // Create token, create refresh with narrower scopes and without - // disabling parent token, parent should work, refresh should have - // narrower scopes - long time = System.currentTimeMillis(); - String parentScope = "/person/read-limited"; - String refreshScope = "/orcid-bio/read-limited"; - String tokenValue = "parent-token-" + getRandom(); - String refreshTokenValue = "refresh-token-" + getRandom(); - Boolean revokeOld = false; - Date parentTokenExpiration = new Date(time + 10000); - Long expireIn = null; - - OrcidOauth2TokenDetail parent = createToken(CLIENT_ID_1, USER_ORCID, tokenValue, refreshTokenValue, parentTokenExpiration, parentScope); - OAuth2AccessToken refresh = generateRefreshToken(parent, null, revokeOld, expireIn, refreshScope); - assertNotNull(refresh); - - OrcidOauth2TokenDetail parentToken = orcidOauth2TokenDetailService.findIgnoringDisabledByTokenValue(parent.getTokenValue()); - assertNotNull(parentToken); - assertEquals(tokenValue, parentToken.getTokenValue()); - assertFalse(parentToken.getTokenDisabled()); - assertEquals(parentScope, parentToken.getScope()); - assertNotNull(parentToken.getTokenExpiration()); - - OrcidOauth2TokenDetail refreshToken = orcidOauth2TokenDetailService.findIgnoringDisabledByTokenValue(refresh.getValue()); - assertNotNull(refreshToken); - assertNotNull(refreshToken.getTokenValue()); - assertNotNull(refreshToken.getRefreshTokenValue()); - assertFalse(refreshToken.getTokenDisabled()); - assertEquals(refreshScope, refreshToken.getScope()); - assertNotNull(refreshToken.getTokenExpiration()); - - assertEquals(parentToken.getTokenExpiration().getTime(), refreshToken.getTokenExpiration().getTime()); - } - - @Test - public void createRefreshTokenWithExpirationOf10Secs() { - // Create token, dont revoke parent and set expiration to 10 secs - long time = System.currentTimeMillis(); - String parentScope = "/person/read-limited"; - String refreshScope = "/orcid-bio/read-limited"; - String tokenValue = "parent-token-" + getRandom(); - String refreshTokenValue = "refresh-token-" + getRandom(); - Boolean revokeOld = false; - Date parentTokenExpiration = new Date(time + 10000); - Long expireIn = 5L; - - OrcidOauth2TokenDetail parent = createToken(CLIENT_ID_1, USER_ORCID, tokenValue, refreshTokenValue, parentTokenExpiration, parentScope); - OAuth2AccessToken refresh = generateRefreshToken(parent, null, revokeOld, expireIn, refreshScope); - assertNotNull(refresh); - - OrcidOauth2TokenDetail parentToken = orcidOauth2TokenDetailService.findIgnoringDisabledByTokenValue(parent.getTokenValue()); - assertNotNull(parentToken); - assertEquals(tokenValue, parentToken.getTokenValue()); - assertFalse(parentToken.getTokenDisabled()); - assertEquals(parentScope, parentToken.getScope()); - assertNotNull(parentToken.getTokenExpiration()); - - OrcidOauth2TokenDetail refreshToken = orcidOauth2TokenDetailService.findIgnoringDisabledByTokenValue(refresh.getValue()); - assertNotNull(refreshToken); - assertNotNull(refreshToken.getTokenValue()); - assertNotNull(refreshToken.getRefreshTokenValue()); - assertFalse(refreshToken.getTokenDisabled()); - assertEquals(refreshScope, refreshToken.getScope()); - assertNotNull(refreshToken.getTokenExpiration()); - - assertTrue(parentToken.getTokenExpiration().getTime() > refreshToken.getTokenExpiration().getTime()); - // Assert that current time plus 6 secs is greather than refresh token - // expiration - assertTrue((time + 6000) > refreshToken.getTokenExpiration().getTime()); - } - - @Test - public void tryToCreateRefreshTokenWithInvalidScopesTest() { - // Create token, try to create refresh token with invalid scopes, fail - long time = System.currentTimeMillis(); - String parentScope = "/person/update"; - String refreshScope = "/orcid-works/read-limited"; - String tokenValue = "parent-token-" + getRandom(); - String refreshTokenValue = "refresh-token-" + getRandom(); - Boolean revokeOld = true; - Date parentTokenExpiration = new Date(time + 10000); - Long expireIn = null; - - OrcidOauth2TokenDetail parent = createToken(CLIENT_ID_1, USER_ORCID, tokenValue, refreshTokenValue, parentTokenExpiration, parentScope); - try { - generateRefreshToken(parent, null, revokeOld, expireIn, refreshScope); - fail(); - } catch(InvalidScopeException e) { - assertTrue(e.getMessage().contains("is not allowed for the parent token")); - } catch(Exception e) { - fail(); - } - } - - @Test - public void tryToCreateRefreshTokenWithThatExpireAfterParentTokenTest() { - // Create token, try to create refresh token that expires after parent - // token, fail - long time = System.currentTimeMillis(); - String parentScope = "/person/update"; - String tokenValue = "parent-token-" + getRandom(); - String refreshTokenValue = "refresh-token-" + getRandom(); - Boolean revokeOld = true; - Date parentTokenExpiration = new Date(time + 10000); - Long expireIn = time + (15000); - - OrcidOauth2TokenDetail parent = createToken(CLIENT_ID_1, USER_ORCID, tokenValue, refreshTokenValue, parentTokenExpiration, parentScope); - try { - generateRefreshToken(parent, null, revokeOld, expireIn, parentScope); - fail(); - } catch(IllegalArgumentException e) { - assertTrue(e.getMessage().contains("Token expiration can't be after")); - } catch(Exception e) { - fail(); - } - } - - @Test - public void tryToCreateRefreshTokenWithInvalidClientTest() { - // Create token for client # 1, try to create a refresh token using - // client # 2, fail - long time = System.currentTimeMillis(); - String parentScope = "/person/update"; - String tokenValue = "parent-token-" + getRandom(); - String refreshTokenValue = "refresh-token-" + getRandom(); - Boolean revokeOld = true; - Date parentTokenExpiration = new Date(time + 10000); - Long expireIn = null; - - OrcidOauth2TokenDetail parent = createToken(CLIENT_ID_1, USER_ORCID, tokenValue, refreshTokenValue, parentTokenExpiration, parentScope); - try { - generateRefreshToken(parent, CLIENT_ID_2, revokeOld, expireIn, parentScope); - fail(); - } catch(IllegalArgumentException e) { - assertTrue(e.getMessage().contains("This token does not belong to the given client")); - } catch(Exception e) { - fail(); - } - - } - - @Test - public void tryToRefreshAnExpiredTokenTest() { - long time = System.currentTimeMillis(); - String parentScope = "/person/update"; - String tokenValue = "parent-token-" + getRandom(); - String refreshTokenValue = "refresh-token-" + getRandom(); - Boolean revokeOld = true; - Date parentTokenExpiration = new Date(time - 10000); - Long expireIn = null; - - OrcidOauth2TokenDetail parent = createToken(CLIENT_ID_1, USER_ORCID, tokenValue, refreshTokenValue, parentTokenExpiration, parentScope); - try { - generateRefreshToken(parent, null, revokeOld, expireIn, parentScope); - fail(); - } catch(InvalidTokenException e) { - assertTrue(e.getMessage().contains("Access token expired:")); - } catch(Exception e) { - fail(); - } - } - - @Test - public void tryToCreateRefreshTokenWithInvalidRefreshTokenTest() { - // Create token, try to create refresh token with invalid refresh value, - // fail - long time = System.currentTimeMillis(); - String parentScope = "/person/update"; - String tokenValue = "parent-token-" + getRandom(); - String refreshTokenValue = "refresh-token-" + getRandom(); - Boolean revokeOld = true; - Date parentTokenExpiration = new Date(time + 10000); - Long expireIn = null; - - OrcidOauth2TokenDetail parent = createToken(CLIENT_ID_1, USER_ORCID, tokenValue, refreshTokenValue, parentTokenExpiration, parentScope); - try { - //Change the value we are going to use for the refresh token - parent.setRefreshTokenValue("invalid-value"); - generateRefreshToken(parent, null, revokeOld, expireIn, parentScope); - fail(); - } catch(InvalidTokenException e) { - assertTrue(e.getMessage().contains("Unable to find refresh token")); - } catch(Exception e) { - fail(); - } - } - - @Test - public void tryToCreateRefreshTokenWithInvalidParentTokenValueTest() { - // Create token, try to create refresh token with invalid parent token - // value, fail - long time = System.currentTimeMillis(); - String parentScope = "/person/update"; - String tokenValue = "parent-token-" + getRandom(); - String refreshTokenValue = "refresh-token-" + getRandom(); - Boolean revokeOld = true; - Date parentTokenExpiration = new Date(time + 15000); - Long expireIn = null; - - OrcidOauth2TokenDetail parent = createToken(CLIENT_ID_1, USER_ORCID, tokenValue, refreshTokenValue, parentTokenExpiration, parentScope); - // Change the value we are going to use for the refresh token - parent.setTokenValue("invalid-value"); - OAuth2AccessToken refreshedToken = generateRefreshToken(parent, null, revokeOld, expireIn, parentScope); - // We shouldn't care about the access token, it's not required and - // shouldn't really be there. If the refresh token and client - // credentials are good, we can generate the refresh token. - assertNotNull(refreshedToken); - } - - private long getRandom() { - long leftLimit = 10L; - long rightLimit = 10000000L; - return new RandomDataGenerator().nextLong(leftLimit, rightLimit); - } -} diff --git a/orcid-core/src/test/java/org/orcid/core/oauth/security/OrcidOauthRedirectResolverTest.java b/orcid-core/src/test/java/org/orcid/core/oauth/security/OrcidOauthRedirectResolverTest.java deleted file mode 100644 index 8014354f080..00000000000 --- a/orcid-core/src/test/java/org/orcid/core/oauth/security/OrcidOauthRedirectResolverTest.java +++ /dev/null @@ -1,245 +0,0 @@ -package org.orcid.core.oauth.security; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Set; -import java.util.TreeSet; - -import org.junit.Rule; -import org.junit.Test; -import org.orcid.core.togglz.Features; -import org.orcid.persistence.jpa.entities.ClientAuthorisedGrantTypeEntity; -import org.orcid.persistence.jpa.entities.ClientDetailsEntity; -import org.orcid.persistence.jpa.entities.ClientRedirectUriEntity; -import org.orcid.persistence.jpa.entities.keys.ClientAuthorisedGrantTypePk; -import org.orcid.persistence.jpa.entities.keys.ClientRedirectUriPk; -import org.springframework.security.oauth2.common.exceptions.InvalidGrantException; -import org.springframework.security.oauth2.common.exceptions.InvalidRequestException; -import org.springframework.security.oauth2.common.exceptions.RedirectMismatchException; -import org.togglz.junit.TogglzRule; - -public class OrcidOauthRedirectResolverTest { - - private Collection allRedirectGrantTypes = Arrays.asList("implicit", "refresh_token", "client_credentials", "authorization_code", - "urn:ietf:params:oauth:grant-type:token-exchange"); - - @Rule - public TogglzRule togglzRule = TogglzRule.allDisabled(Features.class); - - private OrcidOauthRedirectResolver resolver = new OrcidOauthRedirectResolver(); - - { - resolver.setRedirectGrantTypes(allRedirectGrantTypes); - } - - @Test - public void resolveRedirect_emptyGrantTypesTest() { - ClientDetailsEntity clientDetails = new ClientDetailsEntity(); - // Empty authorized grant types should fail - clientDetails.setClientAuthorizedGrantTypes(Set.of()); - try { - resolver.resolveRedirect("", clientDetails); - } catch (InvalidGrantException e) { - assertEquals("A client must have at least one authorized grant type.", e.getMessage()); - } - } - - @Test - public void resolveRedirect_invalidGrantTypesTest() { - ClientDetailsEntity clientDetails = new ClientDetailsEntity(); - // Empty authorized grant types should fail - ClientAuthorisedGrantTypeEntity gte1 = new ClientAuthorisedGrantTypeEntity(); - gte1.setClientId("id"); - gte1.setGrantType("other"); - clientDetails.setClientAuthorizedGrantTypes(Set.of(gte1)); - try { - resolver.resolveRedirect("", clientDetails); - } catch (InvalidGrantException e) { - assertEquals("A redirect_uri can only be used by implicit or authorization_code grant types.", e.getMessage()); - } - } - - @Test - public void resolveRedirect_noRegisteredRedirectUriTest() { - ClientDetailsEntity clientDetails = new ClientDetailsEntity(); - // Empty authorized grant types should fail - ClientAuthorisedGrantTypeEntity gte1 = new ClientAuthorisedGrantTypeEntity(); - gte1.setClientId("id"); - gte1.setGrantType("authorization_code"); - clientDetails.setClientAuthorizedGrantTypes(Set.of(gte1)); - // Null redirect uris - try { - resolver.resolveRedirect("", clientDetails); - } catch (InvalidRequestException e) { - assertEquals("At least one redirect_uri must be registered with the client.", e.getMessage()); - } - // Empty redirect uris - try { - clientDetails.setClientRegisteredRedirectUris(new TreeSet()); - resolver.resolveRedirect("", clientDetails); - } catch (InvalidRequestException e) { - assertEquals("At least one redirect_uri must be registered with the client.", e.getMessage()); - } - } - - @Test - public void resolveRedirect_NoMatchingRedirectTest() { - ClientDetailsEntity clientDetails = new ClientDetailsEntity(); - // Empty authorized grant types should fail - ClientAuthorisedGrantTypeEntity gte1 = new ClientAuthorisedGrantTypeEntity(); - gte1.setClientId("id"); - gte1.setGrantType("authorization_code"); - clientDetails.setClientAuthorizedGrantTypes(Set.of(gte1)); - - TreeSet redirectUris = new TreeSet(); - setRedirectUris("id", redirectUris); - clientDetails.setClientRegisteredRedirectUris(redirectUris); - - // Root url should not match if it is not registered - try { - resolver.resolveRedirect("https://qa.orcid.org", clientDetails); - } catch (RedirectMismatchException e) { - assertEquals("Unable to find a matching redirect_uri for the client.", e.getMessage()); - } - - // Different protocol should not match - try { - resolver.resolveRedirect("http://qa.orcid.org/1", clientDetails); - } catch (RedirectMismatchException e) { - assertEquals("Unable to find a matching redirect_uri for the client.", e.getMessage()); - } - - // Different domain should not match - try { - resolver.resolveRedirect("https://orcid.org/1", clientDetails); - } catch (RedirectMismatchException e) { - assertEquals("Unable to find a matching redirect_uri for the client.", e.getMessage()); - } - - // Different domain should not match - try { - resolver.resolveRedirect("https://example.com/1", clientDetails); - } catch (RedirectMismatchException e) { - assertEquals("Unable to find a matching redirect_uri for the client.", e.getMessage()); - } - - // Same domain but different patch should not match - try { - resolver.resolveRedirect("https://qa.orcid.org/4", clientDetails); - } catch (RedirectMismatchException e) { - assertEquals("Unable to find a matching redirect_uri for the client.", e.getMessage()); - } - } - - @Test - public void resolveRedirectTest() { - ClientDetailsEntity clientDetails = new ClientDetailsEntity(); - // Empty authorized grant types should fail - ClientAuthorisedGrantTypeEntity gte1 = new ClientAuthorisedGrantTypeEntity(); - gte1.setGrantType("authorization_code"); - clientDetails.setClientAuthorizedGrantTypes(Set.of(gte1)); - - TreeSet redirectUris = new TreeSet(); - setRedirectUris("id", redirectUris); - clientDetails.setClientRegisteredRedirectUris(redirectUris); - - assertEquals("https://qa.orcid.org/1", resolver.resolveRedirect("https://qa.orcid.org/1", clientDetails)); - assertEquals("https://qa.orcid.org/2", resolver.resolveRedirect("https://qa.orcid.org/2", clientDetails)); - assertEquals("https://qa.orcid.org/3", resolver.resolveRedirect("https://qa.orcid.org/3", clientDetails)); - assertEquals("https://qa.orcid.org/1/subdirectory", resolver.resolveRedirect("https://qa.orcid.org/1/subdirectory", clientDetails)); - assertEquals("https://qa.orcid.org/1/2/3", resolver.resolveRedirect("https://qa.orcid.org/1/2/3", clientDetails)); - } - - @Test - public void redirectUriGeneralTests() { - redirectUriGeneralTest(); - } - - private void redirectUriGeneralTest() { - // No matches at all - assertFalse(resolver.redirectMatches("https://example.com", "https://qa.orcid.org")); - assertFalse(resolver.redirectMatches("https://qa.orcid.org", "https://example.com")); - - // Different scheme should not match - assertFalse(resolver.redirectMatches("https://qa.orcid.org", "http://qa.orcid.org")); - assertFalse(resolver.redirectMatches("http://qa.orcid.org", "https://qa.orcid.org")); - assertFalse(resolver.redirectMatches("https://example.com", "http://example.com")); - assertFalse(resolver.redirectMatches("http://example.com", "https://example.com")); - - // Different port should not match - assertFalse(resolver.redirectMatches("http://qa.orcid.org", "http://qa.orcid.org:8080")); - assertFalse(resolver.redirectMatches("http://qa.orcid.org:8080", "http://qa.orcid.org")); - assertFalse(resolver.redirectMatches("http://qa.orcid.org:8080", "http://qa.orcid.org:8081")); - assertFalse(resolver.redirectMatches("http://127.0.0.1", "http://127.0.0.1:8080")); - assertFalse(resolver.redirectMatches("http://127.0.0.1:8080", "http://127.0.0.1")); - assertFalse(resolver.redirectMatches("http://127.0.0.1:8080", "http://127.0.0.1:8081")); - - // Different host should not match - assertFalse(resolver.redirectMatches("https://orcid.org", "http://example.com")); - - // Root should not match if it is not registered - assertFalse(resolver.redirectMatches("https://qa.orcid.org", "http://qa.orcid.org/subdirectory")); - - // Subdirectory should not match if it is not registered - assertFalse(resolver.redirectMatches("https://qa.orcid.org/subdirectory/2", "https://qa.orcid.org/subdirectory/1")); - assertFalse(resolver.redirectMatches("https://qa.orcid.org/s2", "https://qa.orcid.org/s1")); - - // Exact match - assertTrue(resolver.redirectMatches("https://orcid.org", "https://orcid.org")); - assertTrue(resolver.redirectMatches("http://example.com:9001", "http://example.com:9001")); - assertTrue(resolver.redirectMatches("http://127.0.0.1", "http://127.0.0.1")); - assertTrue(resolver.redirectMatches("http://127.0.0.1:8080", "http://127.0.0.1:8080")); - - // Subdirectory should match - assertTrue(resolver.redirectMatches("https://orcid.org/subdirectory", "https://orcid.org")); - assertTrue(resolver.redirectMatches("https://orcid.org/subdirectory/1", "https://orcid.org")); - assertTrue(resolver.redirectMatches("https://orcid.org/subdirectory/1/2", "https://orcid.org")); - assertTrue(resolver.redirectMatches("https://orcid.org/subdirectory/1/2", "https://orcid.org/subdirectory")); - assertTrue(resolver.redirectMatches("https://orcid.org/subdirectory/1/2", "https://orcid.org/subdirectory/1")); - assertTrue(resolver.redirectMatches("https://orcid.org/subdirectory/1/2", "https://orcid.org/subdirectory/1/2")); - - // If a subdomain is registered but not the domain, it should fail - assertFalse(resolver.redirectMatches("https://orcid.org", "https://qa.orcid.org")); - // Different subdomains should not match - assertFalse(resolver.redirectMatches("https://qa.orcid.org", "https://sandbox.orcid.org")); - - // Acceptance criteria checks: subdirectory should be allowed - assertTrue(resolver.redirectMatches("https://example.com/subdirectory", "https://example.com")); - } - - @Test - public void redirectMatches_AllowMatchingSubdomainsTest() { - // Subdomain should not match - assertFalse(resolver.redirectMatches("https://www.orcid.org", "https://orcid.org")); - assertFalse(resolver.redirectMatches("https://qa.orcid.org", "https://orcid.org")); - - // Acceptance criteria checks: subdomains should be rejected - assertFalse(resolver.redirectMatches("https://subdomain.example.com/", "https://example.com")); - assertFalse(resolver.redirectMatches("https://subdomain.example.com/subdirectory", "https://example.com")); - assertFalse(resolver.redirectMatches("https://www.example.com", "https://example.com")); - } - - private void setRedirectUris(String clientId, TreeSet redirectUris) { - ClientRedirectUriEntity r1 = new ClientRedirectUriEntity(); - r1.setClientId(clientId); - r1.setRedirectUri("https://qa.orcid.org/1"); - r1.setRedirectUriType("type-1"); - - ClientRedirectUriEntity r2 = new ClientRedirectUriEntity(); - r2.setClientId(clientId); - r2.setRedirectUri("https://qa.orcid.org/2"); - r2.setRedirectUriType("type-1"); - - ClientRedirectUriEntity r3 = new ClientRedirectUriEntity(); - r3.setClientId(clientId); - r3.setRedirectUri("https://qa.orcid.org/3"); - r3.setRedirectUriType("type-1"); - redirectUris.add(r1); - redirectUris.add(r2); - redirectUris.add(r3); - } -} diff --git a/orcid-core/src/test/java/org/orcid/core/oauth/service/OrcidAuthorizationCodeServiceTest.java b/orcid-core/src/test/java/org/orcid/core/oauth/service/OrcidAuthorizationCodeServiceTest.java deleted file mode 100644 index cc7ebada5fc..00000000000 --- a/orcid-core/src/test/java/org/orcid/core/oauth/service/OrcidAuthorizationCodeServiceTest.java +++ /dev/null @@ -1,127 +0,0 @@ -package org.orcid.core.oauth.service; - -import static org.junit.Assert.assertNotNull; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import javax.annotation.Resource; - -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.orcid.core.manager.ProfileEntityManager; -import org.orcid.core.security.OrcidUserDetailsService; -import org.orcid.test.DBUnitTest; -import org.orcid.test.OrcidJUnit4ClassRunner; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.oauth2.common.exceptions.InvalidClientException; -import org.springframework.security.oauth2.common.exceptions.InvalidGrantException; -import org.springframework.security.oauth2.common.util.OAuth2Utils; -import org.springframework.security.oauth2.provider.AuthorizationRequest; -import org.springframework.security.oauth2.provider.ClientDetailsService; -import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.security.oauth2.provider.OAuth2RequestFactory; -import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices; -import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory; -import org.springframework.test.annotation.Rollback; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.transaction.annotation.Transactional; - -/** - * @author Declan Newman (declan) Date: 24/04/2012 - */ -@RunWith(OrcidJUnit4ClassRunner.class) -@ContextConfiguration(locations = { "classpath:test-orcid-core-context.xml" }) -public class OrcidAuthorizationCodeServiceTest extends DBUnitTest { - - @Resource(name = "orcidAuthorizationCodeService") - private AuthorizationCodeServices authorizationCodeServices; - - @Resource(name = "profileEntityManager") - private ProfileEntityManager profileEntityManager; - - @Resource(name = "clientDetailsManager") - private ClientDetailsService clientDetailsService; - - @Resource - private OrcidUserDetailsService orcidUserDetailsService; - - private OAuth2RequestFactory oAuth2RequestFactory; - - @BeforeClass - public static void initDBUnitData() throws Exception { - initDBUnitData(Arrays.asList("/data/SourceClientDetailsEntityData.xml", "/data/ProfileEntityData.xml", - "/data/ClientDetailsEntityData.xml")); - } - - @AfterClass - public static void removeDBUnitData() throws Exception { - removeDBUnitData(Arrays.asList("/data/ClientDetailsEntityData.xml", "/data/ProfileEntityData.xml")); - } - - @Before - public void before() { - oAuth2RequestFactory = new DefaultOAuth2RequestFactory(clientDetailsService); - } - - @Test - @Rollback - @Transactional - public void testCreateAuthorizationCodeWithValidClient() { - AuthorizationRequest request = getAuthorizationRequest("4444-4444-4444-4441"); - OAuth2Authentication oauth2Authentication = new OAuth2Authentication(oAuth2RequestFactory.createOAuth2Request(request), getUserAuthentication("0000-0000-0000-0002")); - String authorizationCode = authorizationCodeServices.createAuthorizationCode(oauth2Authentication); - assertNotNull(authorizationCode); - oauth2Authentication = authorizationCodeServices.consumeAuthorizationCode(authorizationCode); - assertNotNull(oauth2Authentication); - } - - @Test(expected = InvalidGrantException.class) - @Rollback - @Transactional - public void testConsumeNonExistentCode() { - authorizationCodeServices.consumeAuthorizationCode("bodus-code!"); - } - - @Test(expected = InvalidClientException.class) - @Rollback - @Transactional - public void testCreateAuthorizationCodeWithInvalidClient() { - AuthorizationRequest request = getAuthorizationRequest("6444-4444-4444-4441"); - OAuth2Authentication auth = new OAuth2Authentication(oAuth2RequestFactory.createOAuth2Request(request), getUserAuthentication("0000-0000-0000-0002")); - authorizationCodeServices.createAuthorizationCode(auth); - } - - public AuthorizationRequest getAuthorizationRequest(String clientId) { - Set grantedAuthorities = new HashSet(Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"))); - Set resourceIds = new HashSet<>(); - resourceIds.add("orcid"); - - Map params = new HashMap(); - params.put(OAuth2Utils.CLIENT_ID, clientId); - params.put(OAuth2Utils.SCOPE, "a-scope"); - - AuthorizationRequest authorizationRequest = oAuth2RequestFactory.createAuthorizationRequest(params); - authorizationRequest.setAuthorities(grantedAuthorities); - authorizationRequest.setResourceIds(resourceIds); - - return authorizationRequest; - } - - private Authentication getUserAuthentication(String orcid) { - UserDetails details = (UserDetails) orcidUserDetailsService.loadUserByUsername(orcid); - UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(details.getUsername(), "password"); - auth.setDetails(details); - return auth; - } -} diff --git a/orcid-core/src/test/java/org/orcid/core/oauth/service/OrcidOauth2TokenDetailServiceTest.java b/orcid-core/src/test/java/org/orcid/core/oauth/service/OrcidOauth2TokenDetailServiceTest.java deleted file mode 100644 index 6982f76b55a..00000000000 --- a/orcid-core/src/test/java/org/orcid/core/oauth/service/OrcidOauth2TokenDetailServiceTest.java +++ /dev/null @@ -1,353 +0,0 @@ -package org.orcid.core.oauth.service; - -import static org.hamcrest.core.AllOf.allOf; -import static org.hamcrest.core.AnyOf.anyOf; -import static org.hamcrest.core.Is.is; -import static org.hamcrest.core.IsNot.not; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import java.util.Arrays; -import java.util.Date; -import java.util.List; - -import javax.annotation.Resource; -import javax.persistence.NoResultException; - -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.orcid.core.common.manager.EmailFrequencyManager; -import org.orcid.core.constants.RevokeReason; -import org.orcid.core.oauth.OrcidOauth2TokenDetailService; -import org.orcid.core.utils.cache.redis.RedisClient; -import org.orcid.persistence.dao.OrcidOauth2TokenDetailDao; -import org.orcid.persistence.jpa.entities.OrcidOauth2TokenDetail; -import org.orcid.persistence.jpa.entities.ProfileEntity; -import org.orcid.test.DBUnitTest; -import org.orcid.test.OrcidJUnit4ClassRunner; -import org.orcid.test.TargetProxyHelper; -import org.springframework.test.context.ContextConfiguration; - -/** - * - * @author Angel Montenegro - */ -@RunWith(OrcidJUnit4ClassRunner.class) -@ContextConfiguration(locations = { "classpath:test-orcid-core-context.xml" }) -public class OrcidOauth2TokenDetailServiceTest extends DBUnitTest { - private static final String CLIENT_ID_1 = "APP-5555555555555555"; - private static final String CLIENT_ID_2 = "APP-5555555555555556"; - private static final String USER_ORCID = "0000-0000-0000-0001"; - private static final String USER_ORCID_2 = "0000-0000-0000-0002"; - private static final String USER_ORCID_3 = "0000-0000-0000-0003"; - - @Resource - private OrcidOauth2TokenDetailService orcidOauth2TokenDetailService; - - @Resource(name="orcidOauth2TokenDetailDao") - private OrcidOauth2TokenDetailDao orcidOauth2TokenDetailDao; - - @Mock - private RedisClient redisClientMock; - - @BeforeClass - public static void initDBUnitData() throws Exception { - initDBUnitData(Arrays.asList("/data/SubjectEntityData.xml", "/data/SourceClientDetailsEntityData.xml", - "/data/ProfileEntityData.xml")); - } - - @Before - public void before() { - MockitoAnnotations.initMocks(this); - // Enable the cache - TargetProxyHelper.injectIntoProxy(orcidOauth2TokenDetailService, "isTokenCacheEnabled", true); - TargetProxyHelper.injectIntoProxy(orcidOauth2TokenDetailService, "redisClient", redisClientMock); - } - - @AfterClass - public static void removeDBUnitData() throws Exception { - removeDBUnitData(Arrays.asList("/data/ProfileEntityData.xml", "/data/SourceClientDetailsEntityData.xml", - "/data/SubjectEntityData.xml")); - } - - @Test - public void dontGetExpiredTokensTest() { - //Token # 1: expired - Long token1Id = createToken(CLIENT_ID_1, "expired-1", USER_ORCID, new Date(System.currentTimeMillis() - 100000), "/read-limited", false).getId(); - //Token # 2: /activities/update - Long token2Id = createToken(CLIENT_ID_1, "active-1", USER_ORCID, new Date(System.currentTimeMillis() + 100000), "/activities/update", false).getId(); - //Token # 3: disabled - Long token3Id = createToken(CLIENT_ID_1, "disabled-1", USER_ORCID, new Date(System.currentTimeMillis() + 100000), "/activities/update", true).getId(); - //Token # 4: /read-limited - Long token4Id = createToken(CLIENT_ID_1, "active-2", USER_ORCID, new Date(System.currentTimeMillis() + 100000), "/read-limited", false).getId(); - //Fetch all active tokens - List activeTokens = orcidOauth2TokenDetailService.findByUserName(USER_ORCID); - assertNotNull(activeTokens); - assertEquals(2, activeTokens.size()); - assertThat(activeTokens.get(0).getScope(), anyOf(is("/activities/update"), is("/read-limited"))); - assertThat(activeTokens.get(1).getScope(), anyOf(is("/activities/update"), is("/read-limited"))); - - //Find the id of the token with scope '/activities/update' and disable that token - Long tokenToDisableId = null; - for(OrcidOauth2TokenDetail token : activeTokens) { - if("/activities/update".equals(token.getScope())) { - tokenToDisableId = token.getId(); - break; - } - } - - assertNotNull(tokenToDisableId); - //Disable that access token - orcidOauth2TokenDetailService.disableAccessToken(tokenToDisableId, USER_ORCID); - //Fetch the active tokens again, it should contain just one - activeTokens = orcidOauth2TokenDetailService.findByUserName(USER_ORCID); - assertNotNull(activeTokens); - assertEquals(1, activeTokens.size()); - assertEquals("/read-limited", activeTokens.get(0).getScope()); - assertEquals("active-2", activeTokens.get(0).getTokenValue()); - - orcidOauth2TokenDetailDao.remove(token1Id); - orcidOauth2TokenDetailDao.remove(token2Id); - orcidOauth2TokenDetailDao.remove(token3Id); - orcidOauth2TokenDetailDao.remove(token4Id); - } - - @Test - public void removeAllTokensWithSameScopesTest() { - //We will test deleting this token - Long token1Id = createToken(CLIENT_ID_1, "token-1", USER_ORCID, new Date(System.currentTimeMillis() + 100000), "/activities/update /read-limited", false).getId(); //Delete - //Should not delete this - Long token2Id = createToken(CLIENT_ID_1, "token-2", USER_ORCID, new Date(System.currentTimeMillis() + 100000), "/activities/update", false).getId(); - //Should not delete this - Long token3Id = createToken(CLIENT_ID_1, "token-3", USER_ORCID, new Date(System.currentTimeMillis() + 100000), "/read-limited", false).getId(); - //Should delete this one since it have the same scopes but in different order - Long token4Id = createToken(CLIENT_ID_1, "token-4", USER_ORCID, new Date(System.currentTimeMillis() + 100000), "/read-limited /activities/update", false).getId(); //Delete - //Should not delete this since it have one scope more /orcid-profile/read-limited - Long token5Id = createToken(CLIENT_ID_1, "token-5", USER_ORCID, new Date(System.currentTimeMillis() + 100000), "/orcid-profile/read-limited /activities/update /read-limited", false).getId(); - //Should not delete this since it have one scope more /activities/read-limited - Long token6Id = createToken(CLIENT_ID_1, "token-6", USER_ORCID, new Date(System.currentTimeMillis() + 100000), "/read-limited /activities/update /activities/read-limited", false).getId(); - //Should not delete this - Long token7Id = createToken(CLIENT_ID_1, "token-7", USER_ORCID, new Date(System.currentTimeMillis() + 100000), "/person/read-limited", false).getId(); - //Should not delete this since it have several more scopes - Long token8Id = createToken(CLIENT_ID_1, "token-8", USER_ORCID, new Date(System.currentTimeMillis() + 100000), "/funding/read-limited /read-limited /activities/read-limited /orcid-works/create /affiliations/update /activities/update", false).getId(); - //Should remove this since it contains the same scopes - Long token9Id = createToken(CLIENT_ID_1, "token-9", USER_ORCID, new Date(System.currentTimeMillis() + 100000), "/activities/update /read-limited", false).getId(); //Delete - - - List activeTokens = orcidOauth2TokenDetailService.findByUserName(USER_ORCID); - assertNotNull(activeTokens); - assertEquals(9, activeTokens.size()); - - orcidOauth2TokenDetailService.disableAccessToken(token1Id, USER_ORCID); - activeTokens = orcidOauth2TokenDetailService.findByUserName(USER_ORCID); - assertEquals(6, activeTokens.size()); - - for(OrcidOauth2TokenDetail token : activeTokens) { - assertThat(token.getId(), allOf(not(token1Id), not(token4Id), not(token9Id))); - assertThat(token.getId(), anyOf(is(token2Id), is(token3Id), is(token5Id), is(token6Id), is(token7Id), is(token8Id))); - } - - orcidOauth2TokenDetailDao.remove(token1Id); - orcidOauth2TokenDetailDao.remove(token2Id); - orcidOauth2TokenDetailDao.remove(token3Id); - orcidOauth2TokenDetailDao.remove(token4Id); - orcidOauth2TokenDetailDao.remove(token5Id); - orcidOauth2TokenDetailDao.remove(token6Id); - orcidOauth2TokenDetailDao.remove(token7Id); - orcidOauth2TokenDetailDao.remove(token8Id); - orcidOauth2TokenDetailDao.remove(token9Id); - } - - @Test - public void dontRemoveOtherClientScopesTest() { - Long seed = System.currentTimeMillis(); - Long token1Id = createToken(CLIENT_ID_1, "token-1-" + seed, USER_ORCID, new Date(System.currentTimeMillis() + 100000), "/read-limited", false).getId(); //Delete - Long token2Id = createToken(CLIENT_ID_1, "token-2-" + seed, USER_ORCID, new Date(System.currentTimeMillis() + 100000), "/activities/update", false).getId(); - Long token3Id = createToken(CLIENT_ID_1, "token-3-" + seed, USER_ORCID, new Date(System.currentTimeMillis() + 100000), "/activities/update /read-limited", false).getId(); - Long token4Id = createToken(CLIENT_ID_1, "token-4-" + seed, USER_ORCID, new Date(System.currentTimeMillis() + 100000), "/read-limited", false).getId(); //Delete - - Long token5Id = createToken(CLIENT_ID_2, "token-5-" + seed, USER_ORCID, new Date(System.currentTimeMillis() + 100000), "/read-limited", false).getId(); - Long token6Id = createToken(CLIENT_ID_2, "token-6-" + seed, USER_ORCID, new Date(System.currentTimeMillis() + 100000), "/activities/update", false).getId(); - Long token7Id = createToken(CLIENT_ID_2, "token-7-" + seed, USER_ORCID, new Date(System.currentTimeMillis() + 100000), "/activities/update /read-limited", false).getId(); - Long token8Id = createToken(CLIENT_ID_2, "token-8-" + seed, USER_ORCID, new Date(System.currentTimeMillis() + 100000), "/read-limited", false).getId(); - - List activeTokens = orcidOauth2TokenDetailService.findByUserName(USER_ORCID); - assertNotNull(activeTokens); - assertEquals(8, activeTokens.size()); - - orcidOauth2TokenDetailService.disableAccessToken(token1Id, USER_ORCID); - - activeTokens = orcidOauth2TokenDetailService.findByUserName(USER_ORCID); - assertEquals(6, activeTokens.size()); - - for(OrcidOauth2TokenDetail token : activeTokens) { - assertThat(token.getId(), allOf(not(token1Id), not(token4Id))); - assertThat(token.getId(), anyOf(is(token2Id), is(token3Id), is(token5Id), is(token6Id), is(token7Id), is(token8Id))); - } - } - - @Test - public void disableAccessTokenByUserOrcidTest() { - Date date = new Date(System.currentTimeMillis() + 100000); - createToken(CLIENT_ID_1, "active-1-user-1", USER_ORCID_2, date, "/activities/update", false); - createToken(CLIENT_ID_1, "active-2-user-1", USER_ORCID_2, date, "/activities/update", false); - createToken(CLIENT_ID_1, "active-3-user-1", USER_ORCID_2, date, "/activities/update", false); - createToken(CLIENT_ID_1, "active-1-user-2", USER_ORCID_3, date, "/activities/update", false); - createToken(CLIENT_ID_1, "active-2-user-2", USER_ORCID_3, date, "/activities/update", false); - createToken(CLIENT_ID_1, "active-3-user-2", USER_ORCID_3, date, "/activities/update", false); - - List tokensUser1 = orcidOauth2TokenDetailService.findByClientIdAndUserName(CLIENT_ID_1, USER_ORCID_2); - assertEquals(3, tokensUser1.size()); - for(OrcidOauth2TokenDetail token : tokensUser1) { - assertFalse(token.getTokenDisabled()); - } - - List tokensUser2 = orcidOauth2TokenDetailService.findByClientIdAndUserName(CLIENT_ID_1, USER_ORCID_3); - assertEquals(3, tokensUser2.size()); - for(OrcidOauth2TokenDetail token : tokensUser2) { - assertFalse(token.getTokenDisabled()); - } - - orcidOauth2TokenDetailService.disableAccessTokenByUserOrcid(USER_ORCID_2, RevokeReason.RECORD_DEACTIVATED); - - tokensUser1 = orcidOauth2TokenDetailService.findByClientIdAndUserName(CLIENT_ID_1, USER_ORCID_2); - assertEquals(3, tokensUser1.size()); - for(OrcidOauth2TokenDetail token : tokensUser1) { - // Tokens for this user MUST be disabled at this point - assertTrue(token.getTokenDisabled()); - } - - tokensUser2 = orcidOauth2TokenDetailService.findByClientIdAndUserName(CLIENT_ID_1, USER_ORCID_3); - assertEquals(3, tokensUser2.size()); - for(OrcidOauth2TokenDetail token : tokensUser2) { - assertFalse(token.getTokenDisabled()); - } - } - - @Test - public void disableAccessTokenByCodeAndClientTest() { - Date date = new Date(System.currentTimeMillis() + 100000); - String authCode = "auth-code-1"; - OrcidOauth2TokenDetail dbt1 = createToken(CLIENT_ID_1, "token-1", USER_ORCID, date, "/activities/update", false, authCode); - OrcidOauth2TokenDetail dbt2 = createToken(CLIENT_ID_1, "token-2", USER_ORCID, date, "/activities/update", false, authCode); - OrcidOauth2TokenDetail dbt3 = createToken(CLIENT_ID_1, "token-3", USER_ORCID, date, "/activities/update", false, authCode); - OrcidOauth2TokenDetail dbt4 = createToken(CLIENT_ID_1, "token-4", USER_ORCID_2, date, "/activities/update", false, authCode); - OrcidOauth2TokenDetail dbt5 = createToken(CLIENT_ID_2, "token-5", USER_ORCID_3, date, "/activities/update", false, authCode); - OrcidOauth2TokenDetail dbt6 = createToken(CLIENT_ID_2, "token-6", USER_ORCID, date, "/activities/update", false, authCode); - - // Disable tokens with authCode and CLIENT_ID_1 - orcidOauth2TokenDetailService.disableAccessTokenByCodeAndClient(authCode, CLIENT_ID_1, RevokeReason.AUTH_CODE_REUSED); - - verify(redisClientMock, times(1)).remove("token-1"); - verify(redisClientMock, times(1)).remove("token-2"); - verify(redisClientMock, times(1)).remove("token-3"); - verify(redisClientMock, times(1)).remove("token-4"); - - // Tokens 1, 2, 3 and 4 should be revoked - OrcidOauth2TokenDetail t1 = orcidOauth2TokenDetailService.findIgnoringDisabledByTokenValue("token-1"); - assertNotNull(t1.getRevocationDate()); - assertEquals(RevokeReason.AUTH_CODE_REUSED.toString(), t1.getRevokeReason()); - OrcidOauth2TokenDetail t2 = orcidOauth2TokenDetailService.findIgnoringDisabledByTokenValue("token-2"); - assertNotNull(t2.getRevocationDate()); - assertEquals(RevokeReason.AUTH_CODE_REUSED.toString(), t2.getRevokeReason()); - OrcidOauth2TokenDetail t3 = orcidOauth2TokenDetailService.findIgnoringDisabledByTokenValue("token-3"); - assertNotNull(t3.getRevocationDate()); - assertEquals(RevokeReason.AUTH_CODE_REUSED.toString(), t3.getRevokeReason()); - // This case is never possible, the client used the same auth code to create a token on other user - OrcidOauth2TokenDetail t4 = orcidOauth2TokenDetailService.findIgnoringDisabledByTokenValue("token-4"); - assertNotNull(t4.getRevocationDate()); - assertEquals(RevokeReason.AUTH_CODE_REUSED.toString(), t4.getRevokeReason()); - - // Tokens 5 and 6 should be active - OrcidOauth2TokenDetail t5 = orcidOauth2TokenDetailService.findIgnoringDisabledByTokenValue("token-5"); - assertNull(t5.getRevocationDate()); - assertNull(t5.getRevokeReason()); - OrcidOauth2TokenDetail t6 = orcidOauth2TokenDetailService.findIgnoringDisabledByTokenValue("token-6"); - assertNull(t6.getRevocationDate()); - assertNull(t6.getRevokeReason()); - - // Cleanup - orcidOauth2TokenDetailDao.remove(dbt1.getId()); - orcidOauth2TokenDetailDao.remove(dbt2.getId()); - orcidOauth2TokenDetailDao.remove(dbt3.getId()); - orcidOauth2TokenDetailDao.remove(dbt4.getId()); - orcidOauth2TokenDetailDao.remove(dbt5.getId()); - orcidOauth2TokenDetailDao.remove(dbt6.getId()); - } - - @Test - public void updateScopesTest() { - String tokenValue = "TOKEN123"; - String scopes1 = "/person/read-limited /activities/update /read-limited"; - String scopes2 = "/person/read-limited /read-limited"; - String scopes3 = "/read-limited"; - //We will test deleting this token - Long token1Id = createToken(CLIENT_ID_1, tokenValue, USER_ORCID, new Date(System.currentTimeMillis() + 100000), scopes1, false).getId(); //Delete - OrcidOauth2TokenDetail token1 = orcidOauth2TokenDetailDao.find(token1Id); - assertNotNull(token1); - assertEquals(CLIENT_ID_1, token1.getClientDetailsId()); - assertEquals(tokenValue, token1.getTokenValue()); - assertEquals(USER_ORCID, token1.getOrcid()); - assertEquals(scopes1, token1.getScope()); - assertEquals("bearer", token1.getTokenType()); - - orcidOauth2TokenDetailDao.updateScopes(tokenValue, scopes2); - OrcidOauth2TokenDetail token2 = orcidOauth2TokenDetailDao.findByTokenValue(tokenValue); - assertNotNull(token2); - assertEquals(CLIENT_ID_1, token2.getClientDetailsId()); - assertEquals(tokenValue, token2.getTokenValue()); - assertEquals(USER_ORCID, token2.getOrcid()); - assertEquals(scopes2, token2.getScope()); - assertEquals("bearer", token2.getTokenType()); - - orcidOauth2TokenDetailDao.updateScopes(tokenValue, scopes3); - OrcidOauth2TokenDetail token3 = orcidOauth2TokenDetailDao.findByTokenValue(tokenValue); - assertNotNull(token3); - assertEquals(CLIENT_ID_1, token3.getClientDetailsId()); - assertEquals(tokenValue, token3.getTokenValue()); - assertEquals(USER_ORCID, token3.getOrcid()); - assertEquals(scopes3, token3.getScope()); - assertEquals("bearer", token3.getTokenType()); - - orcidOauth2TokenDetailDao.remove(token1Id); - - try { - orcidOauth2TokenDetailDao.findByTokenValue(tokenValue); - fail(); - } catch(NoResultException nre) { - //Expected behavior - } - } - - private OrcidOauth2TokenDetail createToken(String clientId, String tokenValue, String userOrcid, Date expirationDate, String scopes, boolean disabled) { - return createToken(clientId, tokenValue, userOrcid, expirationDate, scopes, disabled, null); - } - - private OrcidOauth2TokenDetail createToken(String clientId, String tokenValue, String userOrcid, Date expirationDate, String scopes, boolean disabled, String authCode) { - OrcidOauth2TokenDetail token = new OrcidOauth2TokenDetail(); - token.setApproved(true); - token.setClientDetailsId(clientId); - token.setOrcid(userOrcid); - token.setScope(scopes); - token.setTokenDisabled(disabled); - token.setTokenExpiration(expirationDate); - token.setTokenType("bearer"); - token.setTokenValue(tokenValue); - token.setAuthorizationCode(authCode); - orcidOauth2TokenDetailDao.persist(token); - return token; - } - -} diff --git a/orcid-core/src/test/java/org/orcid/core/oauth/service/OrcidTokenStoreServiceTest.java b/orcid-core/src/test/java/org/orcid/core/oauth/service/OrcidTokenStoreServiceTest.java deleted file mode 100644 index 5c882cc40ab..00000000000 --- a/orcid-core/src/test/java/org/orcid/core/oauth/service/OrcidTokenStoreServiceTest.java +++ /dev/null @@ -1,279 +0,0 @@ -package org.orcid.core.oauth.service; - -import static org.hamcrest.core.AnyOf.anyOf; -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; - -import java.io.Serializable; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; - -import javax.annotation.Resource; - -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.orcid.core.constants.OrcidOauth2Constants; -import org.orcid.core.manager.ClientDetailsEntityCacheManager; -import org.orcid.core.manager.ClientDetailsManager; -import org.orcid.core.manager.ProfileEntityManager; -import org.orcid.core.manager.SourceNameCacheManager; -import org.orcid.core.manager.v3.read_only.RecordNameManagerReadOnly; -import org.orcid.core.oauth.OrcidUserAuthentication; -import org.orcid.persistence.dao.OrcidOauth2TokenDetailDao; -import org.orcid.persistence.dao.RecordNameDao; -import org.orcid.persistence.jpa.entities.OrcidOauth2TokenDetail; -import org.orcid.persistence.jpa.entities.ProfileEntity; -import org.orcid.test.DBUnitTest; -import org.orcid.test.OrcidJUnit4ClassRunner; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.oauth2.common.DefaultExpiringOAuth2RefreshToken; -import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; -import org.springframework.security.oauth2.common.DefaultOAuth2RefreshToken; -import org.springframework.security.oauth2.common.ExpiringOAuth2RefreshToken; -import org.springframework.security.oauth2.common.OAuth2AccessToken; -import org.springframework.security.oauth2.common.OAuth2RefreshToken; -import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.security.oauth2.provider.OAuth2Request; -import org.springframework.security.oauth2.provider.token.TokenStore; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.transaction.annotation.Transactional; - -/** - * @author Declan Newman (declan) Date: 20/04/2012 - */ -@RunWith(OrcidJUnit4ClassRunner.class) -@ContextConfiguration(locations = { "classpath:test-orcid-core-context.xml" }) -public class OrcidTokenStoreServiceTest extends DBUnitTest { - - @Resource(name = "orcidTokenStore") - private TokenStore orcidTokenStoreService; - - @Resource - private ProfileEntityManager profileEntityManager; - - @Resource(name="orcidOauth2TokenDetailDao") - private OrcidOauth2TokenDetailDao orcidOauth2TokenDetailDao; - - @Resource - private ClientDetailsEntityCacheManager clientDetailsEntityCacheManager; - - @Resource - private SourceNameCacheManager sourceNameCacheManager; - - @Mock - private ClientDetailsManager mockClientDetailsManager; - - @Mock - private RecordNameDao mockRecordNameDao; - - @Mock - private RecordNameManagerReadOnly mockRecordNameManager; - - @BeforeClass - public static void initDBUnitData() throws Exception { - initDBUnitData(Arrays.asList("/data/SubjectEntityData.xml", "/data/SourceClientDetailsEntityData.xml", - "/data/ProfileEntityData.xml", "/data/ClientDetailsEntityData.xml", "/data/OrcidOauth2AuthorisationDetailsData.xml")); - } - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - } - - @AfterClass - public static void removeDBUnitData() throws Exception { - removeDBUnitData(Arrays.asList("/data/OrcidOauth2AuthorisationDetailsData.xml", "/data/ClientDetailsEntityData.xml", "/data/ProfileEntityData.xml", - "/data/SubjectEntityData.xml")); - } - - @Test - public void testReadAuthentication() throws Exception { - OAuth2Authentication oAuth2Authentication = orcidTokenStoreService.readAuthentication("persistent-token-2"); - assertNotNull(oAuth2Authentication); - OAuth2Request oAuth2Request = oAuth2Authentication.getOAuth2Request(); - assertNotNull(oAuth2Request); - Object principal = oAuth2Authentication.getPrincipal(); - assertNotNull(principal); - assertTrue(!oAuth2Authentication.isClientOnly()); - } - - @Test - @Transactional - public void testStoreAccessToken() throws Exception { - String clientId = "4444-4444-4444-4441"; - DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken("some-long-oauth2-token-value-9"); - - ExpiringOAuth2RefreshToken refreshToken = new DefaultExpiringOAuth2RefreshToken("some-long-oauth2-refresh-value-9", new Date()); - token.setRefreshToken(refreshToken); - token.setScope(new HashSet(Arrays.asList("/orcid-bio/read", "/orcid-works/read"))); - token.setTokenType("bearer"); - token.setExpiration(new Date()); - token.setAdditionalInformation(new HashMap()); - - Map parameters = new HashMap(); - parameters.put("client_id", clientId); - parameters.put("state", "read"); - parameters.put("scope", "/orcid-profile/write"); - parameters.put("redirect_uri", "http://www.google.com/"); - parameters.put("response_type", "bearer"); - OAuth2Request request = new OAuth2Request(Collections. emptyMap(), clientId, Collections. emptyList(), true, new HashSet(Arrays.asList("/orcid-profile/read-limited")), Collections. emptySet(), null, Collections. emptySet(), Collections. emptyMap()); - - ProfileEntity profileEntity = profileEntityManager.findByOrcid("4444-4444-4444-4444"); - OrcidUserAuthentication userAuthentication = new OrcidUserAuthentication(profileEntity, true); - - OAuth2Authentication authentication = new OAuth2Authentication(request, userAuthentication); - - assertFalse(token.getAdditionalInformation().containsKey(OrcidOauth2Constants.TOKEN_ID)); - orcidTokenStoreService.storeAccessToken(token, authentication); - assertTrue(token.getAdditionalInformation().containsKey(OrcidOauth2Constants.TOKEN_ID)); - } - - @Test - public void testReadAccessToken() throws Exception { - OAuth2AccessToken oAuth2AccessToken = orcidTokenStoreService.readAccessToken("some-long-oauth2-token-value-1"); - assertNotNull(oAuth2AccessToken); - } - - @Test - public void testRemoveAccessToken() throws Exception { - OAuth2AccessToken accessToken = new DefaultOAuth2AccessToken("code3"); - orcidTokenStoreService.removeAccessToken(accessToken); - OAuth2AccessToken oAuth2AccessToken = orcidTokenStoreService.readAccessToken("code3"); - assertNull(oAuth2AccessToken); - } - - @Test - @Transactional - public void testReadAuthenticationForRefreshToken() throws Exception { - OAuth2RefreshToken refreshToken = new DefaultOAuth2RefreshToken("some-long-oauth2-refresh-value-1"); - OAuth2Authentication oAuth2Authentication = orcidTokenStoreService.readAuthenticationForRefreshToken(refreshToken); - assertNotNull(oAuth2Authentication); - } - - @Test - public void testRemoveByRefreshToken() throws Exception { - OAuth2AccessToken token = orcidTokenStoreService.readAccessToken("some-long-oauth2-token-value-3"); - assertNotNull(token); - orcidTokenStoreService.removeRefreshToken(token.getRefreshToken()); - token = orcidTokenStoreService.readAccessToken("some-long-oauth2-token-value-3"); - assertNull(token); - } - - @Test - public void testRemoveAccessTokenUsingRefreshToken() throws Exception { - OAuth2AccessToken token = orcidTokenStoreService.readAccessToken("persistent-token-1"); - orcidTokenStoreService.removeAccessTokenUsingRefreshToken(token.getRefreshToken()); - token = orcidTokenStoreService.readAccessToken("persistent-token-1"); - assertNull(token); - } - - @Test - @Transactional - public void testGetAccessToken() throws Exception { - String clientId = "APP-5555555555555555"; - String orcid = "0000-0000-0000-0001"; - String scope = "/read-limited"; - String accessTokenValue = "00000000-0000-0000-0000-00000000000"; - Map parameters = new HashMap(); - parameters.put("client_id", clientId); - parameters.put("state", "a-state"); - parameters.put("scope", "/read-limited"); - parameters.put("redirect_uri", "http://www.google.com/"); - parameters.put("response_type", "bearer"); - OAuth2Request request = - new OAuth2Request(Collections. emptyMap(), clientId, Collections. emptyList(), true, new HashSet(Arrays.asList(scope)), Collections. emptySet(), null, Collections. emptySet(), Collections. emptyMap()); - - ProfileEntity profileEntity = profileEntityManager.findByOrcid(orcid); - OrcidUserAuthentication userAuthentication = new OrcidUserAuthentication(profileEntity, true); - - OAuth2Authentication authentication = new OAuth2Authentication(request, userAuthentication); - - OAuth2AccessToken accessToken = orcidTokenStoreService.getAccessToken(authentication); - assertNotNull(accessToken); - assertEquals(accessTokenValue, accessToken.getValue()); - assertEquals(1, accessToken.getScope().size()); - assertTrue(accessToken.getScope().contains(scope)); - } - - @Test - public void testFindTokensByUserName() throws Exception { - Collection tokensByUserName = orcidTokenStoreService.findTokensByClientIdAndUserName("4444-4444-4444-4441", "4444-4444-4444-4441"); - assertNotNull(tokensByUserName); - assertEquals(1, tokensByUserName.size()); - } - - @Test - public void testFindTokensByClientId() throws Exception { - Collection tokensByClientId = orcidTokenStoreService.findTokensByClientId("4444-4444-4444-4441"); - assertNotNull(tokensByClientId); - assertEquals(1, tokensByClientId.size()); - } - - @Test - public void testDisabledTokensAreNotReturnedWhenLookingByClientAndUserName() { - String clientId = "4444-4444-4444-4441"; - String userId = "0000-0000-0000-0001"; - OrcidOauth2TokenDetail token1 = createAccessToken("enabled-1", "/read-limited", clientId, userId, false); - assertNotNull(token1); - assertNotNull(token1.getId()); - - OrcidOauth2TokenDetail token2 = createAccessToken("enabled-2", "/read-limited", clientId, userId, false); - assertNotNull(token2); - assertNotNull(token2.getId()); - - OrcidOauth2TokenDetail token3 = createAccessToken("enabled-3", "/read-limited", clientId, userId, false); - assertNotNull(token3); - assertNotNull(token3.getId()); - - OrcidOauth2TokenDetail disabledToken1 = createAccessToken("disabled-1", "/read-limited", clientId, userId, true); - assertNotNull(disabledToken1); - assertNotNull(disabledToken1.getId()); - - OrcidOauth2TokenDetail disabledToken2 = createAccessToken("disabled-2", "/read-limited", clientId, userId, true); - assertNotNull(disabledToken2); - assertNotNull(disabledToken2.getId()); - - OrcidOauth2TokenDetail disabledToken3 = createAccessToken("disabled-3", "/read-limited", clientId, userId, true); - assertNotNull(disabledToken3); - assertNotNull(disabledToken3.getId()); - - Collection tokens = orcidTokenStoreService.findTokensByClientIdAndUserName(clientId, userId); - assertNotNull(tokens); - assertEquals(3, tokens.size()); - - for(OAuth2AccessToken token : tokens) { - assertThat(token.getValue(), anyOf(is("enabled-1"), is("enabled-2"), is("enabled-3"))); - } - } - - @Transactional - private OrcidOauth2TokenDetail createAccessToken(String tokenValue, String scope, String clientId, String userOrcid, boolean disabled) { - OrcidOauth2TokenDetail token = new OrcidOauth2TokenDetail(); - token.setApproved(true); - token.setClientDetailsId(clientId); - token.setOrcid(userOrcid); - token.setScope(scope); - token.setTokenDisabled(disabled); - token.setTokenValue(tokenValue); - orcidOauth2TokenDetailDao.persist(token); - assertNotNull(token.getDateCreated()); - assertNotNull(token.getLastModified()); - return token; - } - -} diff --git a/orcid-core/src/test/java/org/orcid/core/security/visibility/aop/OrcidApiAuthorizationSecurityAspectTest.java b/orcid-core/src/test/java/org/orcid/core/security/visibility/aop/OrcidApiAuthorizationSecurityAspectTest.java deleted file mode 100644 index 4707f6dc474..00000000000 --- a/orcid-core/src/test/java/org/orcid/core/security/visibility/aop/OrcidApiAuthorizationSecurityAspectTest.java +++ /dev/null @@ -1,129 +0,0 @@ -package org.orcid.core.security.visibility.aop; - -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.assertFalse; -import static org.mockito.Mockito.when; - -import java.util.Arrays; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.orcid.core.BaseTest; -import org.orcid.jaxb.model.message.ScopePathType; -import org.orcid.persistence.dao.OrcidOauth2TokenDetailDao; - -public class OrcidApiAuthorizationSecurityAspectTest extends BaseTest { - private final String clientId = "APP-0001"; - private final String userOrcid = "0000-0000-0000-0001"; - - OrcidApiAuthorizationSecurityAspect orcidApiAuthorizationSecurityAspect; - - @Mock - private OrcidOauth2TokenDetailDao mockedOrcidOauth2TokenDetailDao; - - @Before - public void setup() { - if(orcidApiAuthorizationSecurityAspect == null) { - orcidApiAuthorizationSecurityAspect = new OrcidApiAuthorizationSecurityAspect(); - } - MockitoAnnotations.initMocks(this); - orcidApiAuthorizationSecurityAspect.setOrcidOauth2TokenDetailDao(mockedOrcidOauth2TokenDetailDao); - } - - @Test - public void testActivitiesReadLimitedScopes() { - when(mockedOrcidOauth2TokenDetailDao.findAvailableScopesByUserAndClientId(clientId, userOrcid)).thenReturn(Arrays.asList("/activities/read-limited")); - assertTrue(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.ORCID_WORKS_READ_LIMITED.getContent(), ScopePathType.ORCID_WORKS_UPDATE.getContent())); - assertTrue(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.FUNDING_READ_LIMITED.getContent(), ScopePathType.FUNDING_UPDATE.getContent())); - assertTrue(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.AFFILIATIONS_READ_LIMITED.getContent(), ScopePathType.AFFILIATIONS_UPDATE.getContent())); - } - - @Test - public void testActivitiesUpdateScopes() { - when(mockedOrcidOauth2TokenDetailDao.findAvailableScopesByUserAndClientId(clientId, userOrcid)).thenReturn(Arrays.asList("/activities/update")); - assertTrue(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.ORCID_WORKS_READ_LIMITED.getContent(), ScopePathType.ORCID_WORKS_UPDATE.getContent())); - assertTrue(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.FUNDING_READ_LIMITED.getContent(), ScopePathType.FUNDING_UPDATE.getContent())); - assertTrue(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.AFFILIATIONS_READ_LIMITED.getContent(), ScopePathType.AFFILIATIONS_UPDATE.getContent())); - } - - @Test - public void testWorksReadLimitedScopes() { - when(mockedOrcidOauth2TokenDetailDao.findAvailableScopesByUserAndClientId(clientId, userOrcid)).thenReturn(Arrays.asList("/orcid-works/read-limited")); - assertTrue(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.ORCID_WORKS_READ_LIMITED.getContent(), ScopePathType.ORCID_WORKS_UPDATE.getContent())); - assertFalse(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.FUNDING_READ_LIMITED.getContent(), ScopePathType.FUNDING_UPDATE.getContent())); - assertFalse(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.AFFILIATIONS_READ_LIMITED.getContent(), ScopePathType.AFFILIATIONS_UPDATE.getContent())); - } - - @Test - public void testFundingReadLimitedScopes() { - when(mockedOrcidOauth2TokenDetailDao.findAvailableScopesByUserAndClientId(clientId, userOrcid)).thenReturn(Arrays.asList("/funding/read-limited")); - assertTrue(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.FUNDING_READ_LIMITED.getContent(), ScopePathType.FUNDING_UPDATE.getContent())); - assertFalse(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.ORCID_WORKS_READ_LIMITED.getContent(), ScopePathType.ORCID_WORKS_UPDATE.getContent())); - assertFalse(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.AFFILIATIONS_READ_LIMITED.getContent(), ScopePathType.AFFILIATIONS_UPDATE.getContent())); - } - - @Test - public void testAffiliationsReadLimitedScopes() { - when(mockedOrcidOauth2TokenDetailDao.findAvailableScopesByUserAndClientId(clientId, userOrcid)).thenReturn(Arrays.asList("/affiliations/read-limited")); - assertTrue(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.AFFILIATIONS_READ_LIMITED.getContent(), ScopePathType.AFFILIATIONS_UPDATE.getContent())); - assertFalse(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.FUNDING_READ_LIMITED.getContent(), ScopePathType.FUNDING_UPDATE.getContent())); - assertFalse(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.ORCID_WORKS_READ_LIMITED.getContent(), ScopePathType.ORCID_WORKS_UPDATE.getContent())); - } - - @Test - public void testOrcidBioScopes() { - when(mockedOrcidOauth2TokenDetailDao.findAvailableScopesByUserAndClientId(clientId, userOrcid)).thenReturn(Arrays.asList("/orcid-bio/read-limited")); - assertFalse(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.AFFILIATIONS_READ_LIMITED.getContent(), ScopePathType.AFFILIATIONS_UPDATE.getContent())); - assertFalse(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.FUNDING_READ_LIMITED.getContent(), ScopePathType.FUNDING_UPDATE.getContent())); - assertFalse(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.ORCID_WORKS_READ_LIMITED.getContent(), ScopePathType.ORCID_WORKS_UPDATE.getContent())); - - when(mockedOrcidOauth2TokenDetailDao.findAvailableScopesByUserAndClientId(clientId, userOrcid)).thenReturn(Arrays.asList("/orcid-bio/external-identifiers/create")); - assertFalse(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.AFFILIATIONS_READ_LIMITED.getContent(), ScopePathType.AFFILIATIONS_UPDATE.getContent())); - assertFalse(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.FUNDING_READ_LIMITED.getContent(), ScopePathType.FUNDING_UPDATE.getContent())); - assertFalse(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.ORCID_WORKS_READ_LIMITED.getContent(), ScopePathType.ORCID_WORKS_UPDATE.getContent())); - } - - @Test - public void testOrcidProfileReadLimitedScope() { - when(mockedOrcidOauth2TokenDetailDao.findAvailableScopesByUserAndClientId(clientId, userOrcid)).thenReturn(Arrays.asList("/orcid-profile/read-limited")); - assertTrue(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.ORCID_WORKS_READ_LIMITED.getContent(), ScopePathType.ORCID_WORKS_UPDATE.getContent())); - assertTrue(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.FUNDING_READ_LIMITED.getContent(), ScopePathType.FUNDING_UPDATE.getContent())); - assertTrue(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.AFFILIATIONS_READ_LIMITED.getContent(), ScopePathType.AFFILIATIONS_UPDATE.getContent())); - } - - @Test - public void testPersonReadLimitedScope() { - when(mockedOrcidOauth2TokenDetailDao.findAvailableScopesByUserAndClientId(clientId, userOrcid)).thenReturn(Arrays.asList("/person/read-limited")); - assertFalse(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.ORCID_WORKS_READ_LIMITED.getContent(), ScopePathType.ORCID_WORKS_UPDATE.getContent())); - assertFalse(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.FUNDING_READ_LIMITED.getContent(), ScopePathType.FUNDING_UPDATE.getContent())); - assertFalse(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.AFFILIATIONS_READ_LIMITED.getContent(), ScopePathType.AFFILIATIONS_UPDATE.getContent())); - } - - @Test - public void testPersonUpdateScope() { - when(mockedOrcidOauth2TokenDetailDao.findAvailableScopesByUserAndClientId(clientId, userOrcid)).thenReturn(Arrays.asList("/person/update")); - assertFalse(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.ORCID_WORKS_READ_LIMITED.getContent(), ScopePathType.ORCID_WORKS_UPDATE.getContent())); - assertFalse(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.FUNDING_READ_LIMITED.getContent(), ScopePathType.FUNDING_UPDATE.getContent())); - assertFalse(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.AFFILIATIONS_READ_LIMITED.getContent(), ScopePathType.AFFILIATIONS_UPDATE.getContent())); - } - - @Test - public void testCombineSomeScopes() { - when(mockedOrcidOauth2TokenDetailDao.findAvailableScopesByUserAndClientId(clientId, userOrcid)).thenReturn(Arrays.asList("/person/update", "/orcid-works/update")); - assertTrue(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.ORCID_WORKS_READ_LIMITED.getContent(), ScopePathType.ORCID_WORKS_UPDATE.getContent())); - assertFalse(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.FUNDING_READ_LIMITED.getContent(), ScopePathType.FUNDING_UPDATE.getContent())); - assertFalse(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.AFFILIATIONS_READ_LIMITED.getContent(), ScopePathType.AFFILIATIONS_UPDATE.getContent())); - - when(mockedOrcidOauth2TokenDetailDao.findAvailableScopesByUserAndClientId(clientId, userOrcid)).thenReturn(Arrays.asList("/person/update", "/orcid-works/update", "/funding/read-limited")); - assertTrue(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.ORCID_WORKS_READ_LIMITED.getContent(), ScopePathType.ORCID_WORKS_UPDATE.getContent())); - assertTrue(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.FUNDING_READ_LIMITED.getContent(), ScopePathType.FUNDING_UPDATE.getContent())); - assertFalse(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.AFFILIATIONS_READ_LIMITED.getContent(), ScopePathType.AFFILIATIONS_UPDATE.getContent())); - - when(mockedOrcidOauth2TokenDetailDao.findAvailableScopesByUserAndClientId(clientId, userOrcid)).thenReturn(Arrays.asList("/person/update", "/orcid-works/update", "/funding/read-limited", "/activities/read-limited")); - assertTrue(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.ORCID_WORKS_READ_LIMITED.getContent(), ScopePathType.ORCID_WORKS_UPDATE.getContent())); - assertTrue(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.FUNDING_READ_LIMITED.getContent(), ScopePathType.FUNDING_UPDATE.getContent())); - assertTrue(orcidApiAuthorizationSecurityAspect.hasScopeEnabled(clientId, userOrcid, ScopePathType.AFFILIATIONS_READ_LIMITED.getContent(), ScopePathType.AFFILIATIONS_UPDATE.getContent())); - } -} diff --git a/orcid-core/src/test/java/org/orcid/core/utils/SecurityContextTestUtils.java b/orcid-core/src/test/java/org/orcid/core/utils/SecurityContextTestUtils.java new file mode 100644 index 00000000000..ef9150bf326 --- /dev/null +++ b/orcid-core/src/test/java/org/orcid/core/utils/SecurityContextTestUtils.java @@ -0,0 +1,89 @@ +package org.orcid.core.utils; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.Serializable; +import java.util.*; + +import org.orcid.core.oauth.OrcidBearerTokenAuthentication; +import org.orcid.jaxb.model.message.ScopePathType; +import org.orcid.persistence.jpa.entities.ProfileEntity; +import org.springframework.security.authentication.AnonymousAuthenticationToken; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.context.SecurityContextImpl; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; + +public class SecurityContextTestUtils { + + public final static String DEFAULT_CLIENT_ID = "APP-5555555555555555"; + + static public void setUpSecurityContext() { + setUpSecurityContext(ScopePathType.ORCID_WORKS_CREATE); + } + + static public void setUpSecurityContext(ScopePathType... scopePathTypes) { + setUpSecurityContext("4444-4444-4444-4441", scopePathTypes); + } + + static public void setUpSecurityContext(String userOrcid, ScopePathType... scopePathTypes) { + setUpSecurityContext(userOrcid, DEFAULT_CLIENT_ID, scopePathTypes); + } + + static public void setUpSecurityContext(String userOrcid, String clientId, ScopePathType... scopePathTypes) { + SecurityContextImpl securityContext = new SecurityContextImpl(); + OrcidBearerTokenAuthentication mockedAuthentication = mock(OrcidBearerTokenAuthentication.class); + securityContext.setAuthentication(mockedAuthentication); + SecurityContextHolder.setContext(securityContext); + when(mockedAuthentication.getPrincipal()).thenReturn(clientId); + when(mockedAuthentication.getUserOrcid()).thenReturn(userOrcid); + Set scopes = new HashSet(); + if (scopePathTypes != null) { + for (ScopePathType scopePathType : scopePathTypes) { + scopes.add(scopePathType.value()); + } + } + when(mockedAuthentication.getScopes()).thenReturn(scopes); + } + + static public void setUpSecurityContextForClientOnly() { + setUpSecurityContextForClientOnly("APP-5555555555555555"); + } + + static public void setUpSecurityContextForClientOnly(String clientId) { + Set scopes = new HashSet(); + scopes.add(ScopePathType.ORCID_PROFILE_CREATE.value()); + setUpSecurityContextForClientOnly(clientId, scopes); + } + + static public void setUpSecurityContextForClientOnly(String clientId, ScopePathType... scopePathTypes) { + Set scopes = new HashSet(); + for (ScopePathType scope : scopePathTypes) { + scopes.add(scope.value()); + } + setUpSecurityContextForClientOnly(clientId, scopes); + } + + static public void setUpSecurityContextForClientOnly(String clientId, Set scopes) { + SecurityContextImpl securityContext = new SecurityContextImpl(); + OrcidBearerTokenAuthentication mockedAuthentication = mock(OrcidBearerTokenAuthentication.class); + securityContext.setAuthentication(mockedAuthentication); + SecurityContextHolder.setContext(securityContext); + when(mockedAuthentication.getPrincipal()).thenReturn(clientId); + when(mockedAuthentication.getScopes()).thenReturn(scopes); + } + + static public void setUpSecurityContextForAnonymous() { + SecurityContextImpl securityContext = new SecurityContextImpl(); + ArrayList authorities = new ArrayList<>(); + authorities.add(new SimpleGrantedAuthority("ROLE_ANONYMOUS")); + AnonymousAuthenticationToken anonToken = new AnonymousAuthenticationToken("testKey", "testToken", authorities); + securityContext.setAuthentication(anonToken); + SecurityContextHolder.setContext(securityContext); + } +} \ No newline at end of file diff --git a/orcid-internal-api/src/main/java/org/orcid/internal/server/delegator/impl/InternalClientCredentialEndPointDelegatorImpl.java b/orcid-internal-api/src/main/java/org/orcid/internal/server/delegator/impl/InternalClientCredentialEndPointDelegatorImpl.java deleted file mode 100644 index aecc2cc001d..00000000000 --- a/orcid-internal-api/src/main/java/org/orcid/internal/server/delegator/impl/InternalClientCredentialEndPointDelegatorImpl.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.orcid.internal.server.delegator.impl; - -import java.util.HashSet; -import java.util.Set; - -import javax.annotation.Resource; -import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.core.Response; - -import org.apache.commons.lang.StringUtils; -import org.orcid.api.common.oauth.OrcidClientCredentialEndPointDelegatorImpl; -import org.orcid.core.constants.OrcidOauth2Constants; -import org.orcid.core.exception.OrcidInvalidScopeException; -import org.orcid.core.locale.LocaleManager; -import org.orcid.jaxb.model.message.ScopePathType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.security.authentication.InsufficientAuthenticationException; -import org.springframework.security.core.Authentication; -import org.springframework.security.oauth2.common.OAuth2AccessToken; -import org.springframework.security.oauth2.common.exceptions.UnsupportedGrantTypeException; -import org.springframework.security.oauth2.common.util.OAuth2Utils; - -/** - * - * @author Angel Montenegro - * - */ -public class InternalClientCredentialEndPointDelegatorImpl extends OrcidClientCredentialEndPointDelegatorImpl { - - private static final Logger LOGGER = LoggerFactory.getLogger(OrcidClientCredentialEndPointDelegatorImpl.class); - - @Resource - protected LocaleManager localeManager; - - @Override - public Response obtainOauth2Token(String authorization, MultivaluedMap formParams) { - String grantType = formParams.getFirst(OrcidOauth2Constants.GRANT_TYPE); - String scopeList = formParams.getFirst(OrcidOauth2Constants.SCOPE_PARAM); - String clientId = formParams.getFirst(OrcidOauth2Constants.CLIENT_ID_PARAM); - // Verify it is a client_credentials grant type request - if(!OrcidOauth2Constants.GRANT_TYPE_CLIENT_CREDENTIALS.equals(grantType)) { - Object params[] = {grantType}; - throw new UnsupportedGrantTypeException(localeManager.resolveMessage("apiError.unsupported_client_type.exception", params)); - } - - Authentication client = getClientAuthentication(); - if (!client.isAuthenticated()) { - LOGGER.info("Not authenticated for OAuth2: clientId={}, grantType={}, scope={}", new Object[] { - clientId, grantType, scopeList }); - throw new InsufficientAuthenticationException(localeManager.resolveMessage("apiError.client_not_authenticated.exception")); - } - - Set scopes = new HashSet(); - if (StringUtils.isNotEmpty(scopeList)) { - scopes = OAuth2Utils.parseParameterList(scopeList); - } - - // Verify it is requesting an internal scope - HashSet filteredScopes = new HashSet(); - for(String scope : scopes) { - ScopePathType scopeType = ScopePathType.fromValue(scope); - if(scopeType.isInternalScope()) { - filteredScopes.add(scope); - } - } - - if(filteredScopes.isEmpty()) { - String message = localeManager.resolveMessage("apiError.9015.developerMessage", new Object[]{}); - throw new OrcidInvalidScopeException(message); - } - - OAuth2AccessToken token = generateToken(client, scopes, grantType); - removeMetadataFromToken(token); - setToCache(client.getName(), token); - return getResponse(token); - } -} diff --git a/orcid-internal-api/src/main/resources/orcid-internal-api-security-context.xml b/orcid-internal-api/src/main/resources/orcid-internal-api-security-context.xml index d27c470afec..0acdab44be1 100644 --- a/orcid-internal-api/src/main/resources/orcid-internal-api-security-context.xml +++ b/orcid-internal-api/src/main/resources/orcid-internal-api-security-context.xml @@ -86,21 +86,5 @@ - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/orcid-pub-web/src/main/java/org/orcid/api/filters/ApiRateLimitFilter.java b/orcid-pub-web/src/main/java/org/orcid/api/filters/ApiRateLimitFilter.java index 865bb8a4b3a..d412a0019cf 100644 --- a/orcid-pub-web/src/main/java/org/orcid/api/filters/ApiRateLimitFilter.java +++ b/orcid-pub-web/src/main/java/org/orcid/api/filters/ApiRateLimitFilter.java @@ -21,9 +21,10 @@ import org.orcid.core.manager.impl.OrcidUrlManager; import org.orcid.core.manager.v3.EmailManager; import org.orcid.core.manager.v3.RecordNameManager; -import org.orcid.core.oauth.service.OrcidTokenStore; +import org.orcid.persistence.dao.OrcidOauth2TokenDetailDao; import org.orcid.persistence.dao.ProfileDao; import org.orcid.persistence.jpa.entities.ClientDetailsEntity; +import org.orcid.persistence.jpa.entities.OrcidOauth2TokenDetail; import org.orcid.persistence.jpa.entities.ProfileEntity; import org.orcid.utils.email.MailGunManager; import org.orcid.utils.panoply.PanoplyRedshiftClient; @@ -68,14 +69,11 @@ public class ApiRateLimitFilter extends OncePerRequestFilter { @Autowired private EmailManager emailManager; - @Resource - private PanoplyRedshiftClient panoplyClient; - @Resource private PapiRateLimitRedisClient papiRedisClient; - @Autowired - private OrcidTokenStore orcidTokenStore; + @Resource(name="orcidOauth2TokenDetailDaoReadOnly") + private OrcidOauth2TokenDetailDao orcidOauth2TokenDetailDaoReadOnly; @Autowired private MessageSource messageSource; @@ -89,7 +87,6 @@ public class ApiRateLimitFilter extends OncePerRequestFilter { @Value("${org.orcid.papi.rate.limit.enabled:false}") private boolean enableRateLimiting; - // :192.168.65.1 127.0.0.1 @Value("${org.orcid.papi.rate.limit.ip.whiteSpaceSeparatedWhiteList:192.168.65.1 127.0.0.1}") private String papiWhiteSpaceSeparatedWhiteList; @@ -146,11 +143,13 @@ public void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletR String ipAddress = getClientIpAddress(httpServletRequest); if (!isIPInCidrWhiteListRange(ipAddress)) { - String clientId = null; if (tokenValue != null) { try { - clientId = orcidTokenStore.readClientId(tokenValue); + OrcidOauth2TokenDetail token = orcidOauth2TokenDetailDaoReadOnly.findByTokenValue(tokenValue); + if(token != null) { + clientId = token.getClientDetailsId(); + } } catch (Exception ex) { LOG.error("Exception when trying to get the client id from token value, ignoring and treating as anonymous client", ex); } diff --git a/orcid-web/src/main/java/org/orcid/frontend/util/AuthorizationRequestLocalCache.java b/orcid-web/src/main/java/org/orcid/frontend/util/AuthorizationRequestLocalCache.java deleted file mode 100644 index bed691f1a07..00000000000 --- a/orcid-web/src/main/java/org/orcid/frontend/util/AuthorizationRequestLocalCache.java +++ /dev/null @@ -1,59 +0,0 @@ -package org.orcid.frontend.util; -import org.ehcache.Cache; -import org.ehcache.CacheManager; -import org.ehcache.config.builders.CacheConfigurationBuilder; -import org.ehcache.config.builders.CacheManagerBuilder; -import org.ehcache.config.builders.ResourcePoolsBuilder; -import org.ehcache.expiry.Duration; -import org.ehcache.expiry.Expirations; -import org.orcid.pojo.ajaxForm.RequestInfoForm; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.security.oauth2.provider.AuthorizationRequest; -import org.springframework.stereotype.Component; - -import javax.annotation.PostConstruct; -import java.util.concurrent.TimeUnit; - -@Component -public class AuthorizationRequestLocalCache { - - @Value("${org.orcid.core.session.localCache.ttl:10800}") - private int cacheTTLInSeconds; - - @Value("${org.orcid.core.session.localCache.heap:50000}") - private int heapSize; - - private CacheManager cacheManager; - private Cache cache; - - public AuthorizationRequestLocalCache() { - cacheManager = CacheManagerBuilder - .newCacheManagerBuilder().build(); - cacheManager.init(); - } - - @PostConstruct - public void initCache() { - cache = cacheManager - .createCache("authorizationRequestLocalCache", CacheConfigurationBuilder - .newCacheConfigurationBuilder( - String.class, AuthorizationRequest.class, - ResourcePoolsBuilder.heap(heapSize)).withExpiry(Expirations.timeToLiveExpiration(Duration.of(cacheTTLInSeconds, TimeUnit.SECONDS)))); - } - - public AuthorizationRequest get(String key) { - return cache.get(key); - } - - public void put(String key, AuthorizationRequest value) { - cache.put(key, value); - } - - public void remove(String key) { - cache.remove(key); - } - - public boolean containsKey(String key) { - return cache.containsKey(key); - } -} diff --git a/orcid-web/src/main/java/org/orcid/frontend/util/OriginalAuthorizationRequestLocalCache.java b/orcid-web/src/main/java/org/orcid/frontend/util/OriginalAuthorizationRequestLocalCache.java deleted file mode 100644 index 47fb42e7013..00000000000 --- a/orcid-web/src/main/java/org/orcid/frontend/util/OriginalAuthorizationRequestLocalCache.java +++ /dev/null @@ -1,59 +0,0 @@ -package org.orcid.frontend.util; -import org.ehcache.Cache; -import org.ehcache.CacheManager; -import org.ehcache.config.builders.CacheConfigurationBuilder; -import org.ehcache.config.builders.CacheManagerBuilder; -import org.ehcache.config.builders.ResourcePoolsBuilder; -import org.ehcache.expiry.Duration; -import org.ehcache.expiry.Expirations; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.security.oauth2.provider.AuthorizationRequest; -import org.springframework.stereotype.Component; - -import javax.annotation.PostConstruct; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -@Component -public class OriginalAuthorizationRequestLocalCache { - - @Value("${org.orcid.core.session.localCache.ttl:900}") - private int cacheTTLInSeconds; - - @Value("${org.orcid.core.session.localCache.heap:10000}") - private int heapSize; - - private CacheManager cacheManager; - private Cache cache; - - public OriginalAuthorizationRequestLocalCache() { - cacheManager = CacheManagerBuilder - .newCacheManagerBuilder().build(); - cacheManager.init(); - } - - @PostConstruct - public void initCache() { - cache = cacheManager - .createCache("originalAuthorizationRequestLocalCache", CacheConfigurationBuilder - .newCacheConfigurationBuilder( - String.class, Map.class, - ResourcePoolsBuilder.heap(heapSize)).withExpiry(Expirations.timeToLiveExpiration(Duration.of(cacheTTLInSeconds, TimeUnit.SECONDS)))); - } - - public Map get(String key) { - return cache.get(key); - } - - public void put(String key, Map value) { - cache.put(key, value); - } - - public void remove(String key) { - cache.remove(key); - } - - public boolean containsKey(String key) { - return cache.containsKey(key); - } -} diff --git a/orcid-web/src/main/java/org/orcid/frontend/util/RequestInfoFormLocalCache.java b/orcid-web/src/main/java/org/orcid/frontend/util/RequestInfoFormLocalCache.java deleted file mode 100644 index 9e0450cdb7e..00000000000 --- a/orcid-web/src/main/java/org/orcid/frontend/util/RequestInfoFormLocalCache.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.orcid.frontend.util; -import org.ehcache.Cache; -import org.ehcache.CacheManager; -import org.ehcache.config.builders.CacheConfigurationBuilder; -import org.ehcache.config.builders.CacheManagerBuilder; -import org.ehcache.config.builders.ResourcePoolsBuilder; -import org.ehcache.expiry.Duration; -import org.ehcache.expiry.Expirations; -import org.orcid.pojo.ajaxForm.RequestInfoForm; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -import javax.annotation.PostConstruct; -import java.util.concurrent.TimeUnit; - -@Component -public class RequestInfoFormLocalCache { - - @Value("${org.orcid.core.session.localCache.ttl:10800}") - private int cacheTTLInSeconds; - - @Value("${org.orcid.core.session.localCache.heap:50000}") - private int heapSize; - - private CacheManager cacheManager; - private Cache cache; - - public RequestInfoFormLocalCache() { - cacheManager = CacheManagerBuilder - .newCacheManagerBuilder().build(); - cacheManager.init(); - } - - @PostConstruct - public void initCache() { - cache = cacheManager - .createCache("requestInfoFormLocalCache", CacheConfigurationBuilder - .newCacheConfigurationBuilder( - String.class, RequestInfoForm.class, - ResourcePoolsBuilder.heap(heapSize)).withExpiry(Expirations.timeToLiveExpiration(Duration.of(cacheTTLInSeconds, TimeUnit.SECONDS)))); - } - - public RequestInfoForm get(String key) { - return cache.get(key); - } - - public void put(String key, RequestInfoForm value) { - cache.put(key, value); - } - - public void remove(String key) { - cache.remove(key); - } - - public boolean containsKey(String key) { - return cache.containsKey(key); - } -} diff --git a/orcid-web/src/main/java/org/orcid/frontend/web/controllers/BaseWorkspaceController.java b/orcid-web/src/main/java/org/orcid/frontend/web/controllers/BaseWorkspaceController.java index 7ab1b0ac170..b44002aa3bc 100644 --- a/orcid-web/src/main/java/org/orcid/frontend/web/controllers/BaseWorkspaceController.java +++ b/orcid-web/src/main/java/org/orcid/frontend/web/controllers/BaseWorkspaceController.java @@ -20,7 +20,6 @@ import org.orcid.core.manager.EncryptionManager; import org.orcid.core.manager.v3.ActivityManager; import org.orcid.core.manager.v3.ProfileEntityManager; -import org.orcid.core.security.visibility.filter.VisibilityFilter; import org.orcid.frontend.web.util.NumberList; import org.orcid.frontend.web.util.YearsList; import org.orcid.pojo.ajaxForm.Contributor; @@ -45,9 +44,6 @@ public class BaseWorkspaceController extends BaseController { @Resource(name = "profileEntityManagerV3") protected ProfileEntityManager profileEntityManager; - @Resource(name = "visibilityFilter") - protected VisibilityFilter visibilityFilter; - @Resource private ProfileLastModifiedAspect profileLastModifiedAspect; diff --git a/orcid-web/src/main/java/org/orcid/frontend/web/controllers/FindMyStuffController.java b/orcid-web/src/main/java/org/orcid/frontend/web/controllers/FindMyStuffController.java index 318a0ec8cfd..880b2a57212 100644 --- a/orcid-web/src/main/java/org/orcid/frontend/web/controllers/FindMyStuffController.java +++ b/orcid-web/src/main/java/org/orcid/frontend/web/controllers/FindMyStuffController.java @@ -6,7 +6,6 @@ import org.orcid.core.manager.v3.FindMyStuffManager; import org.orcid.core.manager.v3.NotificationManager; -import org.orcid.core.oauth.OrcidOauth2TokenDetailService; import org.orcid.pojo.FindMyStuffResult; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; @@ -21,9 +20,6 @@ public class FindMyStuffController extends BaseController{ @Resource NotificationController notificationsController; - @Resource - private OrcidOauth2TokenDetailService orcidOauth2TokenService; - @Resource(name = "notificationManagerV3") private NotificationManager notificationManager; diff --git a/orcid-web/src/main/java/org/orcid/frontend/web/controllers/LoginController.java b/orcid-web/src/main/java/org/orcid/frontend/web/controllers/LoginController.java index 8269f518a03..989c5f3dc1e 100644 --- a/orcid-web/src/main/java/org/orcid/frontend/web/controllers/LoginController.java +++ b/orcid-web/src/main/java/org/orcid/frontend/web/controllers/LoginController.java @@ -1,87 +1,29 @@ package org.orcid.frontend.web.controllers; -import java.io.IOException; import java.io.UnsupportedEncodingException; -import java.net.MalformedURLException; -import java.util.UUID; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.apache.commons.lang3.StringUtils; -import org.codehaus.jettison.json.JSONException; -import org.codehaus.jettison.json.JSONObject; -import org.orcid.core.common.manager.EventManager; import org.orcid.core.constants.OrcidOauth2Constants; -import org.orcid.core.manager.ClientDetailsEntityCacheManager; -import org.orcid.core.manager.UserConnectionManager; import org.orcid.core.manager.v3.read_only.RecordNameManagerReadOnly; -import org.orcid.core.security.OrcidUserDetailsService; -import org.orcid.core.togglz.Features; -import org.orcid.frontend.spring.web.social.config.SocialSignInUtils; -import org.orcid.frontend.spring.web.social.config.SocialType; -import org.orcid.frontend.spring.web.social.config.UserCookieGenerator; -import org.orcid.frontend.util.RequestInfoFormLocalCache; -import org.orcid.frontend.web.controllers.helper.OauthHelper; -import org.orcid.jaxb.model.message.ScopePathType; import org.orcid.jaxb.model.v3.release.common.Visibility; import org.orcid.jaxb.model.v3.release.record.Name; -import org.orcid.persistence.jpa.entities.EventType; -import org.orcid.persistence.jpa.entities.ProfileEntity; -import org.orcid.persistence.jpa.entities.UserconnectionEntity; -import org.orcid.persistence.jpa.entities.UserconnectionPK; import org.orcid.pojo.ajaxForm.Names; -import org.orcid.pojo.ajaxForm.PojoUtil; -import org.orcid.pojo.ajaxForm.RequestInfoForm; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.HttpStatus; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.ModelAndView; -import org.springframework.web.servlet.view.RedirectView; @Controller("loginController") -public class LoginController extends OauthControllerBase { - - private static final Logger LOGGER = LoggerFactory.getLogger(LoginController.class); - - @Resource - protected ClientDetailsEntityCacheManager clientDetailsEntityCacheManager; +public class LoginController extends BaseController { @Resource(name = "recordNameManagerV3") private RecordNameManagerReadOnly recordNameManager; - @Resource - protected UserConnectionManager userConnectionManager; - - @Resource - private OrcidUserDetailsService orcidUserDetailsService; - - @Resource - private UserCookieGenerator userCookieGenerator; - - @Resource - private SocialSignInUtils socialSignInUtils; - - @Resource - private OauthHelper oauthHelper; - - @Resource - private EventManager eventManager; - - @Resource - private RequestInfoFormLocalCache requestInfoFormLocalCache; - @RequestMapping(value = "/account/names/{type}", method = RequestMethod.GET) public @ResponseBody Names getAccountNames(@PathVariable String type) { String currentOrcid = getCurrentUserOrcid(); @@ -100,11 +42,6 @@ public class LoginController extends OauthControllerBase { @RequestMapping(value = { "/signin", "/login" }, method = RequestMethod.GET) public ModelAndView loginGetHandler(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException { String query = request.getQueryString(); - if (!PojoUtil.isEmpty(query)) { - if (query.contains("oauth")) { - return handleOauthSignIn(request, response); - } - } ModelAndView mav = new ModelAndView("login"); if (!domainsAllowingRobots.contains(orcidUrlManager.getBaseDomainRmProtocall())) { mav.addObject("noIndex", true); @@ -140,233 +77,4 @@ public String sessionExpiredHandler() { return "session_expired"; } - private ModelAndView handleOauthSignIn(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException { - String queryString = request.getQueryString(); - String redirectUri = null; - - // Get and save the request information form - RequestInfoForm requestInfoForm; - try { - requestInfoForm = oauthHelper.generateRequestInfoForm(queryString); - } catch (Exception e) { - // convert to a 400 - ModelAndView mav = new ModelAndView("oauth-error"); - mav.setStatus(HttpStatus.BAD_REQUEST); - mav.addObject("noIndex", true); - return mav; - } - - // force a login even if the user is already logged in if openid - // prompt=login param present - boolean forceLogin = false; - if (!PojoUtil.isEmpty(requestInfoForm.getScopesAsString()) - && ScopePathType.getScopesFromSpaceSeparatedString(requestInfoForm.getScopesAsString()).contains(ScopePathType.OPENID)) { - String prompt = request.getParameter(OrcidOauth2Constants.PROMPT); - if (prompt != null && prompt.equals(OrcidOauth2Constants.PROMPT_LOGIN)) { - forceLogin = true; - } - } - - // Check if user is already logged in, if so, redirect it to - // oauth/authorize - UserDetails userDetails = getCurrentUser(); - if (!forceLogin && userDetails != null) { - redirectUri = orcidUrlManager.getBaseUrl() + "/oauth/authorize?"; - queryString = queryString.replace("oauth&", ""); - redirectUri = redirectUri + queryString; - RedirectView rView = new RedirectView(redirectUri); - return new ModelAndView(rView); - } - - // Redirect URI - redirectUri = requestInfoForm.getRedirectUrl(); - - // Check that the client have the required permissions - // Get client name - String clientId = requestInfoForm.getClientId(); - if (PojoUtil.isEmpty(clientId)) { - String redirectUriWithParams = redirectUri + "?error=invalid_client&error_description=invalid client_id"; - return new ModelAndView(new RedirectView(redirectUriWithParams)); - } - - // handle openID prompt and max_age behaviour - // here we remove prompt=login if present - // here we remove max_age if present - // - if (!PojoUtil.isEmpty(requestInfoForm.getScopesAsString()) - && ScopePathType.getScopesFromSpaceSeparatedString(requestInfoForm.getScopesAsString()).contains(ScopePathType.OPENID)) { - String prompt = request.getParameter(OrcidOauth2Constants.PROMPT); - if (prompt != null && prompt.equals(OrcidOauth2Constants.PROMPT_NONE)) { - String redirectUriWithParams = requestInfoForm.getRedirectUrl(); - - if (requestInfoForm.getResponseType().contains(OrcidOauth2Constants.CODE_RESPONSE_TYPE)) - redirectUriWithParams += "?"; - else - redirectUriWithParams += "#"; - - redirectUriWithParams += "error=login_required"; - RedirectView rView = new RedirectView(redirectUriWithParams); - ModelAndView error = new ModelAndView(); - error.setView(rView); - return error; - } - if (prompt != null && prompt.equals(OrcidOauth2Constants.PROMPT_CONFIRM)) { - // keep - handled by OAuthAuthorizeController - } else if (prompt != null && prompt.equals(OrcidOauth2Constants.PROMPT_LOGIN)) { - // remove because otherwise we'll end up back here again! - queryString = removeQueryStringParams(queryString, OrcidOauth2Constants.PROMPT); - } - if (request.getParameter(OrcidOauth2Constants.MAX_AGE) != null) { - // remove because otherwise we'll end up back here again! - queryString = removeQueryStringParams(queryString, OrcidOauth2Constants.MAX_AGE); - } - } - - requestInfoFormLocalCache.put(request.getSession().getId(), requestInfoForm); - // Save also the original query string - request.getSession().setAttribute(OrcidOauth2Constants.OAUTH_QUERY_STRING, queryString); - // Save a flag to indicate this is a request from the new - request.getSession().setAttribute(OrcidOauth2Constants.OAUTH_2SCREENS, true); - ModelAndView mav = new ModelAndView("login"); - mav.addObject("noIndex", true); - return mav; - } - - @RequestMapping(value = { "/signin/facebook" }, method = RequestMethod.POST) - public RedirectView initFacebookLogin(HttpServletRequest request) { - String sessionState = UUID.randomUUID().toString(); - request.getSession().setAttribute("f_state", sessionState); - return socialSignInUtils.initFacebookLogin(sessionState); - } - - @RequestMapping(value = { "/signin/facebook" }, method = RequestMethod.GET) - public ModelAndView getFacebookLogin(HttpServletRequest request, HttpServletResponse response, @RequestParam(name = "state") String state, - @RequestParam(name = "code", required = false) String code, @RequestParam(name = "error", required = false) String error, - @RequestParam(name = "error_code", required = false) String errorCode, @RequestParam(name = "error_description", required = false) String errorDescription, - @RequestParam(name = "error_reason", required = false) String errorReason) throws UnsupportedEncodingException, IOException, JSONException { - String facebookSessionState = (String) request.getSession().getAttribute("f_state"); - if (!state.equals(facebookSessionState)) { - LOGGER.warn("Facebook session state doesnt match"); - return new ModelAndView("redirect:" + calculateRedirectUrl("/login")); - } - - if (StringUtils.isBlank(code)) { - LOGGER.warn("Can't login to Facebook, {}: {}", error, errorDescription); - return new ModelAndView("redirect:" + calculateRedirectUrl("/login")); - } - - JSONObject userData = socialSignInUtils.getFacebookUserData(code); - return processSocialLogin(request, response, SocialType.FACEBOOK, userData); - } - - @RequestMapping(value = { "/signin/google" }, method = RequestMethod.POST) - public RedirectView initGoogleLogin(HttpServletRequest request) { - String sessionState = UUID.randomUUID().toString(); - request.getSession().setAttribute("g_state", sessionState); - return socialSignInUtils.initGoogleLogin(sessionState); - } - - @RequestMapping(value = { "/signin/google" }, method = RequestMethod.GET) - public ModelAndView getGoogleLogin(HttpServletRequest request, HttpServletResponse response, @RequestParam(name = "state") String state, - @RequestParam(name = "code", required = false) String code) throws MalformedURLException, IOException, JSONException { - String googleSessionState = (String) request.getSession().getAttribute("g_state"); - if (!state.equals(googleSessionState)) { - LOGGER.warn("Google session state doesnt match"); - return new ModelAndView("redirect:" + calculateRedirectUrl("/login")); - } - if (StringUtils.isBlank(code)) { - LOGGER.warn("Can't login to Google"); - return new ModelAndView("redirect:" + calculateRedirectUrl("/login")); - } - - JSONObject userData = socialSignInUtils.getGoogleUserData(code); - return processSocialLogin(request, response, SocialType.GOOGLE, userData); - } - - private ModelAndView processSocialLogin(HttpServletRequest request, HttpServletResponse response, SocialType socialType, JSONObject userData) throws JSONException { - String providerUserId = userData.getString(OrcidOauth2Constants.PROVIDER_USER_ID); - String accessToken = userData.getString(OrcidOauth2Constants.ACCESS_TOKEN); - Long expiresIn = Long.valueOf(userData.getString(OrcidOauth2Constants.EXPIRES_IN)); - - UserconnectionEntity userConnection = userConnectionManager.findByProviderIdAndProviderUserId(userData.getString(OrcidOauth2Constants.PROVIDER_USER_ID), - socialType.value()); - String userConnectionId = null; - ModelAndView view = null; - if (userConnection != null) { - userConnectionId = userConnection.getId().getUserid(); - // Store relevant data in the session - socialSignInUtils.setSignedInData(request, userData); - - if(userConnection.isLinked()) { - // If user exists and is linked update user connection info - // and redirect to user record - view = updateUserConnectionAndSocialLogUserIn(request, response, socialType, userConnection.getOrcid(), userConnection.getId().getUserid(), providerUserId, - accessToken, expiresIn); - } else { - // Forward to account link page - view = socialLinking(request); - } - } else { - // Store relevant data in the session - socialSignInUtils.setSignedInData(request, userData); - // Store user info - userConnectionId = createUserConnection(socialType, providerUserId, userData.getString(OrcidOauth2Constants.EMAIL), - userData.getString(OrcidOauth2Constants.DISPLAY_NAME), accessToken, expiresIn); - // Forward to account link page - view = socialLinking(request); - } - if (userConnectionId == null) { - throw new IllegalArgumentException("Unable to find userConnectionId for providerUserId = " + providerUserId); - } - - if (Features.EVENTS.isActive()) { - eventManager.createEvent(EventType.SIGN_IN, request); - } - userCookieGenerator.addCookie(userConnectionId, response); - - if ("social_2FA".equals(view.getViewName())) { - return new ModelAndView("redirect:" + calculateRedirectUrl("/2fa-signin?social=true")); - } - - return view; - } - - private String createUserConnection(SocialType socialType, String providerUserId, String email, String userName, String accessToken, Long expireTime) { - LOGGER.info("Creating userconnection for type={}, providerUserId={}, userName={}", new Object[] { socialType.value(), providerUserId, userName }); - return userConnectionManager.create(providerUserId, socialType.value(), email, userName, accessToken, expireTime); - } - - private ModelAndView updateUserConnectionAndSocialLogUserIn(HttpServletRequest request, HttpServletResponse response, SocialType socialType, String userOrcid, - String userConnectionId, String providerUserId, String accessToken, Long expiresIn) { - LOGGER.info("Updating existing userconnection for orcid={}, type={}, providerUserId={}", new Object[] { userOrcid, socialType.value(), providerUserId }); - // Update user connection info - userConnectionManager.update(providerUserId, socialType.value(), accessToken, expiresIn); - - // Log user in - ProfileEntity profileEntity = profileEntityCacheManager.retrieve(userOrcid); - if (profileEntity.getUsing2FA()) { - return new ModelAndView("social_2FA"); - } - - UserconnectionPK pk = new UserconnectionPK(userConnectionId, socialType.value(), providerUserId); - String aCredentials = socialType.value() + ':' + providerUserId; - PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(userOrcid, aCredentials); - token.setDetails(orcidUserDetailsService.loadUserByProfile(profileEntity)); - Authentication authentication = authenticationManager.authenticate(token); - userConnectionManager.updateLoginInformation(pk); - - // Update security context with user information - SecurityContextHolder.getContext().setAuthentication(authentication); - return new ModelAndView(new RedirectView(calculateRedirectUrl(request, response, false, false, "social"))); - } - - private ModelAndView socialLinking(HttpServletRequest request) { - String socialLinking = "/social-linking"; - String queryString = (String) request.getSession().getAttribute(OrcidOauth2Constants.OAUTH_QUERY_STRING); - if (queryString != null) { - socialLinking = socialLinking + "?" + queryString; - } - return new ModelAndView(new RedirectView(orcidUrlManager.getBaseUrl() + socialLinking, true)); - - } } diff --git a/orcid-web/src/main/java/org/orcid/frontend/web/controllers/OauthAuthorizeController.java b/orcid-web/src/main/java/org/orcid/frontend/web/controllers/OauthAuthorizeController.java deleted file mode 100644 index a538b7287f1..00000000000 --- a/orcid-web/src/main/java/org/orcid/frontend/web/controllers/OauthAuthorizeController.java +++ /dev/null @@ -1,337 +0,0 @@ -package org.orcid.frontend.web.controllers; - -import java.io.UnsupportedEncodingException; -import java.util.HashMap; -import java.util.Map; - -import javax.annotation.Resource; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.orcid.core.constants.OrcidOauth2Constants; -import org.orcid.core.exception.ClientDeactivatedException; -import org.orcid.core.exception.LockedException; -import org.orcid.core.common.manager.EventManager; -import org.orcid.core.manager.v3.ProfileEntityManager; -import org.orcid.core.oauth.OrcidRandomValueTokenServices; -import org.orcid.core.togglz.Features; -import org.orcid.frontend.util.AuthorizationRequestLocalCache; -import org.orcid.frontend.util.OriginalAuthorizationRequestLocalCache; -import org.orcid.frontend.util.RequestInfoFormLocalCache; -import org.orcid.frontend.web.controllers.helper.OauthHelper; -import org.orcid.jaxb.model.message.ScopePathType; -import org.orcid.persistence.jpa.entities.ClientDetailsEntity; -import org.orcid.persistence.jpa.entities.EventType; -import org.orcid.pojo.ajaxForm.OauthAuthorizeForm; -import org.orcid.pojo.ajaxForm.PojoUtil; -import org.orcid.pojo.ajaxForm.RequestInfoForm; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.oauth2.common.exceptions.InvalidRequestException; -import org.springframework.security.oauth2.common.exceptions.InvalidScopeException; -import org.springframework.security.oauth2.common.util.OAuth2Utils; -import org.springframework.security.oauth2.provider.AuthorizationRequest; -import org.springframework.security.web.savedrequest.HttpSessionRequestCache; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.bind.support.SimpleSessionStatus; -import org.springframework.web.servlet.ModelAndView; -import org.springframework.web.servlet.view.RedirectView; - -@Controller("oauthAuthorizeController") -public class OauthAuthorizeController extends OauthControllerBase { - - private static final Logger LOGGER = LoggerFactory.getLogger(OauthAuthorizeController.class); - - @Resource - protected OrcidRandomValueTokenServices tokenServices; - - @Resource(name = "profileEntityManagerV3") - private ProfileEntityManager profileEntityManager; - - @Resource - private OauthHelper oauthHelper; - - @Resource - private EventManager eventManager; - - @Resource - private RequestInfoFormLocalCache requestInfoFormLocalCache; - - @Resource - private AuthorizationRequestLocalCache authorizationRequestLocalCache; - - @Resource - private OriginalAuthorizationRequestLocalCache originalAuthorizationRequestLocalCache; - - /** This is called if user is already logged in. - * Checks permissions have been granted to client and generates access code. - * - * @param request - * @param response - * @param mav - * @return - * @throws UnsupportedEncodingException - */ - @RequestMapping(value = "/oauth/confirm_access", method = RequestMethod.GET) - public ModelAndView loginGetHandler(HttpServletRequest request, HttpServletResponse response, ModelAndView mav) throws UnsupportedEncodingException { - //Get and save the request information form - - String queryString = request.getQueryString(); - RequestInfoForm requestInfoForm = oauthHelper.generateRequestInfoForm(queryString); - - // Store the request info form in the cache - requestInfoFormLocalCache.put(request.getSession().getId(), requestInfoForm); - - boolean usePersistentTokens = false; - - ClientDetailsEntity clientDetails = clientDetailsEntityCacheManager.retrieve(requestInfoForm.getClientId()); - // validate client scopes - try { - authorizationEndpoint.validateScope(requestInfoForm.getScopesAsString(), clientDetails,requestInfoForm.getResponseType()); - orcidOAuth2RequestValidator.validateClientIsEnabled(clientDetails); - } catch (InvalidScopeException | ClientDeactivatedException | LockedException e) { - String redirectUriWithParams = requestInfoForm.getRedirectUrl(); - if(e instanceof InvalidScopeException) { - redirectUriWithParams += "?error=invalid_scope&error_description=" + e.getMessage(); - } else if (e instanceof LockedException) { - redirectUriWithParams += "?error=client_locked&error_description=" + e.getMessage(); - } else { - redirectUriWithParams += "?error=client_deactivated&error_description=" + e.getMessage(); - } - RedirectView rView = new RedirectView(redirectUriWithParams); - ModelAndView error = new ModelAndView(); - error.setView(rView); - return error; - } - - //implicit id_token requests must have nonce. - if (!PojoUtil.isEmpty(requestInfoForm.getScopesAsString()) - && ScopePathType.getScopesFromSpaceSeparatedString(requestInfoForm.getScopesAsString()).contains(ScopePathType.OPENID) - && request.getParameter(OAuth2Utils.RESPONSE_TYPE).contains("id_token") - && request.getParameter(OrcidOauth2Constants.NONCE) == null) { - String redirectUriWithParams = requestInfoForm.getRedirectUrl(); - redirectUriWithParams += "#error=invalid_request "; - RedirectView rView = new RedirectView(redirectUriWithParams); - ModelAndView error = new ModelAndView(); - error.setView(rView); - return error; - } - - //Check for prompt=login and max_age. This is a MUST in the openid spec. - //If found redirect back to the signin page. - //Add check for prompt=confirm here. This is a SHOULD in the openid spec. - //If found, force user to confirm permissions. - boolean forceConfirm = false; - if (!PojoUtil.isEmpty(requestInfoForm.getScopesAsString()) && ScopePathType.getScopesFromSpaceSeparatedString(requestInfoForm.getScopesAsString()).contains(ScopePathType.OPENID) ){ - String prompt = request.getParameter(OrcidOauth2Constants.PROMPT); - String maxAge = request.getParameter(OrcidOauth2Constants.MAX_AGE); - String orcid = getEffectiveUserOrcid(); - if (maxAge!=null){ - //if maxAge+lastlogin > now, force login. max_age is in seconds. - java.util.Date authTime = profileEntityManager.getLastLogin(orcid); //is also on the entity. - try{ - long max = Long.parseLong(maxAge); - if (authTime == null || ((authTime.getTime() + (max*1000)) < (new java.util.Date()).getTime())){ - return redirectToForceSignin(request); - } - }catch(NumberFormatException e){ - //ignore - } - } - if (prompt != null && prompt.equals(OrcidOauth2Constants.PROMPT_CONFIRM)){ - forceConfirm=true; - }else if (prompt!=null && prompt.equals(OrcidOauth2Constants.PROMPT_LOGIN)){ - return redirectToForceSignin(request); - } - } - - // Check if the client has persistent tokens enabled - if (clientDetails.isPersistentTokensEnabled()) { - usePersistentTokens = true; - } - - if (!forceConfirm && usePersistentTokens) { - boolean tokenLongLifeAlreadyExists = tokenServices.longLifeTokenExist(requestInfoForm.getClientId(), getEffectiveUserOrcid(), OAuth2Utils.parseParameterList(requestInfoForm.getScopesAsString())); - if (tokenLongLifeAlreadyExists) { - AuthorizationRequest authorizationRequest = authorizationRequestLocalCache.get(request.getSession().getId()); - Authentication auth = SecurityContextHolder.getContext().getAuthentication(); - Map requestParams = new HashMap(); - copyRequestParameters(request, requestParams); - Map approvalParams = new HashMap(); - - requestParams.put(OAuth2Utils.USER_OAUTH_APPROVAL, "true"); - approvalParams.put(OAuth2Utils.USER_OAUTH_APPROVAL, "true"); - - requestParams.put(OrcidOauth2Constants.TOKEN_VERSION, OrcidOauth2Constants.PERSISTENT_TOKEN); - - boolean hasPersistent = hasPersistenTokensEnabled(requestInfoForm.getClientId()); - // Don't let non persistent clients persist - if (!hasPersistent && "true".equals(requestParams.get(OrcidOauth2Constants.GRANT_PERSISTENT_TOKEN))){ - requestParams.put(OrcidOauth2Constants.GRANT_PERSISTENT_TOKEN, "false"); - } - //default to client default if not set - if (requestParams.get(OrcidOauth2Constants.GRANT_PERSISTENT_TOKEN) == null) { - if (hasPersistent) - requestParams.put(OrcidOauth2Constants.GRANT_PERSISTENT_TOKEN, "true"); - else - requestParams.put(OrcidOauth2Constants.GRANT_PERSISTENT_TOKEN, "false"); - } - - // Session status - SimpleSessionStatus status = new SimpleSessionStatus(); - - authorizationRequest.setRequestParameters(requestParams); - // Authorization request model - Map model = new HashMap(); - model.put("authorizationRequest", authorizationRequest); - - // Approve using the spring authorization endpoint code. - //note this will also handle generting implicit tokens via getTokenGranter().grant("implicit",new ImplicitTokenRequest(tokenRequest, storedOAuth2Request)); - RedirectView view = (RedirectView) authorizationEndpoint.approveOrDeny(approvalParams, model, status, auth); - ModelAndView authCodeView = new ModelAndView(); - authCodeView.setView(view); - return authCodeView; - } - } - - if (!PojoUtil.isEmpty(requestInfoForm.getScopesAsString()) && ScopePathType.getScopesFromSpaceSeparatedString(requestInfoForm.getScopesAsString()).contains(ScopePathType.OPENID) ){ - String prompt = request.getParameter(OrcidOauth2Constants.PROMPT); - if (prompt!=null && prompt.equals(OrcidOauth2Constants.PROMPT_NONE)){ - String redirectUriWithParams = requestInfoForm.getRedirectUrl(); - redirectUriWithParams += "#error=interaction_required"; - RedirectView rView = new RedirectView(redirectUriWithParams); - ModelAndView error = new ModelAndView(); - error.setView(rView); - return error; - } - } - mav.addObject("noIndex", true); - mav.addObject("hideSupportWidget", true); - mav.setViewName("confirm-oauth-access"); - return mav; - } - - @RequestMapping(value = { "/oauth/custom/authorize.json" }, method = RequestMethod.POST) - public @ResponseBody RequestInfoForm authorize(HttpServletRequest request, HttpServletResponse response, @RequestBody OauthAuthorizeForm form) { - RequestInfoForm requestInfoForm = new RequestInfoForm(); - Authentication auth = SecurityContextHolder.getContext().getAuthentication(); - AuthorizationRequest authorizationRequest = authorizationRequestLocalCache.get(request.getSession().getId()); - if (requestInfoFormLocalCache.containsKey(request.getSession().getId())) { - requestInfoForm = requestInfoFormLocalCache.get(request.getSession().getId()); - } else if (authorizationRequest != null) { - requestInfoForm.setRedirectUrl(authorizationRequest.getRedirectUri()); - requestInfoForm.setClientId(authorizationRequest.getClientId()); - } else { - LOGGER.warn("Both authorizationRequest and requestInfoForm caches are empty"); - } - - Map requestParams = new HashMap(authorizationRequest.getRequestParameters()); - Map approvalParams = new HashMap(); - - // Add the persistent token information - if (form.getApproved()) { - requestParams.put(OAuth2Utils.USER_OAUTH_APPROVAL, "true"); - approvalParams.put(OAuth2Utils.USER_OAUTH_APPROVAL, "true"); - } else { - requestParams.put(OAuth2Utils.USER_OAUTH_APPROVAL, "false"); - approvalParams.put(OAuth2Utils.USER_OAUTH_APPROVAL, "false"); - } - requestParams.put(OrcidOauth2Constants.TOKEN_VERSION, OrcidOauth2Constants.PERSISTENT_TOKEN); - // Check if the client have persistent tokens enabled - requestParams.put(OrcidOauth2Constants.GRANT_PERSISTENT_TOKEN, "false"); - if (hasPersistenTokensEnabled(requestInfoForm.getClientId())) - // Then check if the client granted the persistent token - if (form.getPersistentTokenEnabled()) - requestParams.put(OrcidOauth2Constants.GRANT_PERSISTENT_TOKEN, "true"); - - // strip /email/read-private scope if user has not consented - if (requestInfoForm.containsEmailReadPrivateScope() && !form.isEmailAccessAllowed()) { - requestInfoForm.removeEmailReadPrivateScope(); - requestParams.put(OrcidOauth2Constants.SCOPE_PARAM, requestInfoForm.getScopesAsString()); - } - - // Session status - SimpleSessionStatus status = new SimpleSessionStatus(); - - authorizationRequest.setRequestParameters(requestParams); - // Authorization request model - Map model = new HashMap(); - model.put("authorizationRequest", authorizationRequest); - Map originalRequest = originalAuthorizationRequestLocalCache.get(request.getSession().getId()); - if(originalRequest != null) { - model.put(OrcidOauth2Constants.ORIGINAL_AUTHORIZATION_REQUEST, originalRequest); - } - - // Approve - try { - RedirectView view = (RedirectView) authorizationEndpoint.approveOrDeny(approvalParams, model, status, auth); - requestInfoForm.setRedirectUrl(view.getUrl()); - } catch (InvalidRequestException ire) { - LOGGER.error("Something changed on the request, here are the authorization request and original authorization request values:"); - LOGGER.error("Client id: original '{}' latest '{}'", originalRequest.get(OrcidOauth2Constants.CLIENT_ID), authorizationRequest.getClientId()); - LOGGER.error("State: original '{}' latest '{}'", originalRequest.get(OrcidOauth2Constants.STATE_PARAM), authorizationRequest.getState()); - LOGGER.error("Redirect uri: original '{}' latest '{}'", originalRequest.get(OrcidOauth2Constants.REDIRECT_URI_PARAM), authorizationRequest.getRedirectUri()); - LOGGER.error("Response type: original '{}' latest '{}'", originalRequest.get(OrcidOauth2Constants.RESPONSE_TYPE_PARAM), authorizationRequest.getResponseTypes()); - LOGGER.error("Scope: original '{}' latest '{}'", originalRequest.get(OrcidOauth2Constants.SCOPE_PARAM), authorizationRequest.getScope()); - LOGGER.error("Approved: original '{}' latest '{}'", originalRequest.get("approved"), authorizationRequest.isApproved()); - LOGGER.error("Resource Ids: original '{}' latest '{}'", originalRequest.get("resourceIds"), authorizationRequest.getResourceIds()); - LOGGER.error("Authorities: original '{}' latest '{}'", originalRequest.get("authorities"), authorizationRequest.getAuthorities()); - // Propagate the exception - throw ire; - } - if (Features.EVENTS.isActive()) { - EventType eventType = "true".equals(approvalParams.get("user_oauth_approval")) ? EventType.AUTHORIZE : EventType.AUTHORIZE_DENY; - String orcid = null; - Object principal = auth.getPrincipal(); - if (principal instanceof UserDetails) { - orcid = ((UserDetails) principal).getUsername(); - } else { - orcid = auth.getPrincipal().toString(); - } - eventManager.createEvent(eventType, request); - } - if(new HttpSessionRequestCache().getRequest(request, response) != null) - new HttpSessionRequestCache().removeRequest(request, response); - LOGGER.info("OauthConfirmAccessController form.getRedirectUri being sent to client browser: " + requestInfoForm.getRedirectUrl()); - //Oauth has been finalized, hence, remove the oauth flag from the session - - // Remove the request info form from the cache - requestInfoFormLocalCache.remove(request.getSession().getId()); - - request.getSession().removeAttribute(OrcidOauth2Constants.OAUTH_2SCREENS); - return requestInfoForm; - } - - /** - * Copies all request parameters into the provided params map - * - * @param request - * The server request - * @param params - * The map to copy the params - * */ - private void copyRequestParameters(HttpServletRequest request, Map params) { - if (request != null && request.getParameterMap() != null) { - Map savedParams = request.getParameterMap(); - copy(savedParams, params); - } - } - - private ModelAndView redirectToForceSignin(HttpServletRequest request) { - String q = request.getQueryString(); - q = removeQueryStringParams(q,"prompt","max_age"); - q += "&prompt=login"; - RedirectView rView = new RedirectView(orcidUrlManager.getBaseUrl() + "/signin?oauth&" +q); - ModelAndView m = new ModelAndView(); - m.setView(rView); - return m; - } - -} diff --git a/orcid-web/src/main/java/org/orcid/frontend/web/controllers/OauthControllerBase.java b/orcid-web/src/main/java/org/orcid/frontend/web/controllers/OauthControllerBase.java deleted file mode 100644 index e883d8f5a8c..00000000000 --- a/orcid-web/src/main/java/org/orcid/frontend/web/controllers/OauthControllerBase.java +++ /dev/null @@ -1,168 +0,0 @@ -package org.orcid.frontend.web.controllers; - -import java.util.Map; - -import javax.annotation.Resource; -import javax.servlet.http.HttpServletRequest; - -import org.orcid.core.constants.OrcidOauth2Constants; -import org.orcid.core.manager.ClientDetailsEntityCacheManager; -import org.orcid.core.manager.ProfileEntityCacheManager; -import org.orcid.core.manager.v3.read_only.RecordNameManagerReadOnly; -import org.orcid.core.oauth.service.OrcidAuthorizationEndpoint; -import org.orcid.core.oauth.service.OrcidOAuth2RequestValidator; -import org.orcid.authorization.authentication.MFAWebAuthenticationDetails; -import org.orcid.persistence.jpa.entities.ClientDetailsEntity; -import org.orcid.pojo.ajaxForm.PojoUtil; -import org.orcid.pojo.ajaxForm.RequestInfoForm; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.oauth2.common.util.OAuth2Utils; - -@Deprecated -public class OauthControllerBase extends BaseController { - protected static String REDIRECT_URI_ERROR = "/oauth/error/redirect-uri-mismatch?client_id={0}"; - - @Resource - protected ClientDetailsEntityCacheManager clientDetailsEntityCacheManager; - - @Resource - protected OrcidOAuth2RequestValidator orcidOAuth2RequestValidator; - - @Resource - protected ProfileEntityCacheManager profileEntityCacheManager; - - @Resource - protected AuthenticationManager authenticationManager; - - @Resource - protected OrcidAuthorizationEndpoint authorizationEndpoint; - - @Resource(name = "recordNameManagerReadOnlyV3") - private RecordNameManagerReadOnly recordNameManagerReadOnly; - - public AuthenticationManager getAuthenticationManager() { - return authenticationManager; - } - - public void setAuthenticationManager(AuthenticationManager authenticationManager) { - this.authenticationManager = authenticationManager; - } - - public OrcidAuthorizationEndpoint getAuthorizationEndpoint() { - return authorizationEndpoint; - } - - public void setAuthorizationEndpoint(OrcidAuthorizationEndpoint authorizationEndpoint) { - this.authorizationEndpoint = authorizationEndpoint; - } - - protected void fillOauthParams(RequestInfoForm requestInfoForm, Map params, Map approvalParams, boolean userEnabledPersistentTokens, boolean allowEmailAccess) { - if (requestInfoForm.containsEmailReadPrivateScope() && !allowEmailAccess) { - requestInfoForm.removeEmailReadPrivateScope(); - } - - if (!PojoUtil.isEmpty(requestInfoForm.getScopesAsString())) { - params.put(OrcidOauth2Constants.SCOPE_PARAM, requestInfoForm.getScopesAsString()); - } - - params.put(OrcidOauth2Constants.TOKEN_VERSION, OrcidOauth2Constants.PERSISTENT_TOKEN); - params.put(OrcidOauth2Constants.CLIENT_ID_PARAM, requestInfoForm.getClientId()); - - // Redirect URI - if (!PojoUtil.isEmpty(requestInfoForm.getRedirectUrl())) { - params.put(OrcidOauth2Constants.REDIRECT_URI_PARAM, requestInfoForm.getRedirectUrl()); - } else { - params.put(OrcidOauth2Constants.REDIRECT_URI_PARAM, new String()); - } - - // Response type - if (!PojoUtil.isEmpty(requestInfoForm.getResponseType())) { - params.put(OrcidOauth2Constants.RESPONSE_TYPE_PARAM, requestInfoForm.getResponseType()); - } - // State param - if (!PojoUtil.isEmpty(requestInfoForm.getStateParam())) { - params.put(OrcidOauth2Constants.STATE_PARAM, requestInfoForm.getStateParam()); - } - - // Set approval params - params.put(OAuth2Utils.USER_OAUTH_APPROVAL, "true"); - approvalParams.put(OAuth2Utils.USER_OAUTH_APPROVAL, "true"); - - // Set persistent token flag - if(requestInfoForm.getClientHavePersistentTokens() && userEnabledPersistentTokens) { - params.put(OrcidOauth2Constants.GRANT_PERSISTENT_TOKEN, "true"); - } else { - params.put(OrcidOauth2Constants.GRANT_PERSISTENT_TOKEN, "false"); - } - - //OpenID connect - if (!PojoUtil.isEmpty(requestInfoForm.getNonce())){ - params.put(OrcidOauth2Constants.NONCE, requestInfoForm.getNonce()); - } - } - - /** - * Builds the redirect uri string to use when the user deny the request - * - * @param redirectUri - * Redirect uri - * @return the redirect uri string with the deny params - */ - protected String buildDenyRedirectUri(String redirectUri, String stateParam) { - if (!PojoUtil.isEmpty(redirectUri)) { - if (redirectUri.contains("?")) { - redirectUri = redirectUri.concat("&error=access_denied&error_description=User denied access"); - } else { - redirectUri = redirectUri.concat("?error=access_denied&error_description=User denied access"); - } - } - if (!PojoUtil.isEmpty(stateParam)) - redirectUri += "&state=" + stateParam; - return redirectUri; - } - - protected void copy(Map savedParams, Map params) { - if (savedParams != null && !savedParams.isEmpty()) { - for (String key : savedParams.keySet()) { - String[] values = savedParams.get(key); - if (values != null && values.length > 0) - params.put(key, values[0]); - } - } - } - - /***************************** - * Authenticate user methods - ****************************/ - protected Authentication authenticateUser(HttpServletRequest request, String email, String password) { - UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(email, password); - token.setDetails(new MFAWebAuthenticationDetails(request)); - Authentication authentication = authenticationManager.authenticate(token); - SecurityContextHolder.getContext().setAuthentication(authentication); - return authentication; - } - - /** - * Checks if the client has the persistent tokens enabled - * - * @return true if the persistent tokens are enabled for that client - * @throws IllegalArgumentException - */ - protected boolean hasPersistenTokensEnabled(String clientId) throws IllegalArgumentException { - ClientDetailsEntity clientDetails = clientDetailsEntityCacheManager.retrieve(clientId); - if (clientDetails == null) - throw new IllegalArgumentException(getMessage("web.orcid.oauth_invalid_client.exception")); - return clientDetails.isPersistentTokensEnabled(); - } - - protected String removeQueryStringParams(String queryString, String... params) { - for (String param : params) { - String keyValue = param + "=[^&]*?"; - queryString = queryString.replaceAll("(&" + keyValue + "(?=(&|$))|^" + keyValue + "(&|$))", ""); - } - return queryString; - } -} diff --git a/orcid-web/src/main/java/org/orcid/frontend/web/controllers/OauthGenericCallsController.java b/orcid-web/src/main/java/org/orcid/frontend/web/controllers/OauthGenericCallsController.java index 44a8a3bcc91..79f0b20c5cc 100644 --- a/orcid-web/src/main/java/org/orcid/frontend/web/controllers/OauthGenericCallsController.java +++ b/orcid-web/src/main/java/org/orcid/frontend/web/controllers/OauthGenericCallsController.java @@ -1,64 +1,40 @@ package org.orcid.frontend.web.controllers; import java.io.IOException; -import java.io.UnsupportedEncodingException; import java.net.URISyntaxException; -import java.util.Enumeration; import java.util.Map; import java.util.stream.Collectors; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.MultivaluedHashMap; -import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.orcid.core.oauth.authorizationServer.AuthorizationServerUtil; -import org.orcid.api.common.oauth.OrcidClientCredentialEndPointDelegator; import org.orcid.core.constants.OrcidOauth2Constants; import org.orcid.core.oauth.OAuthError; import org.orcid.core.oauth.OAuthErrorUtils; import org.orcid.core.togglz.Features; -import org.orcid.frontend.util.RequestInfoFormLocalCache; -import org.orcid.pojo.ajaxForm.OauthAuthorizeForm; -import org.orcid.pojo.ajaxForm.OauthRegistrationForm; -import org.orcid.pojo.ajaxForm.PojoUtil; -import org.orcid.pojo.ajaxForm.RequestInfoForm; -import org.orcid.pojo.ajaxForm.Text; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.ResponseBody; import static org.orcid.core.constants.OrcidOauth2Constants.IETF_EXCHANGE_GRANT_TYPE; @Controller("oauthGenericCallsController") -public class OauthGenericCallsController extends OauthControllerBase { +public class OauthGenericCallsController { private static final Logger logger = Logger.getLogger(OauthGenericCallsController.class); - @Resource - private RegistrationController registrationController; - - @Resource - private OrcidClientCredentialEndPointDelegator orcidClientCredentialEndPointDelegator; - @Context private UriInfo uriInfo; - @Resource - private RequestInfoFormLocalCache requestInfoFormLocalCache; - @Resource private AuthorizationServerUtil authorizationServerUtil; @@ -72,50 +48,32 @@ public ResponseEntity obtainOauth2TokenPost(HttpServletRequest request) throw error.setResponseStatus(Response.Status.BAD_REQUEST); return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error); } - if(Features.OAUTH_AUTHORIZATION_CODE_EXCHANGE.isActive()) { - try { - Response response = null; - if(StringUtils.isNotBlank(request.getHeader("Authorization"))) { - response = handleBasicAuthentication(grantType, request); - } else { - response = handlePlainClientCredentials(grantType, request); - } - HttpHeaders responseHeaders = new HttpHeaders(); - responseHeaders.set(Features.OAUTH_AUTHORIZATION_CODE_EXCHANGE.name(), - "ON"); - return ResponseEntity.status(response.getStatus()).headers(responseHeaders).body(response.getEntity()); - } catch(Exception e) { - OAuthError error = OAuthErrorUtils.getOAuthError(e); - Map params = request.getParameterMap(); - if(params != null && !params.isEmpty()) { - String paramList = params.entrySet().stream() - .map(entry -> { - String paramValues = (entry.getValue() == null || entry.getValue().length == 0) ? "-NOTHING-" : String.join(",", entry.getValue()); - return entry.getKey() + "=" + paramValues; - }) - .collect(Collectors.joining(", ")); - logger.error("Exception sending request to authorization server: " + error.getErrorDescription() + " - Param list: " + paramList, e); - } - return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(error); - } - } else { - String authorization = request.getHeader("Authorization"); - Enumeration paramNames = request.getParameterNames(); - MultivaluedMap formParams = new MultivaluedHashMap(); - while(paramNames.hasMoreElements()) { - String paramName = paramNames.nextElement(); - formParams.add(paramName, request.getParameter(paramName)); + try { + Response response = null; + if(StringUtils.isNotBlank(request.getHeader("Authorization"))) { + response = handleBasicAuthentication(grantType, request); + } else { + response = handlePlainClientCredentials(grantType, request); } - try { - Response response = orcidClientCredentialEndPointDelegator.obtainOauth2Token(authorization, formParams); - return ResponseEntity.status(response.getStatus()).body(response.getEntity()); - } catch(Exception e) { - OAuthError error = OAuthErrorUtils.getOAuthError(e); - HttpStatus status = HttpStatus.valueOf(error.getResponseStatus().getStatusCode()); - return ResponseEntity.status(status).body(error); + HttpHeaders responseHeaders = new HttpHeaders(); + responseHeaders.set(Features.OAUTH_AUTHORIZATION_CODE_EXCHANGE.name(), + "ON"); + return ResponseEntity.status(response.getStatus()).headers(responseHeaders).body(response.getEntity()); + } catch(Exception e) { + OAuthError error = OAuthErrorUtils.getOAuthError(e); + Map params = request.getParameterMap(); + if(params != null && !params.isEmpty()) { + String paramList = params.entrySet().stream() + .map(entry -> { + String paramValues = (entry.getValue() == null || entry.getValue().length == 0) ? "-NOTHING-" : String.join(",", entry.getValue()); + return entry.getKey() + "=" + paramValues; + }) + .collect(Collectors.joining(", ")); + logger.error("Exception sending request to authorization server: " + error.getErrorDescription() + " - Param list: " + paramList, e); } + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(error); } } @@ -183,90 +141,4 @@ private Response handleBasicAuthentication(String grantType, HttpServletRequest return response; } - - @RequestMapping(value = "/oauth/custom/authorize/get_request_info_form.json", method = RequestMethod.GET) - public @ResponseBody RequestInfoForm getRequestInfoForm(HttpServletRequest request) throws UnsupportedEncodingException { - RequestInfoForm requestInfoForm = new RequestInfoForm(); - if(requestInfoFormLocalCache.containsKey(request.getSession().getId())) { - requestInfoForm = requestInfoFormLocalCache.get(request.getSession().getId()); - } - return requestInfoForm; - } - - @RequestMapping(value = "/oauth/custom/authorize/empty.json", method = RequestMethod.GET) - public @ResponseBody OauthAuthorizeForm getEmptyAuthorizeForm(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException { - OauthAuthorizeForm empty = new OauthAuthorizeForm(); - Text emptyText = Text.valueOf(StringUtils.EMPTY); - empty.setPassword(emptyText); - empty.setUserName(emptyText); - - RequestInfoForm requestInfoForm = getRequestInfoForm(request); - if(requestInfoForm != null) { - if(!PojoUtil.isEmpty(requestInfoForm.getUserId())) { - empty.setUserName(Text.valueOf(requestInfoForm.getUserId())); - } - } - - return empty; - } - - /***************************** - * Validators - ****************************/ - @RequestMapping(value = "/oauth/custom/register/validateActivitiesVisibilityDefault.json", method = RequestMethod.POST) - public @ResponseBody OauthRegistrationForm validateActivitiesVisibilityDefaul(@RequestBody OauthRegistrationForm reg) { - registrationController.registerActivitiesVisibilityDefaultValidate(reg); - return reg; - } - - @RequestMapping(value = "/oauth/custom/register/validatePasswordConfirm.json", method = RequestMethod.POST) - public @ResponseBody OauthRegistrationForm validatePasswordConfirm(@RequestBody OauthRegistrationForm reg) { - registrationController.registerPasswordConfirmValidate(reg); - return reg; - } - - @RequestMapping(value = "/oauth/custom/register/validatePassword.json", method = RequestMethod.POST) - public @ResponseBody OauthRegistrationForm validatePassword(@RequestBody OauthRegistrationForm reg) { - registrationController.registerPasswordValidate(reg); - return reg; - } - - @RequestMapping(value = "/oauth/custom/register/validateTermsOfUse.json", method = RequestMethod.POST) - public @ResponseBody OauthRegistrationForm validateTermsOfUse(@RequestBody OauthRegistrationForm reg) { - registrationController.registerTermsOfUseValidate(reg); - return reg; - } - - @RequestMapping(value = "/oauth/custom/register/validateGivenNames.json", method = RequestMethod.POST) - public @ResponseBody OauthRegistrationForm validateGivenName(@RequestBody OauthRegistrationForm reg) { - - registrationController.registerGivenNameValidate(reg); - return reg; - } - - - @RequestMapping(value = "/oauth/custom/register/validateFamilyNames.json", method = RequestMethod.POST) - public @ResponseBody OauthRegistrationForm validateFamilyName(@RequestBody OauthRegistrationForm reg) { - - registrationController.registerFamilyNameValidate(reg); - return reg; - } - - @RequestMapping(value = "/oauth/custom/register/validateEmail.json", method = RequestMethod.POST) - public @ResponseBody OauthRegistrationForm validateEmail(HttpServletRequest request, @RequestBody OauthRegistrationForm reg) { - registrationController.regEmailValidate(request, reg, true, false); - return reg; - } - - @RequestMapping(value = "/oauth/custom/register/validateEmailConfirm.json", method = RequestMethod.POST) - public @ResponseBody OauthRegistrationForm validateEmailConfirm(@RequestBody OauthRegistrationForm reg) { - registrationController.regEmailConfirmValidate(reg); - return reg; - } - - @RequestMapping(value = "/oauth/custom/register/validateEmailsAdditional.json", method = RequestMethod.POST) - public @ResponseBody OauthRegistrationForm validateEmailsAdditional(HttpServletRequest request, @RequestBody OauthRegistrationForm reg) { - additionalEmailsValidateOnRegister(request, reg); - return reg; - } } diff --git a/orcid-web/src/main/java/org/orcid/frontend/web/controllers/OauthLoginController.java b/orcid-web/src/main/java/org/orcid/frontend/web/controllers/OauthLoginController.java deleted file mode 100644 index 91b838aa8b7..00000000000 --- a/orcid-web/src/main/java/org/orcid/frontend/web/controllers/OauthLoginController.java +++ /dev/null @@ -1,216 +0,0 @@ -package org.orcid.frontend.web.controllers; - -import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; - -import javax.annotation.Resource; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.orcid.core.constants.OrcidOauth2Constants; -import org.orcid.core.exception.ClientDeactivatedException; -import org.orcid.core.exception.LockedException; -import org.orcid.core.manager.v3.ProfileEntityManager; -import org.orcid.core.security.UnclaimedProfileExistsException; -import org.orcid.core.utils.OrcidRequestUtil; -import org.orcid.authorization.authentication.MFAWebAuthenticationDetails; -import org.orcid.frontend.util.RequestInfoFormLocalCache; -import org.orcid.frontend.web.controllers.helper.OauthHelper; -import org.orcid.frontend.web.exception.Bad2FARecoveryCodeException; -import org.orcid.frontend.web.exception.Bad2FAVerificationCodeException; -import org.orcid.frontend.web.exception.VerificationCodeFor2FARequiredException; -import org.orcid.jaxb.model.message.ScopePathType; -import org.orcid.persistence.jpa.entities.ClientDetailsEntity; -import org.orcid.pojo.ajaxForm.OauthAuthorizeForm; -import org.orcid.pojo.ajaxForm.PojoUtil; -import org.orcid.pojo.ajaxForm.RequestInfoForm; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.security.authentication.DisabledException; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.oauth2.common.exceptions.InvalidScopeException; -import org.springframework.security.oauth2.common.exceptions.RedirectMismatchException; -import org.springframework.security.web.savedrequest.HttpSessionRequestCache; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.bind.support.SimpleSessionStatus; -import org.springframework.web.servlet.ModelAndView; -import org.springframework.web.servlet.view.RedirectView; - -@Deprecated -@Controller("oauthLoginController") -public class OauthLoginController extends OauthControllerBase { - private static final Logger LOGGER = LoggerFactory.getLogger(OauthLoginController.class); - - @Resource(name = "profileEntityManagerV3") - private ProfileEntityManager profileEntityManager; - - @Resource - private OauthHelper oauthHelper; - - @Resource - private RequestInfoFormLocalCache requestInfoFormLocalCache; - - @RequestMapping(value = { "/oauth/signin", "/oauth/login" }, method = RequestMethod.GET) - public ModelAndView loginGetHandler(HttpServletRequest request, HttpServletResponse response, ModelAndView mav) throws UnsupportedEncodingException { - String url = request.getQueryString(); - // Get and save the request information form - RequestInfoForm requestInfoForm = oauthHelper.generateRequestInfoForm(url); - // Store the request info form in the cache - requestInfoFormLocalCache.put(request.getSession().getId(), requestInfoForm); - - // Check that the client have the required permissions - // Get client name - ClientDetailsEntity clientDetails = clientDetailsEntityCacheManager.retrieve(requestInfoForm.getClientId()); - - // validate client scopes - try { - authorizationEndpoint.validateScope(requestInfoForm.getScopesAsString(), clientDetails,requestInfoForm.getResponseType()); - orcidOAuth2RequestValidator.validateClientIsEnabled(clientDetails); - } catch (InvalidScopeException | ClientDeactivatedException | LockedException e) { - String redirectUriWithParams = requestInfoForm.getRedirectUrl(); - if (e instanceof InvalidScopeException) { - redirectUriWithParams += "?error=invalid_scope&error_description=" + e.getMessage(); - } else if (e instanceof LockedException) { - redirectUriWithParams += "?error=client_locked&error_description=" + e.getMessage(); - } else { - redirectUriWithParams += "?error=client_deactivated&error_description=" + e.getMessage(); - } - RedirectView rView = new RedirectView(redirectUriWithParams); - ModelAndView error = new ModelAndView(); - error.setView(rView); - return error; - } - - //handle openID behaviour - if (!PojoUtil.isEmpty(requestInfoForm.getScopesAsString()) && ScopePathType.getScopesFromSpaceSeparatedString(requestInfoForm.getScopesAsString()).contains(ScopePathType.OPENID) ){ - String prompt = request.getParameter(OrcidOauth2Constants.PROMPT); - if (prompt != null && prompt.equals(OrcidOauth2Constants.PROMPT_NONE)){ - String redirectUriWithParams = requestInfoForm.getRedirectUrl(); - redirectUriWithParams += "?error=login_required"; - RedirectView rView = new RedirectView(redirectUriWithParams); - ModelAndView error = new ModelAndView(); - error.setView(rView); - return error; - } - } - mav.addObject("noIndex", true); - mav.addObject("hideSupportWidget", true); - mav.setViewName("oauth_login"); - return mav; - } - - @RequestMapping(value = { "/oauth/custom/signin.json", "/oauth/custom/login.json" }, method = RequestMethod.POST) - public @ResponseBody OauthAuthorizeForm authenticateAndAuthorize(HttpServletRequest request, HttpServletResponse response, @RequestBody OauthAuthorizeForm form) { - // Clean form errors - form.setErrors(new ArrayList()); - RequestInfoForm requestInfoForm = requestInfoFormLocalCache.get(request.getSession().getId()); - - boolean willBeRedirected = false; - if (form.getApproved()) { - // Validate name and password - validateUserNameAndPassword(form); - if (form.getErrors().isEmpty()) { - try { - // Authenticate user - copy2FAFields(form, request); - Authentication auth = authenticateUser(request, form.getUserName().getValue(), form.getPassword().getValue()); - profileEntityManager.updateLastLoginDetails(auth.getName(), OrcidRequestUtil.getIpAddress(request)); - - // Create authorization params - SimpleSessionStatus status = new SimpleSessionStatus(); - Map model = new HashMap(); - Map params = new HashMap(); - Map approvalParams = new HashMap(); - - fillOauthParams(requestInfoForm, params, approvalParams, form.getPersistentTokenEnabled(), form.isEmailAccessAllowed()); - - // Authorize - try { - authorizationEndpoint.authorize(model, params, status, auth); - } catch (RedirectMismatchException rUriError) { - String redirectUri = this.getBaseUri() + REDIRECT_URI_ERROR; - // Set the client id - redirectUri = redirectUri.replace("{0}", requestInfoForm.getClientId()); - // Set the response type if needed - if (!PojoUtil.isEmpty(requestInfoForm.getResponseType())) - redirectUri += "&response_type=" + requestInfoForm.getResponseType(); - // Set the redirect uri - if (!PojoUtil.isEmpty(requestInfoForm.getRedirectUrl())) - redirectUri += "&redirect_uri=" + requestInfoForm.getRedirectUrl(); - // Set the scope param - if (!PojoUtil.isEmpty(requestInfoForm.getScopesAsString())) - redirectUri += "&scope=" + requestInfoForm.getScopesAsString(); - // Copy the state param if present - if (!PojoUtil.isEmpty(requestInfoForm.getStateParam())) - redirectUri += "&state=" + requestInfoForm.getStateParam(); - form.setRedirectUrl(redirectUri); - LOGGER.info("OauthLoginController being sent to client browser: " + form.getRedirectUrl()); - return form; - } - // Approve - RedirectView view = (RedirectView) authorizationEndpoint.approveOrDeny(approvalParams, model, status, auth); - form.setRedirectUrl(view.getUrl()); - willBeRedirected = true; - - - } catch (AuthenticationException ae) { - if(ae.getCause() instanceof DisabledException){ - // Handle this message in angular to allow AJAX action - form.getErrors().add("orcid.frontend.security.orcid_deactivated"); - } else if(ae.getCause() instanceof UnclaimedProfileExistsException) { - String email = PojoUtil.isEmpty(form.getUserName()) ? null : form.getUserName().getValue(); - String resendEmailUrl = createResendClaimUrl(email, request); - String errorMessage = getMessage("orcid.frontend.security.unclaimed_exists_1"); - errorMessage += ""; - errorMessage += getMessage("orcid.frontend.security.unclaimed_exists_2"); - errorMessage += "" + getMessage("orcid.frontend.security.unclaimed_exists_3"); - form.getErrors().add(errorMessage); - } else if (ae instanceof VerificationCodeFor2FARequiredException) { - form.setVerificationCodeRequired(true); - } else if (ae instanceof Bad2FAVerificationCodeException) { - form.getErrors().add(getMessage("orcid.frontend.security.2fa.bad_verification_code")); - } else if (ae instanceof Bad2FARecoveryCodeException) { - form.getErrors().add(getMessage("orcid.frontend.security.2fa.bad_recovery_code")); - } else { - form.getErrors().add(getMessage("orcid.frontend.security.bad_credentials")); - } - } - } - } else { - form.setRedirectUrl(buildDenyRedirectUri(requestInfoForm.getRedirectUrl(), requestInfoForm.getStateParam())); - willBeRedirected = true; - } - - // If there was an authentication error, dont log since the user will - // not be redirected yet - if (willBeRedirected) { - if (new HttpSessionRequestCache().getRequest(request, response) != null) - new HttpSessionRequestCache().removeRequest(request, response); - LOGGER.info("OauthConfirmAccessController form.getRedirectUri being sent to client browser: " + requestInfoForm.getRedirectUrl()); - } - return form; - } - - private void copy2FAFields(OauthAuthorizeForm form, HttpServletRequest request) { - if (form.getVerificationCode() != null) { - request.setAttribute(MFAWebAuthenticationDetails.VERIFICATION_CODE_PARAMETER, form.getVerificationCode().getValue()); - } - - if (form.getRecoveryCode() != null) { - request.setAttribute(MFAWebAuthenticationDetails.RECOVERY_CODE_PARAMETER, form.getRecoveryCode().getValue()); - } - } - - private void validateUserNameAndPassword(OauthAuthorizeForm form) { - if (PojoUtil.isEmpty(form.getUserName()) || PojoUtil.isEmpty(form.getPassword())) { - form.getErrors().add(getMessage("orcid.frontend.security.bad_credentials")); - } - } -} diff --git a/orcid-web/src/main/java/org/orcid/frontend/web/controllers/OauthRegistrationController.java b/orcid-web/src/main/java/org/orcid/frontend/web/controllers/OauthRegistrationController.java deleted file mode 100644 index 45d177c8fd0..00000000000 --- a/orcid-web/src/main/java/org/orcid/frontend/web/controllers/OauthRegistrationController.java +++ /dev/null @@ -1,224 +0,0 @@ -package org.orcid.frontend.web.controllers; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; - -import javax.annotation.Resource; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.lang.StringUtils; -import org.orcid.core.constants.OrcidOauth2Constants; -import org.orcid.core.utils.OrcidRequestUtil; -import org.orcid.frontend.util.RequestInfoFormLocalCache; -import org.orcid.frontend.web.controllers.helper.OauthHelper; -import org.orcid.jaxb.model.message.CreationMethod; -import org.orcid.pojo.ajaxForm.OauthRegistrationForm; -import org.orcid.pojo.ajaxForm.PojoUtil; -import org.orcid.pojo.ajaxForm.RequestInfoForm; -import org.orcid.pojo.ajaxForm.Text; -import org.orcid.utils.OrcidStringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.security.core.Authentication; -import org.springframework.security.oauth2.common.exceptions.RedirectMismatchException; -import org.springframework.security.web.savedrequest.HttpSessionRequestCache; -import org.springframework.security.web.savedrequest.SavedRequest; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.bind.support.SimpleSessionStatus; -import org.springframework.web.servlet.support.RequestContextUtils; -import org.springframework.web.servlet.view.RedirectView; - -@Controller("oauthRegisterController") -public class OauthRegistrationController extends OauthControllerBase { - private static final Logger LOGGER = LoggerFactory.getLogger(OauthRegistrationController.class); - @Resource - private RegistrationController registrationController; - - @Resource - private RequestInfoFormLocalCache requestInfoFormLocalCache; - - public RegistrationController getRegistrationController() { - return registrationController; - } - - public void setRequestInfoFormLocalCache(RequestInfoFormLocalCache requestInfoFormLocalCache) { - this.requestInfoFormLocalCache = requestInfoFormLocalCache; - } - - public void setRegistrationController(RegistrationController registrationController) { - this.registrationController = registrationController; - } - - @RequestMapping(value = "/oauth/custom/register/empty.json", method = RequestMethod.GET) - public @ResponseBody OauthRegistrationForm getRegister(HttpServletRequest request, HttpServletResponse response) { - // Remove the session hash if needed - if (request.getSession().getAttribute(RegistrationController.GRECAPTCHA_SESSION_ATTRIBUTE_NAME) != null) { - request.getSession().removeAttribute(RegistrationController.GRECAPTCHA_SESSION_ATTRIBUTE_NAME); - } - OauthRegistrationForm empty = new OauthRegistrationForm(registrationController.getRegister(request, response)); - // Creation type in oauth will always be member referred - empty.setCreationType(Text.valueOf(CreationMethod.MEMBER_REFERRED.value())); - Text emptyText = Text.valueOf(StringUtils.EMPTY); - empty.setPassword(emptyText); - return empty; - } - - @RequestMapping(value = "/oauth/custom/register.json", method = RequestMethod.POST) - public @ResponseBody OauthRegistrationForm checkRegisterForm(HttpServletRequest request, HttpServletResponse response, @RequestBody OauthRegistrationForm form) { - form.setErrors(new ArrayList()); - RequestInfoForm requestInfoForm = requestInfoFormLocalCache.get(request.getSession().getId()); - - if (form.getApproved()) { - registrationController.validateRegistrationFields(request, form); - registrationController.validateGrcaptcha(request, form); - } else { - SavedRequest savedRequest = new HttpSessionRequestCache().getRequest(request, response); - String stateParam = null; - if (savedRequest != null && savedRequest.getParameterMap() != null && savedRequest.getParameterValues("state") != null) { - if (savedRequest.getParameterValues("state").length > 0) - stateParam = savedRequest.getParameterValues("state")[0]; - } - form.setRedirectUrl(buildDenyRedirectUri(requestInfoForm.getRedirectUrl(), stateParam)); - } - - return form; - } - - @RequestMapping(value = "/oauth/custom/registerConfirm.json", method = RequestMethod.POST) - public @ResponseBody RequestInfoForm registerAndAuthorize(HttpServletRequest request, HttpServletResponse response, @RequestBody OauthRegistrationForm form) { - RequestInfoForm requestInfoForm = requestInfoFormLocalCache.get(request.getSession().getId()); - if (form.getApproved()) { - boolean usedCaptcha = false; - - // If recatcha wasn't loaded do nothing. This is for countries that - // block google. - if (form.getGrecaptchaWidgetId().getValue() != null) { - // If the captcha verified key is not in the session, redirect - // to the login page - if (request.getSession().getAttribute(RegistrationController.GRECAPTCHA_SESSION_ATTRIBUTE_NAME) == null - || PojoUtil.isEmpty(form.getGrecaptcha()) - || !form.getGrecaptcha().getValue().equals( - request.getSession().getAttribute(RegistrationController.GRECAPTCHA_SESSION_ATTRIBUTE_NAME))) { - String redirectUri = this.getBaseUri() + REDIRECT_URI_ERROR; - // Set the client id - redirectUri = redirectUri.replace("{0}", requestInfoForm.getClientId()); - // Set the response type if needed - if (!PojoUtil.isEmpty(requestInfoForm.getResponseType())) - redirectUri += "&response_type=" + requestInfoForm.getResponseType(); - // Set the redirect uri - if (!PojoUtil.isEmpty(requestInfoForm.getRedirectUrl())) - redirectUri += "&redirect_uri=" + requestInfoForm.getRedirectUrl(); - // remove email access scope if present but not granted - if (requestInfoForm.containsEmailReadPrivateScope() && !form.isEmailAccessAllowed()) { - requestInfoForm.removeEmailReadPrivateScope(); - } - // Set the scope param - if (!PojoUtil.isEmpty(requestInfoForm.getScopesAsString())) - redirectUri += "&scope=" + requestInfoForm.getScopesAsString(); - // Copy the state param if present - if (!PojoUtil.isEmpty(requestInfoForm.getStateParam())) - redirectUri += "&state=" + requestInfoForm.getStateParam(); - requestInfoForm.setRedirectUrl(redirectUri); - SavedRequest savedRequest = new HttpSessionRequestCache().getRequest(request, response); - if (savedRequest != null) - LOGGER.info("OauthConfirmAccessController original request: " + savedRequest.getRedirectUrl()); - LOGGER.info("OauthConfirmAccessController form.getRedirectUri being sent to client browser: " + requestInfoForm.getRedirectUrl()); - return requestInfoForm; - } - usedCaptcha = true; - } - - // Remove the session hash if needed - if (request.getSession().getAttribute(RegistrationController.GRECAPTCHA_SESSION_ATTRIBUTE_NAME) != null) { - request.getSession().removeAttribute(RegistrationController.GRECAPTCHA_SESSION_ATTRIBUTE_NAME); - } - - //Strip any html code from names before validating them - if(!PojoUtil.isEmpty(form.getFamilyNames())){ - form.getFamilyNames().setValue(OrcidStringUtils.stripHtml(form.getFamilyNames().getValue())); - } - - if(!PojoUtil.isEmpty(form.getGivenNames())) { - form.getGivenNames().setValue(OrcidStringUtils.stripHtml(form.getGivenNames().getValue())); - } - - // Check there are no errors - registrationController.validateRegistrationFields(request, form); - if (form.getErrors().isEmpty()) { - // Register user - try { - // Locale - Locale locale = RequestContextUtils.getLocale(request); - // Ip - String ip = OrcidRequestUtil.getIpAddress(request); - registrationController.createMinimalRegistration(request, form, usedCaptcha, locale, ip); - } catch(Exception e) { - LOGGER.error("Error registering a new user", e); - requestInfoForm.getErrors().add(getMessage("register.error.generalError")); - return requestInfoForm; - } - // Authenticate user - String email = form.getEmail().getValue(); - String password = form.getPassword().getValue(); - Authentication auth = authenticateUser(request, email, password); - // Create authorization params - SimpleSessionStatus status = new SimpleSessionStatus(); - Map model = new HashMap(); - Map params = new HashMap(); - Map approvalParams = new HashMap(); - - fillOauthParams(requestInfoForm, params, approvalParams, form.getPersistentTokenEnabled(), form.isEmailAccessAllowed()); - - // Authorize - try { - authorizationEndpoint.authorize(model, params, status, auth); - } catch (RedirectMismatchException rUriError) { - String redirectUri = this.getBaseUri() + REDIRECT_URI_ERROR; - // Set the client id - redirectUri = redirectUri.replace("{0}", requestInfoForm.getClientId()); - // Set the response type if needed - if (!PojoUtil.isEmpty(requestInfoForm.getResponseType())) - redirectUri += "&response_type=" + requestInfoForm.getResponseType(); - // Set the redirect uri - if (!PojoUtil.isEmpty(requestInfoForm.getRedirectUrl())) - redirectUri += "&redirect_uri=" + requestInfoForm.getRedirectUrl(); - // Set the scope param - if (!PojoUtil.isEmpty(requestInfoForm.getScopesAsString())) - redirectUri += "&scope=" + requestInfoForm.getScopesAsString(); - // Copy the state param if present - if (!PojoUtil.isEmpty(requestInfoForm.getStateParam())) - redirectUri += "&state=" + requestInfoForm.getStateParam(); - requestInfoForm.setRedirectUrl(redirectUri); - LOGGER.info("OauthRegisterController being sent to client browser: " + requestInfoForm.getRedirectUrl()); - return requestInfoForm; - } - - Boolean isOauth2ScreensRequest = (Boolean) request.getSession().getAttribute(OrcidOauth2Constants.OAUTH_2SCREENS); - if(isOauth2ScreensRequest != null && isOauth2ScreensRequest) { - // Just redirect to the authorization screen - String queryString = (String) request.getSession().getAttribute(OrcidOauth2Constants.OAUTH_QUERY_STRING); - requestInfoForm.setRedirectUrl(orcidUrlManager.getBaseUrl() + "/oauth/authorize?" + queryString); - request.getSession().removeAttribute(OrcidOauth2Constants.OAUTH_2SCREENS); - } else { - // Approve - RedirectView view = (RedirectView) authorizationEndpoint.approveOrDeny(approvalParams, model, status, auth); - requestInfoForm.setRedirectUrl(view.getUrl()); - } - } - } else { - requestInfoForm.setRedirectUrl(buildDenyRedirectUri(requestInfoForm.getRedirectUrl(), requestInfoForm.getStateParam())); - } - - if(new HttpSessionRequestCache().getRequest(request, response) != null) - new HttpSessionRequestCache().removeRequest(request, response); - LOGGER.info("OauthConfirmAccessController form.getRedirectUri being sent to client browser: " + requestInfoForm.getRedirectUrl()); - return requestInfoForm; - } -} diff --git a/orcid-web/src/main/java/org/orcid/frontend/web/controllers/OpenIDController.java b/orcid-web/src/main/java/org/orcid/frontend/web/controllers/OpenIDController.java index f1a45e11595..4a2f930831e 100644 --- a/orcid-web/src/main/java/org/orcid/frontend/web/controllers/OpenIDController.java +++ b/orcid-web/src/main/java/org/orcid/frontend/web/controllers/OpenIDController.java @@ -1,15 +1,23 @@ package org.orcid.frontend.web.controllers; import java.io.IOException; +import java.net.URISyntaxException; +import java.util.Arrays; import java.util.Set; import java.util.regex.Pattern; +import java.util.stream.Collectors; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.codehaus.jettison.json.JSONException; +import org.orcid.core.manager.v3.read_only.PersonDetailsManagerReadOnly; import org.orcid.core.oauth.authorizationServer.AuthorizationServerUtil; import org.orcid.core.oauth.openid.OpenIDConnectUserInfo; import org.orcid.jaxb.model.message.ScopePathType; +import org.orcid.jaxb.model.v3.release.record.Person; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -19,8 +27,7 @@ import org.springframework.web.bind.annotation.ResponseBody; import com.fasterxml.jackson.core.JsonProcessingException; - -import net.minidev.json.JSONObject; +import org.codehaus.jettison.json.JSONObject; @Controller public class OpenIDController { @@ -54,7 +61,7 @@ public class OpenIDController { * @throws IOException */ @RequestMapping(value = "/oauth/userinfo", method = RequestMethod.GET, produces = "application/json") - public @ResponseBody ResponseEntity getUserInfo(HttpServletRequest request) throws IOException{ + public @ResponseBody ResponseEntity getUserInfo(HttpServletRequest request) throws IOException, JSONException, URISyntaxException, InterruptedException { if (request.getHeader("Authorization") != null) {//look in header String tokenValue = request.getHeader("Authorization").replaceAll("Bearer|bearer", "").trim(); OpenIDConnectUserInfo info = getInfoFromToken(tokenValue); @@ -65,7 +72,7 @@ public class OpenIDController { } @RequestMapping(value = "/oauth/userinfo", method = RequestMethod.POST, produces = "application/json") - public @ResponseBody ResponseEntity getUserInfoPOST(HttpServletRequest request) throws IOException{ + public @ResponseBody ResponseEntity getUserInfoPOST(HttpServletRequest request) throws IOException, JSONException, URISyntaxException, InterruptedException { if (request.getParameter("access_token") != null) { OpenIDConnectUserInfo info = getInfoFromToken(request.getParameter("access_token")); if (info != null) @@ -76,7 +83,7 @@ public class OpenIDController { //lookup token, check it's valid, check scope. //deal with incorrect bearer case in request (I'm looking at you spring security!) - private OpenIDConnectUserInfo getInfoFromToken(String tokenValue) { + private OpenIDConnectUserInfo getInfoFromToken(String tokenValue) throws JSONException, IOException, URISyntaxException, InterruptedException { //TODO: Refactor this to get the information from the database // This is unexpected and should be done with token introspection form the oauth server JSONObject tokenInfo = authorizationServerUtil.tokenIntrospection(tokenValue); @@ -88,17 +95,17 @@ private OpenIDConnectUserInfo getInfoFromToken(String tokenValue) { if(isTokenActive) { // If the token is user revoked it might be used for DELETE requests - Set scopes = OAuth2Utils.parseParameterList(tokenInfo.getString("scope")); - OpenIDConnectUserInfo info = scopes.stream().forEach(s -> - { - ScopePathType scope = ScopePathType.fromValue(s); - if(scope.hasScope(ScopePathType.AUTHENTICATE)) { - OpenIDConnectUserInfo t = new OpenIDConnectUserInfo(); - //TODO set data to the token - //TODO the name doesn't come in the token introspection data, so, we should get it from the names cache - return t; + Set scopes = Arrays.stream(tokenInfo.getString("scope").split("[\\s,]+")) + .collect(Collectors.toSet()); + OpenIDConnectUserInfo info = null; + for(String scope : scopes) { + ScopePathType scopePathType = ScopePathType.fromValue(scope); + if(scopePathType.hasScope(ScopePathType.AUTHENTICATE)) { + String orcid = tokenInfo.getString("username"); + Person person = personDetailsManagerReadOnly.getPublicPersonDetails(orcid); + return new OpenIDConnectUserInfo(orcid,person,path); } - }); + } return info; } return null; @@ -117,4 +124,14 @@ private OpenIDConnectUserInfo getInfoFromToken(String tokenValue) { throw new UnsupportedOperationException("Should be requested from the auth server"); } + @JsonInclude(JsonInclude.Include.NON_NULL) + public static class OpenIDConnectUserInfoAccessDenied extends OpenIDConnectUserInfo{ + @JsonProperty("error") + String error = "access_denied"; + @JsonProperty("error-description") + String errorDescription="access_token is invalid"; + OpenIDConnectUserInfoAccessDenied(){ + + } + } } diff --git a/orcid-web/src/main/java/org/orcid/frontend/web/controllers/PublicProfileController.java b/orcid-web/src/main/java/org/orcid/frontend/web/controllers/PublicProfileController.java index d5bc25a1c34..c276b7ca94c 100644 --- a/orcid-web/src/main/java/org/orcid/frontend/web/controllers/PublicProfileController.java +++ b/orcid-web/src/main/java/org/orcid/frontend/web/controllers/PublicProfileController.java @@ -33,7 +33,6 @@ import org.orcid.core.manager.v3.read_only.ProfileFundingManagerReadOnly; import org.orcid.core.manager.v3.read_only.ResearchResourceManagerReadOnly; import org.orcid.core.manager.v3.read_only.WorkManagerReadOnly; -import org.orcid.core.oauth.OrcidOauth2TokenDetailService; import org.orcid.core.utils.v3.ContributorUtils; import org.orcid.core.utils.v3.SourceUtils; import org.orcid.core.utils.v3.activities.FundingComparators; @@ -127,9 +126,6 @@ public class PublicProfileController extends BaseWorkspaceController { @Resource private OrgDisambiguatedManager orgDisambiguatedManager; - @Resource - private OrcidOauth2TokenDetailService orcidOauth2TokenService; - @Resource(name = "sourceUtilsV3") private SourceUtils sourceUtils; @@ -220,7 +216,7 @@ public ModelAndView publicPreview(HttpServletRequest request, HttpServletRespons } if (!profile.isReviewed()) { - if (!orcidOauth2TokenService.hasToken(orcid, lastModifiedTime)) { + if (!profileEntityManagerReadOnly.hasToken(orcid, lastModifiedTime)) { mav.addObject("noIndex", true); } } @@ -276,7 +272,7 @@ private boolean isRecordReadyForIndexing(ProfileEntity profile) { // False if it is not reviewed and doesn't have any integration if(!profile.isReviewed()) { String userOrcid = profile.getId(); - if (!orcidOauth2TokenService.hasToken(userOrcid, getLastModifiedTime(userOrcid))) { + if (!profileEntityManagerReadOnly.hasToken(userOrcid, getLastModifiedTime(userOrcid))) { // If the user doesn't have any token, check if it was created by member, if so, // verify if that member pushed any work of affiliation on creation time SourceEntity source = profile.getSource(); diff --git a/orcid-web/src/main/java/org/orcid/frontend/web/controllers/RegistrationController.java b/orcid-web/src/main/java/org/orcid/frontend/web/controllers/RegistrationController.java index 4027e47c308..d99e2fe8fc9 100644 --- a/orcid-web/src/main/java/org/orcid/frontend/web/controllers/RegistrationController.java +++ b/orcid-web/src/main/java/org/orcid/frontend/web/controllers/RegistrationController.java @@ -36,7 +36,6 @@ import org.orcid.frontend.spring.ShibbolethAjaxAuthenticationSuccessHandler; import org.orcid.frontend.spring.SocialAjaxAuthenticationSuccessHandler; import org.orcid.frontend.spring.web.social.config.SocialSignInUtils; -import org.orcid.frontend.util.RequestInfoFormLocalCache; import org.orcid.frontend.web.util.RecaptchaVerifier; import org.orcid.jaxb.model.common.AvailableLocales; import org.orcid.jaxb.model.message.CreationMethod; @@ -44,7 +43,6 @@ import org.orcid.jaxb.model.v3.release.record.AffiliationType; import org.orcid.persistence.constants.SendEmailFrequency; import org.orcid.persistence.jpa.entities.EventType; -import org.orcid.pojo.EmailListChange; import org.orcid.pojo.Redirect; import org.orcid.pojo.ajaxForm.AffiliationForm; import org.orcid.pojo.ajaxForm.Date; @@ -127,9 +125,6 @@ public class RegistrationController extends BaseController { @Resource private EventManager eventManager; - @Resource - private RequestInfoFormLocalCache requestInfoFormLocalCache; - @RequestMapping(value = "/register.json", method = RequestMethod.GET) public @ResponseBody Registration getRegister(HttpServletRequest request, HttpServletResponse response) { // Remove the session hash if needed @@ -160,21 +155,6 @@ public class RegistrationController extends BaseController { setError(reg.getTermsOfUse(), "validations.acceptTermsAndConditions"); - RequestInfoForm requestInfoForm = requestInfoFormLocalCache.get(request.getSession().getId()); - if (requestInfoForm != null) { - if (!PojoUtil.isEmpty(requestInfoForm.getUserEmail())) { - reg.getEmail().setValue(requestInfoForm.getUserEmail()); - } - - if (!PojoUtil.isEmpty(requestInfoForm.getUserGivenNames())) { - reg.getGivenNames().setValue(requestInfoForm.getUserGivenNames()); - } - - if (!PojoUtil.isEmpty(requestInfoForm.getUserFamilyNames())) { - reg.getFamilyNames().setValue(requestInfoForm.getUserFamilyNames()); - } - } - long numVal = generateRandomNumForValidation(); reg.setValNumServer(numVal); reg.setValNumClient(0); diff --git a/orcid-web/src/main/java/org/orcid/frontend/web/controllers/helper/OauthHelper.java b/orcid-web/src/main/java/org/orcid/frontend/web/controllers/helper/OauthHelper.java deleted file mode 100644 index c9198e286f1..00000000000 --- a/orcid-web/src/main/java/org/orcid/frontend/web/controllers/helper/OauthHelper.java +++ /dev/null @@ -1,263 +0,0 @@ -package org.orcid.frontend.web.controllers.helper; - -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.nio.charset.StandardCharsets; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.annotation.Resource; - -import org.apache.commons.lang.StringUtils; -import org.orcid.core.locale.LocaleManager; -import org.orcid.core.manager.ClientDetailsEntityCacheManager; -import org.orcid.core.manager.v3.EmailManager; -import org.orcid.core.manager.v3.ProfileEntityManager; -import org.orcid.core.manager.v3.read_only.RecordNameManagerReadOnly; -import org.orcid.frontend.web.controllers.BaseControllerUtil; -import org.orcid.frontend.web.controllers.RegistrationController; -import org.orcid.frontend.web.exception.OauthInvalidRequestException; -import org.orcid.jaxb.model.clientgroup.ClientType; -import org.orcid.jaxb.model.message.ScopePathType; -import org.orcid.jaxb.model.v3.release.record.Name; -import org.orcid.persistence.jpa.entities.ClientDetailsEntity; -import org.orcid.pojo.ajaxForm.PojoUtil; -import org.orcid.pojo.ajaxForm.RequestInfoForm; -import org.orcid.pojo.ajaxForm.ScopeInfoForm; -import org.orcid.utils.OrcidStringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.NoSuchMessageException; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.oauth2.common.exceptions.InvalidRequestException; -import org.springframework.stereotype.Component; - -@Component -public class OauthHelper { - - private static final Logger LOGGER = LoggerFactory.getLogger(OauthHelper.class); - public static final String PUBLIC_MEMBER_NAME = "PubApp"; - - private final Pattern redirectUriPattern = Pattern.compile("redirect_uri=([^&]*)"); - private final Pattern responseTypePattern = Pattern.compile("response_type=([^&]*)"); - private final Pattern stateParamPattern = Pattern.compile("state=([^&]*)"); - private final Pattern orcidPattern = Pattern.compile("(&|\\?)orcid=([^&]*)"); - private final Pattern noncePattern = Pattern.compile("nonce=([^&]*)"); - private final Pattern clientIdPattern = Pattern.compile("client_id=([^&]*)"); - private final Pattern scopesPattern = Pattern.compile("scope=([^&]*)"); - private final Pattern maxAgePattern = Pattern.compile("max_age=([^&]*)"); - - private BaseControllerUtil baseControllerUtil = new BaseControllerUtil(); - - @Resource(name = "profileEntityManagerV3") - protected ProfileEntityManager profileEntityManager; - - @Resource(name = "recordNameManagerReadOnlyV3") - private RecordNameManagerReadOnly recordNameManagerReadOnly; - - @Resource - protected LocaleManager localeManager; - - @Resource - protected ClientDetailsEntityCacheManager clientDetailsEntityCacheManager; - - @Resource(name = "emailManagerV3") - protected EmailManager emailManager; - - public RequestInfoForm generateRequestInfoForm(String requestUrl) throws UnsupportedEncodingException { - RequestInfoForm infoForm = new RequestInfoForm(); - - // If the user is logged in - String loggedUserOrcid = getEffectiveUserOrcid(); - if (!PojoUtil.isEmpty(loggedUserOrcid)) { - infoForm.setUserOrcid(loggedUserOrcid); - String creditName = recordNameManagerReadOnly.fetchDisplayableCreditName(loggedUserOrcid); - if (!PojoUtil.isEmpty(creditName)) { - infoForm.setUserName(URLDecoder.decode(creditName, "UTF-8").trim()); - } - } - - if (!PojoUtil.isEmpty(requestUrl)) { - Matcher matcher = clientIdPattern.matcher(requestUrl); - if (matcher.find()) { - String clientId = matcher.group(1); - // Check if the client has persistent tokens enabled - ClientDetailsEntity clientDetails = clientDetailsEntityCacheManager.retrieve(clientId); - if (clientDetails.isPersistentTokensEnabled()) { - infoForm.setClientHavePersistentTokens(true); - } - - // If client details is ok, continue - String clientName = clientDetails.getClientName() == null ? "" : clientDetails.getClientName(); - String clientEmailRequestReason = clientDetails.getEmailAccessReason() == null ? "" : clientDetails.getEmailAccessReason(); - String clientDescription = clientDetails.getClientDescription() == null ? "" : clientDetails.getClientDescription(); - String memberName = ""; - - // If client type is null it means it is a public client - if (ClientType.PUBLIC_CLIENT.equals(clientDetails.getClientType())) { - memberName = PUBLIC_MEMBER_NAME; - } else if (!PojoUtil.isEmpty(clientDetails.getGroupProfileId())) { - Name name = recordNameManagerReadOnly.getRecordName(clientDetails.getGroupProfileId()); - if (name != null) { - memberName = name.getCreditName() != null ? name.getCreditName().getContent() : ""; - } - } - - // If the group name is empty, use the same as the client - // name, since it should be a SSO user - if (StringUtils.isBlank(memberName)) { - memberName = clientName; - } - infoForm.setClientId(clientId); - infoForm.setClientDescription(clientDescription); - infoForm.setClientName(clientName); - infoForm.setClientEmailRequestReason(clientEmailRequestReason); - infoForm.setMemberName(memberName); - } else { - throw new OauthInvalidRequestException("Please specify a client id"); - } - - Matcher orcidMatcher = orcidPattern.matcher(requestUrl); - boolean userIdSet = false; - if (orcidMatcher.find()) { - String orcid = orcidMatcher.group(2); - try { - orcid = OrcidStringUtils.stripHtml(URLDecoder.decode(orcid, "UTF-8").trim()); - } catch (UnsupportedEncodingException e) { - } - if (!PojoUtil.isEmpty(orcid) && profileEntityManager.orcidExists(orcid)) { - infoForm.setUserId(orcid); - userIdSet = true; - } - } - - Matcher emailMatcher = RegistrationController.emailPattern.matcher(requestUrl); - if (emailMatcher.find()) { - String email = emailMatcher.group(1); - if (email != null && email.contains("%20")) { - email = email.replace("%20", "%2B"); - } - - email = OrcidStringUtils.stripHtml(URLDecoder.decode(email, StandardCharsets.UTF_8).trim()); - - if (!userIdSet && !PojoUtil.isEmpty(email)) { - email = OrcidStringUtils.filterEmailAddress(email); - if (emailManager.emailExists(email)) { - infoForm.setUserId(email); - } - } - infoForm.setUserEmail(email); - } - - Matcher scopeMatcher = scopesPattern.matcher(requestUrl); - if (scopeMatcher.find()) { - String scopes = scopeMatcher.group(1); - String scopesString = URLDecoder.decode(scopes, "UTF-8").trim(); - // Replace any number of spaces or a plus (+) sign with a single space - scopesString = scopesString.replaceAll("( |\\+)+", " "); - if(scopesString == null || scopesString.isBlank()) { - throw new OauthInvalidRequestException("Please specify the desired scopes"); - } - for (ScopePathType theScope : ScopePathType.getScopesFromSpaceSeparatedString(scopesString)) { - ScopeInfoForm scopeInfoForm = new ScopeInfoForm(); - scopeInfoForm.setValue(theScope.value()); - scopeInfoForm.setName(theScope.name()); - try { - scopeInfoForm.setDescription(getMessage(ScopePathType.class.getName() + '.' + theScope.name())); - scopeInfoForm.setLongDescription(getMessage(ScopePathType.class.getName() + '.' + theScope.name() + ".longDesc")); - } catch (NoSuchMessageException e) { - LOGGER.warn("Unable to find key message for scope: " + theScope.name() + " " + theScope.value()); - } - infoForm.getScopes().add(scopeInfoForm); - } - } else { - throw new OauthInvalidRequestException("Please specify the desired scopes"); - } - - Matcher redirectUriMatcher = redirectUriPattern.matcher(requestUrl); - if (redirectUriMatcher.find()) { - try { - infoForm.setRedirectUrl(OrcidStringUtils.stripHtml(URLDecoder.decode(redirectUriMatcher.group(1), "UTF-8").trim())); - } catch (UnsupportedEncodingException e) { - throw new OauthInvalidRequestException("Invalid redirect URL"); - } - } else { - throw new OauthInvalidRequestException("Please specify a redirect URL"); - } - - Matcher stateParamMatcher = stateParamPattern.matcher(requestUrl); - if (stateParamMatcher.find()) { - try { - infoForm.setStateParam(OrcidStringUtils.stripHtml(URLDecoder.decode(stateParamMatcher.group(1), "UTF-8").trim())); - } catch (UnsupportedEncodingException e) { - - } - } - - Matcher responseTypeMatcher = responseTypePattern.matcher(requestUrl); - if (responseTypeMatcher.find()) { - try { - infoForm.setResponseType(OrcidStringUtils.stripHtml(URLDecoder.decode(responseTypeMatcher.group(1), "UTF-8").trim())); - } catch (UnsupportedEncodingException e) { - throw new OauthInvalidRequestException("Invalid response type"); - } - } else { - throw new OauthInvalidRequestException("Please specify a response type"); - } - - Matcher givenNamesMatcher = RegistrationController.givenNamesPattern.matcher(requestUrl); - if (givenNamesMatcher.find()) { - infoForm.setUserGivenNames(OrcidStringUtils.stripHtml(URLDecoder.decode(givenNamesMatcher.group(1), "UTF-8").trim())); - } - - Matcher familyNamesMatcher = RegistrationController.familyNamesPattern.matcher(requestUrl); - if (familyNamesMatcher.find()) { - infoForm.setUserFamilyNames(OrcidStringUtils.stripHtml(URLDecoder.decode(familyNamesMatcher.group(1), "UTF-8").trim())); - } - - Matcher nonceMatcher = noncePattern.matcher(requestUrl); - if (nonceMatcher.find()) { - infoForm.setNonce(OrcidStringUtils.stripHtml(URLDecoder.decode(nonceMatcher.group(1), "UTF-8").trim())); - } - - Matcher maxAgeMatcher = maxAgePattern.matcher(requestUrl); - if (maxAgeMatcher.find()) { - String maxAge = OrcidStringUtils.stripHtml(URLDecoder.decode(maxAgeMatcher.group(1), "UTF-8").trim()); - if(!PojoUtil.isEmpty(maxAge)) { - try { - Long.parseLong(maxAge); - } catch(NumberFormatException nfe) { - throw new InvalidRequestException("Invalid max_age param"); - } - } - infoForm.setMaxAge(maxAge); - } - } - - return infoForm; - } - - public void setUserName(RequestInfoForm requestInfoForm) throws UnsupportedEncodingException { - String loggedUserOrcid = getEffectiveUserOrcid(); - if (!PojoUtil.isEmpty(loggedUserOrcid)) { - requestInfoForm.setUserOrcid(loggedUserOrcid); - String creditName = recordNameManagerReadOnly.fetchDisplayableCreditName(loggedUserOrcid); - if (!PojoUtil.isEmpty(creditName)) { - requestInfoForm.setUserName(URLDecoder.decode(creditName, "UTF-8").trim()); - } - } - } - - private String getEffectiveUserOrcid() { - UserDetails currentUser = baseControllerUtil.getCurrentUser(SecurityContextHolder.getContext()); - if (currentUser == null) { - return null; - } - return currentUser.getUsername(); - } - - public String getMessage(String messageCode, Object... messageParams) { - return localeManager.resolveMessage(messageCode, messageParams); - } -} diff --git a/orcid-web/src/main/java/org/orcid/frontend/web/exception/OauthInvalidRequestException.java b/orcid-web/src/main/java/org/orcid/frontend/web/exception/OauthInvalidRequestException.java deleted file mode 100644 index 678da54756d..00000000000 --- a/orcid-web/src/main/java/org/orcid/frontend/web/exception/OauthInvalidRequestException.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.orcid.frontend.web.exception; - -import org.orcid.pojo.ajaxForm.RequestInfoForm; -import org.springframework.security.oauth2.common.exceptions.InvalidRequestException; - -@SuppressWarnings("serial") -public class OauthInvalidRequestException extends InvalidRequestException { - - public OauthInvalidRequestException(final String msg) { - super(msg); - } - - public OauthInvalidRequestException(final String msg, final Throwable t) { - super(msg, t); - } - -} diff --git a/orcid-web/src/main/java/org/orcid/frontend/web/filter/OAuthAuthorizeNotSignedInFilter.java b/orcid-web/src/main/java/org/orcid/frontend/web/filter/OAuthAuthorizeNotSignedInFilter.java deleted file mode 100644 index 10e6339cf2c..00000000000 --- a/orcid-web/src/main/java/org/orcid/frontend/web/filter/OAuthAuthorizeNotSignedInFilter.java +++ /dev/null @@ -1,87 +0,0 @@ -package org.orcid.frontend.web.filter; - -import java.io.IOException; - -import javax.annotation.Resource; -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; - -import org.orcid.core.constants.OrcidOauth2Constants; -import org.orcid.core.manager.impl.OrcidUrlManager; -import org.orcid.frontend.util.RequestInfoFormLocalCache; -import org.orcid.frontend.web.controllers.BaseControllerUtil; -import org.orcid.frontend.web.controllers.helper.OauthHelper; -import org.orcid.pojo.ajaxForm.RequestInfoForm; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.web.savedrequest.HttpSessionRequestCache; - -/** - * - * @author rcpeters - * - */ -public class OAuthAuthorizeNotSignedInFilter implements Filter { - - private BaseControllerUtil baseControllerUtil = new BaseControllerUtil(); - - @Resource - private OrcidUrlManager orcidUrlManager; - - @Resource - private OauthHelper oauthHelper; - - @Resource - private RequestInfoFormLocalCache requestInfoFormLocalCache; - - @Override - public void destroy() { - // Do nothing - } - - public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { - HttpServletRequest request = (HttpServletRequest) req; - if (OrcidUrlManager.getPathWithoutContextPath(request).equals("/oauth/authorize")) { - HttpServletResponse response = (HttpServletResponse) res; - HttpSession session = request.getSession(); - String queryString = request.getQueryString(); - boolean forceLogin = false; - - if (OrcidOauth2Constants.PROMPT_LOGIN.equals(request.getParameter(OrcidOauth2Constants.PROMPT))) { - //remove prompt param later on to prevent loop - forceLogin = true; - } - - //users not logged in must sign in - SecurityContext sci = null; - if (session != null) - sci = (SecurityContext)session.getAttribute("SPRING_SECURITY_CONTEXT"); - if (forceLogin || baseControllerUtil.getCurrentUser(sci) == null) { - if (session != null) { - new HttpSessionRequestCache().saveRequest(request, response); - RequestInfoForm rif = oauthHelper.generateRequestInfoForm(request.getQueryString()); - - // Store the request info form in the cache - requestInfoFormLocalCache.put(request.getSession().getId(), rif); - - request.getSession().setAttribute(OrcidOauth2Constants.OAUTH_QUERY_STRING, queryString); - } - response.sendRedirect(orcidUrlManager.getBaseUrl() + "/signin?oauth&" + queryString); - return; - } - } - chain.doFilter(req, res); - } - - @Override - public void init(FilterConfig arg0) throws ServletException { - // Do nothing - } - -} diff --git a/orcid-web/src/main/java/org/orcid/frontend/web/util/ThirdPartyLinkManager.java b/orcid-web/src/main/java/org/orcid/frontend/web/util/ThirdPartyLinkManager.java index 6e18c6715f2..c62e52ce608 100644 --- a/orcid-web/src/main/java/org/orcid/frontend/web/util/ThirdPartyLinkManager.java +++ b/orcid-web/src/main/java/org/orcid/frontend/web/util/ThirdPartyLinkManager.java @@ -7,8 +7,8 @@ import javax.annotation.Resource; import org.orcid.core.locale.LocaleManager; -import org.orcid.core.oauth.OrcidOauth2TokenDetailService; import org.orcid.core.manager.ClientDetailsEntityCacheManager; +import org.orcid.core.manager.v3.read_only.ClientDetailsManagerReadOnly; import org.orcid.core.utils.JsonUtils; import org.orcid.core.utils.cache.redis.RedisClient; import org.orcid.jaxb.model.clientgroup.RedirectUriType; @@ -42,12 +42,12 @@ public class ThirdPartyLinkManager implements InitializingBean { @Resource private LocaleManager localeManager; - @Resource - private OrcidOauth2TokenDetailService orcidOauth2TokenDetailService; - @Resource private RedisClient redisClient; + @Resource(name = "clientDetailsManagerReadOnlyV3") + private ClientDetailsManagerReadOnly clientDetailsManagerReadOnly; + @Value("${org.orcid.core.utils.cache.redis.works-search-and-link-wizard.ttl:3600}") private int worksSearchAndLinkWizardCacheTtl; @@ -105,7 +105,7 @@ public List findSearchAndLinkWizardClients(Strin List list = getWorksSearchAndLinkWizardBaseList(); for (SearchAndLinkWizardFormSummary form : list) { form.setConnected(StringUtils.isNotBlank(currentUserOrcid) - && orcidOauth2TokenDetailService.doesClientKnowUser(form.getId(), currentUserOrcid)); + && clientDetailsManagerReadOnly.doesClientKnowUser(form.getId(), currentUserOrcid)); } return list; } diff --git a/orcid-web/src/main/resources/orcid-frontend-security.xml b/orcid-web/src/main/resources/orcid-frontend-security.xml index 03e0d92d914..e469dae90c9 100644 --- a/orcid-web/src/main/resources/orcid-frontend-security.xml +++ b/orcid-web/src/main/resources/orcid-frontend-security.xml @@ -2,11 +2,9 @@ + http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> @@ -189,48 +187,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/orcid-web/src/test/java/org/orcid/frontend/web/controllers/PublicProfileControllerTest.java b/orcid-web/src/test/java/org/orcid/frontend/web/controllers/PublicProfileControllerTest.java index a95abc2f5cc..3a4f673aae3 100644 --- a/orcid-web/src/test/java/org/orcid/frontend/web/controllers/PublicProfileControllerTest.java +++ b/orcid-web/src/test/java/org/orcid/frontend/web/controllers/PublicProfileControllerTest.java @@ -30,7 +30,6 @@ import org.orcid.core.manager.EncryptionManager; import org.orcid.core.manager.ProfileEntityCacheManager; import org.orcid.core.manager.v3.read_only.ProfileEntityManagerReadOnly; -import org.orcid.core.oauth.OrcidOauth2TokenDetailService; import org.orcid.jaxb.model.common.Iso3166Country; import org.orcid.jaxb.model.v3.release.common.Visibility; import org.orcid.jaxb.model.v3.release.record.Address; @@ -102,9 +101,6 @@ public class PublicProfileControllerTest extends DBUnitTest { @Mock private ProfileEntityCacheManager profileEntityCacheManagerMock; - @Mock - private OrcidOauth2TokenDetailService orcidOauth2TokenServiceMock; - @Mock private ProfileEntityManagerReadOnly profileEntityManagerReadOnlyMock; @@ -393,7 +389,6 @@ public void getUserInfoTest() { private void setupUserInfoMocks() { SourceEntity sourceEntity = new SourceEntity(new ClientDetailsEntity("APP-000000000001")); - TargetProxyHelper.injectIntoProxy(publicProfileController, "orcidOauth2TokenService", orcidOauth2TokenServiceMock); TargetProxyHelper.injectIntoProxy(publicProfileController, "profileEntityCacheManager", profileEntityCacheManagerMock); TargetProxyHelper.injectIntoProxy(publicProfileController, "profileEntityManagerReadOnly", profileEntityManagerReadOnlyMock); @@ -407,7 +402,7 @@ private void setupUserInfoMocks() { reviewedNoIntegrations.setRecordLocked(false); reviewedNoIntegrations.setReviewed(true); - when(orcidOauth2TokenServiceMock.hasToken(eq(reviewedNoIntegrationsOrcid), anyLong())).thenReturn(false); + when(profileEntityManagerReadOnlyMock.hasToken(eq(reviewedNoIntegrationsOrcid), anyLong())).thenReturn(false); when(profileEntityCacheManagerMock.retrieve(reviewedNoIntegrationsOrcid)).thenReturn(reviewedNoIntegrations); // Reviewed record with integrations @@ -415,7 +410,7 @@ private void setupUserInfoMocks() { reviewedWithIntegrations.setRecordLocked(false); reviewedWithIntegrations.setReviewed(true); - when(orcidOauth2TokenServiceMock.hasToken(eq(reviewedWithIntegrationsOrcid), anyLong())).thenReturn(true); + when(profileEntityManagerReadOnlyMock.hasToken(eq(reviewedWithIntegrationsOrcid), anyLong())).thenReturn(true); when(profileEntityCacheManagerMock.retrieve(reviewedWithIntegrationsOrcid)).thenReturn(reviewedWithIntegrations); // Un-reviewed record with no integrations @@ -423,7 +418,7 @@ private void setupUserInfoMocks() { unreviewedNoIntegrations.setRecordLocked(false); unreviewedNoIntegrations.setReviewed(false); - when(orcidOauth2TokenServiceMock.hasToken(eq(unreviewedNoIntegrationsOrcid), anyLong())).thenReturn(false); + when(profileEntityManagerReadOnlyMock.hasToken(eq(unreviewedNoIntegrationsOrcid), anyLong())).thenReturn(false); when(profileEntityCacheManagerMock.retrieve(unreviewedNoIntegrationsOrcid)).thenReturn(unreviewedNoIntegrations); // Un-reviewed record with integrations @@ -431,7 +426,7 @@ private void setupUserInfoMocks() { unreviewedWithIntegrations.setRecordLocked(false); unreviewedWithIntegrations.setReviewed(false); - when(orcidOauth2TokenServiceMock.hasToken(eq(unreviewedWithIntegrationsOrcid), anyLong())).thenReturn(true); + when(profileEntityManagerReadOnlyMock.hasToken(eq(unreviewedWithIntegrationsOrcid), anyLong())).thenReturn(true); when(profileEntityCacheManagerMock.retrieve(unreviewedWithIntegrationsOrcid)).thenReturn(unreviewedWithIntegrations); // Un reviewed record, created by member, with no integrations and no activities @@ -440,7 +435,7 @@ private void setupUserInfoMocks() { unreviewedWithIntegrations.setRecordLocked(false); unreviewedWithIntegrations.setReviewed(false); - when(orcidOauth2TokenServiceMock.hasToken(eq(unreviewedCreatedByMembersWithNoActivitiesOrcid), anyLong())).thenReturn(false); + when(profileEntityManagerReadOnlyMock.hasToken(eq(unreviewedCreatedByMembersWithNoActivitiesOrcid), anyLong())).thenReturn(false); when(profileEntityCacheManagerMock.retrieve(eq(unreviewedCreatedByMembersWithNoActivitiesOrcid))).thenReturn(unreviewedCreatedByMembersWithNoActivities); @@ -450,7 +445,7 @@ private void setupUserInfoMocks() { unreviewedWithIntegrations.setRecordLocked(false); unreviewedWithIntegrations.setReviewed(false); - when(orcidOauth2TokenServiceMock.hasToken(eq(unreviewedCreatedByMembersWithActivitiesOrcid), anyLong())).thenReturn(false); + when(profileEntityManagerReadOnlyMock.hasToken(eq(unreviewedCreatedByMembersWithActivitiesOrcid), anyLong())).thenReturn(false); when(profileEntityCacheManagerMock.retrieve(eq(unreviewedCreatedByMembersWithActivitiesOrcid))).thenReturn(unreviewedCreatedByMembersWithActivities); // Deprecated @@ -459,7 +454,7 @@ private void setupUserInfoMocks() { deprecated.setReviewed(true); deprecated.setPrimaryRecord(new ProfileEntity(primaryRecord)); - when(orcidOauth2TokenServiceMock.hasToken(eq(deprecatedUserOrcid), anyLong())).thenReturn(true); + when(profileEntityManagerReadOnlyMock.hasToken(eq(deprecatedUserOrcid), anyLong())).thenReturn(true); when(profileEntityCacheManagerMock.retrieve(deprecatedUserOrcid)).thenReturn(deprecated); // Locked @@ -467,7 +462,7 @@ private void setupUserInfoMocks() { locked.setRecordLocked(true); locked.setReviewed(true); - when(orcidOauth2TokenServiceMock.hasToken(eq(lockedUserOrcid), anyLong())).thenReturn(true); + when(profileEntityManagerReadOnlyMock.hasToken(eq(lockedUserOrcid), anyLong())).thenReturn(true); when(profileEntityCacheManagerMock.retrieve(lockedUserOrcid)).thenReturn(locked); @@ -476,7 +471,7 @@ private void setupUserInfoMocks() { allOk.setRecordLocked(false); allOk.setReviewed(true); - when(orcidOauth2TokenServiceMock.hasToken(eq(userOrcid), anyLong())).thenReturn(true); + when(profileEntityManagerReadOnlyMock.hasToken(eq(userOrcid), anyLong())).thenReturn(true); when(profileEntityCacheManagerMock.retrieve(userOrcid)).thenReturn(allOk); } } diff --git a/orcid-web/src/test/java/org/orcid/frontend/web/filter/OAuthAuthorizeNotSignedInFilterTest.java b/orcid-web/src/test/java/org/orcid/frontend/web/filter/OAuthAuthorizeNotSignedInFilterTest.java deleted file mode 100644 index 6f6b81154c2..00000000000 --- a/orcid-web/src/test/java/org/orcid/frontend/web/filter/OAuthAuthorizeNotSignedInFilterTest.java +++ /dev/null @@ -1,185 +0,0 @@ -package org.orcid.frontend.web.filter; - -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.io.IOException; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Vector; - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; -import org.orcid.core.constants.OrcidOauth2Constants; -import org.orcid.core.manager.impl.OrcidUrlManager; -import org.orcid.frontend.util.RequestInfoFormLocalCache; -import org.orcid.frontend.web.controllers.helper.OauthHelper; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.core.userdetails.UserDetails; - -public class OAuthAuthorizeNotSignedInFilterTest { - - @InjectMocks - OAuthAuthorizeNotSignedInFilter oaFilter; - - @Mock - OrcidUrlManager orcidUrlManager; - - @Mock - HttpServletRequest request; - - @Mock - HttpServletResponse response; - - @Mock - HttpSession session; - - @Mock - FilterChain chain; - - @Mock - SecurityContext context; - - @Mock - UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken; - - @Mock - UserDetails orcidProfileUserDetails; - - @Mock - OauthHelper oauthHelper; - - @Mock - RequestInfoFormLocalCache requestInfoFormLocalCache; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - when(orcidUrlManager.getBaseUrl()).thenReturn("http://test.com"); - when(request.getHeaderNames()).thenReturn(new Vector().elements()); - when(request.getLocales()).thenReturn(new Vector().elements()); - when(request.getParameterMap()).thenReturn(new HashMap()); - when(request.getScheme()).thenReturn("i hate you with all my heart spring mvc"); - when(request.getRequestURL()).thenReturn(new StringBuffer("really, we should break up")); - when(session.getId()).thenReturn("ID1"); - } - - @Test - public void nullSession() throws IOException, ServletException { - when(request.getContextPath()).thenReturn("http://test.com"); - when(request.getRequestURI()).thenReturn("http://test.com/oauth/authorize"); - when(request.getQueryString()).thenReturn("test_param=param"); - when(request.getSession(false)).thenReturn(null); - - oaFilter.doFilter((ServletRequest) request, (ServletResponse) response, chain); - - verify(response).sendRedirect("http://test.com/signin?oauth&test_param=param"); - verify(chain, never()).doFilter(Mockito.any(), Mockito.any()); - } - - @Test - public void noSecurityContext() throws IOException, ServletException { - when(request.getContextPath()).thenReturn("http://test.com"); - when(request.getRequestURI()).thenReturn("http://test.com/oauth/authorize"); - when(request.getQueryString()).thenReturn("test_param=param"); - when(request.getSession()).thenReturn(session); - when(request.getSession(false)).thenReturn(session); - - oaFilter.doFilter((ServletRequest) request, (ServletResponse) response, chain); - - verify(response).sendRedirect("http://test.com/signin?oauth&test_param=param"); - verify(chain, never()).doFilter(Mockito.any(), Mockito.any()); - } - - @Test - public void noAuthentication() throws IOException, ServletException { - when(request.getContextPath()).thenReturn("http://test.com"); - when(request.getRequestURI()).thenReturn("http://test.com/oauth/authorize"); - when(request.getQueryString()).thenReturn("test_param=param"); - when(request.getSession()).thenReturn(session); - when(request.getSession(false)).thenReturn(session); - when(session.getAttribute("SPRING_SECURITY_CONTEXT")).thenReturn(context); - - oaFilter.doFilter((ServletRequest) request, (ServletResponse) response, chain); - - verify(response).sendRedirect("http://test.com/signin?oauth&test_param=param"); - verify(chain, never()).doFilter(Mockito.any(), Mockito.any()); - } - - @Test - public void hasOrcidProfileUserDetails() throws IOException, ServletException { - when(request.getContextPath()).thenReturn("http://test.com"); - when(request.getRequestURI()).thenReturn("http://test.com/oauth/authorize"); - when(request.getQueryString()).thenReturn("test_param=param"); - when(request.getSession()).thenReturn(session); - when(request.getSession(false)).thenReturn(session); - when(session.getAttribute("SPRING_SECURITY_CONTEXT")).thenReturn(context); - when(context.getAuthentication()).thenReturn(usernamePasswordAuthenticationToken); - when(usernamePasswordAuthenticationToken.getName()).thenReturn("0000-0000-0000-0000"); - when(usernamePasswordAuthenticationToken.getCredentials()).thenReturn("password"); - - oaFilter.doFilter((ServletRequest) request, (ServletResponse) response, chain); - - verify(response, never()).sendRedirect(Mockito.anyString()); - verify(chain).doFilter(Mockito.any(), Mockito.any()); - } - - @Test - public void notUriOauthAuthorize() throws IOException, ServletException { - when(request.getContextPath()).thenReturn("http://test.com"); - when(request.getRequestURI()).thenReturn("http://test.com/signin?oauth"); - when(request.getQueryString()).thenReturn("test_param=param"); - when(request.getSession(false)).thenReturn(null); - - oaFilter.doFilter((ServletRequest) request, (ServletResponse) response, chain); - - verify(response, never()).sendRedirect(Mockito.anyString()); - verify(chain).doFilter(Mockito.any(), Mockito.any()); - } - - @Test - public void oauth2ScreensFeatureEnabledTest() throws IOException, ServletException { - when(request.getContextPath()).thenReturn("http://test.com"); - when(request.getRequestURI()).thenReturn("http://test.com/oauth/authorize"); - when(request.getQueryString()).thenReturn("test_param=param"); - when(request.getSession()).thenReturn(session); - when(request.getSession(false)).thenReturn(session); - when(session.getAttribute("SPRING_SECURITY_CONTEXT")).thenReturn(context); - - oaFilter.doFilter((ServletRequest) request, (ServletResponse) response, chain); - - verify(response).sendRedirect("http://test.com/signin?oauth&test_param=param"); - verify(chain, never()).doFilter(Mockito.any(), Mockito.any()); - } - - @Test - public void oauth2ScreensFeatureFlagUsedTest() throws IOException, ServletException { - when(request.getContextPath()).thenReturn("http://test.com"); - when(request.getRequestURI()).thenReturn("http://test.com/oauth/authorize"); - when(request.getQueryString()).thenReturn("test_param=param&" + OrcidOauth2Constants.OAUTH_2SCREENS); - when(request.getSession()).thenReturn(session); - when(request.getSession(false)).thenReturn(session); - when(session.getAttribute("SPRING_SECURITY_CONTEXT")).thenReturn(context); - - oaFilter.doFilter((ServletRequest) request, (ServletResponse) response, chain); - - verify(response).sendRedirect("http://test.com/signin?oauth&test_param=param&" + OrcidOauth2Constants.OAUTH_2SCREENS); - verify(chain, never()).doFilter(Mockito.any(), Mockito.any()); - } -} From bf574f2746569e5362cab3ef6cb57c52f944d2bc Mon Sep 17 00:00:00 2001 From: amontenegro Date: Thu, 26 Mar 2026 18:59:41 -0600 Subject: [PATCH 11/16] It is compiling! --- .../common/filter/TokenTargetFilterTest.java | 25 +- ...ClientCredentialEndPointDelegatorTest.java | 345 ------------------ .../core/utils/SecurityContextTestUtils.java | 19 +- .../src/main/resources/orcid-core-context.xml | 7 +- .../NamespacedRandomCodeGeneratorTest.java | 157 -------- .../api/filters/ApiRateLimitFilterTest.java | 25 -- .../resources/orcid-core-context-spam.xml | 7 +- .../frontend/email/RecordEmailSenderTest.java | 26 -- .../frontend/oauth2/RevokeControllerTest.java | 175 ++++----- .../OauthGenericCallsControllerTest.java | 73 ---- .../OauthRegistrationControllerTest.java | 135 ------- .../web/util/ThirdPartyLinkManagerTest.java | 16 +- 12 files changed, 94 insertions(+), 916 deletions(-) delete mode 100644 orcid-api-common/src/test/java/org/orcid/api/common/oauth/OrcidClientCredentialEndPointDelegatorTest.java rename orcid-core/src/{test => main}/java/org/orcid/core/utils/SecurityContextTestUtils.java (83%) delete mode 100644 orcid-core/src/test/java/org/orcid/core/oauth/service/NamespacedRandomCodeGeneratorTest.java delete mode 100644 orcid-web/src/test/java/org/orcid/frontend/web/controllers/OauthGenericCallsControllerTest.java delete mode 100644 orcid-web/src/test/java/org/orcid/frontend/web/controllers/OauthRegistrationControllerTest.java diff --git a/orcid-api-common/src/test/java/org/orcid/api/common/filter/TokenTargetFilterTest.java b/orcid-api-common/src/test/java/org/orcid/api/common/filter/TokenTargetFilterTest.java index 99d936597c8..1b8d45508f0 100644 --- a/orcid-api-common/src/test/java/org/orcid/api/common/filter/TokenTargetFilterTest.java +++ b/orcid-api-common/src/test/java/org/orcid/api/common/filter/TokenTargetFilterTest.java @@ -15,16 +15,14 @@ import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; -import org.orcid.api.common.filter.TokenTargetFilter; import org.orcid.core.exception.OrcidUnauthorizedException; -import org.orcid.core.oauth.OrcidOAuth2Authentication; +import org.orcid.core.oauth.OrcidBearerTokenAuthentication; import org.orcid.jaxb.model.message.ScopePathType; import org.orcid.persistence.jpa.entities.ProfileEntity; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextImpl; -import org.springframework.security.oauth2.provider.OAuth2Request; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import org.glassfish.jersey.server.ContainerRequest; @@ -104,29 +102,16 @@ public void readPublicTokenTest() { private void setUpSecurityContext(String userOrcid, String clientId, ScopePathType... scopePathTypes) { SecurityContextImpl securityContext = new SecurityContextImpl(); - OrcidOAuth2Authentication mockedAuthentication = mock(OrcidOAuth2Authentication.class); + OrcidBearerTokenAuthentication mockedAuthentication = mock(OrcidBearerTokenAuthentication.class); securityContext.setAuthentication(mockedAuthentication); SecurityContextHolder.setContext(securityContext); if(userOrcid != null) { - ProfileEntity userProfileEntity = new ProfileEntity(userOrcid); - when(mockedAuthentication.getPrincipal()).thenReturn(userProfileEntity); - Authentication userAuthentication = mock(Authentication.class); - when(userAuthentication.getPrincipal()).thenReturn(userProfileEntity); - when(mockedAuthentication.getUserAuthentication()).thenReturn(userAuthentication); + when(mockedAuthentication.getPrincipal()).thenReturn(clientId); + when(mockedAuthentication.getUserOrcid()).thenReturn(userOrcid); } else { when(mockedAuthentication.getPrincipal()).thenReturn(clientId); } - - Set scopes = new HashSet(); - if (scopePathTypes != null) { - for (ScopePathType scopePathType : scopePathTypes) { - scopes.add(scopePathType.value()); - } - } - OAuth2Request authorizationRequest = new OAuth2Request(Collections. emptyMap(), clientId, - Collections. emptyList(), true, scopes, Collections. emptySet(), null, Collections. emptySet(), - Collections. emptyMap()); - when(mockedAuthentication.getOAuth2Request()).thenReturn(authorizationRequest); + when(mockedAuthentication.isAuthenticated()).thenReturn(true); } } diff --git a/orcid-api-common/src/test/java/org/orcid/api/common/oauth/OrcidClientCredentialEndPointDelegatorTest.java b/orcid-api-common/src/test/java/org/orcid/api/common/oauth/OrcidClientCredentialEndPointDelegatorTest.java deleted file mode 100644 index 2598c1ad043..00000000000 --- a/orcid-api-common/src/test/java/org/orcid/api/common/oauth/OrcidClientCredentialEndPointDelegatorTest.java +++ /dev/null @@ -1,345 +0,0 @@ -package org.orcid.api.common.oauth; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; - -import javax.annotation.Resource; -import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.core.Response; - -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; -import org.orcid.core.constants.OrcidOauth2Constants; -import org.orcid.core.oauth.openid.OpenIDConnectKeyService; -import org.orcid.core.utils.JsonUtils; -import org.orcid.core.utils.SecurityContextTestUtils; -import org.orcid.core.utils.cache.redis.RedisClient; -import org.orcid.jaxb.model.message.ScopePathType; -import org.orcid.persistence.dao.OrcidOauth2AuthoriziationCodeDetailDao; -import org.orcid.persistence.dao.ProfileLastModifiedDao; -import org.orcid.persistence.jpa.entities.ClientDetailsEntity; -import org.orcid.persistence.jpa.entities.IndexingStatus; -import org.orcid.persistence.jpa.entities.OrcidOauth2AuthoriziationCodeDetail; -import org.orcid.persistence.jpa.entities.ProfileEntity; -import org.orcid.pojo.ajaxForm.PojoUtil; -import org.orcid.test.DBUnitTest; -import org.orcid.test.OrcidJUnit4ClassRunner; -import org.orcid.test.TargetProxyHelper; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; -import org.springframework.security.oauth2.common.exceptions.InvalidScopeException; -import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; -import org.springframework.test.context.ContextConfiguration; - - -import javax.ws.rs.core.MultivaluedHashMap; - -@RunWith(OrcidJUnit4ClassRunner.class) -@ContextConfiguration(locations = { "classpath:test-orcid-api-common-context.xml"}) -public class OrcidClientCredentialEndPointDelegatorTest extends DBUnitTest { - - private static final String CLIENT_ID_1 = "APP-5555555555555555"; - private static final String USER_ORCID = "0000-0000-0000-0001"; - - @Resource - private OrcidOauth2AuthoriziationCodeDetailDao orcidOauth2AuthoriziationCodeDetailDao; - - @Resource - private OrcidClientCredentialEndPointDelegator orcidClientCredentialEndPointDelegator; - - @Resource - private OpenIDConnectKeyService keyManager; - - @Mock - private ProfileLastModifiedDao profileLastModifiedDaoMock; - - @Mock - private RedisClient redisClientMock; - - @Resource - private ProfileLastModifiedDao profileLastModifiedDao; - - @BeforeClass - public static void initDBUnitData() throws Exception { - initDBUnitData( - Arrays.asList("/data/SubjectEntityData.xml", "/data/SourceClientDetailsEntityData.xml", "/data/ProfileEntityData.xml", "/data/RecordNameEntityData.xml")); - } - - @Before - public void before() { - MockitoAnnotations.initMocks(this); - TargetProxyHelper.injectIntoProxy(orcidClientCredentialEndPointDelegator, "profileLastModifiedDao", profileLastModifiedDaoMock); - TargetProxyHelper.injectIntoProxy(orcidClientCredentialEndPointDelegator, "redisClient", redisClientMock); - // Keep the cache disabled - orcidClientCredentialEndPointDelegator.setTokenCacheEnabled(false); - } - - @AfterClass - public static void removeDBUnitData() throws Exception { - removeDBUnitData( - Arrays.asList("/data/RecordNameEntityData.xml", "/data/ProfileEntityData.xml", "/data/SourceClientDetailsEntityData.xml", "/data/SubjectEntityData.xml")); - } - - @After - public void after() { - SecurityContextHolder.clearContext(); - TargetProxyHelper.injectIntoProxy(orcidClientCredentialEndPointDelegator, "profileLastModifiedDao", profileLastModifiedDao); - } - - private OrcidOauth2AuthoriziationCodeDetail createAuthorizationCode(String value, String clientId, String redirectUri, boolean persistent, String... scopes) { - OrcidOauth2AuthoriziationCodeDetail authorizationCode = new OrcidOauth2AuthoriziationCodeDetail(); - authorizationCode.setId(value); - authorizationCode.setApproved(true); - authorizationCode.setScopes(new HashSet(Arrays.asList(scopes))); - authorizationCode.setClientDetailsEntity(new ClientDetailsEntity(clientId)); - authorizationCode.setPersistent(persistent); - authorizationCode.setOrcid(USER_ORCID); - authorizationCode.setRedirectUri(redirectUri); - authorizationCode.setResourceIds(new HashSet(Arrays.asList("orcid"))); - authorizationCode.setAuthenticated(true); - orcidOauth2AuthoriziationCodeDetailDao.persist(authorizationCode); - return authorizationCode; - } - - @Test - public void generateAccessTokenTest() { - SecurityContextTestUtils.setUpSecurityContextForClientOnly(CLIENT_ID_1, ScopePathType.ACTIVITIES_UPDATE, ScopePathType.READ_LIMITED); - OrcidOauth2AuthoriziationCodeDetail authCode = createAuthorizationCode("code-1", CLIENT_ID_1, "http://www.APP-5555555555555555.com/redirect/oauth", true, - "/activities/update"); - MultivaluedMap formParams = new MultivaluedHashMap(); - formParams.add("client_id", CLIENT_ID_1); - formParams.add("client_secret", "DhkFj5EI0qp6GsUKi55Vja+h+bsaKpBx"); - formParams.add("grant_type", "authorization_code"); - formParams.add("redirect_uri", "http://www.APP-5555555555555555.com/redirect/oauth"); - formParams.add("code", authCode.getId()); - Response response = orcidClientCredentialEndPointDelegator.obtainOauth2Token(null, formParams); - assertNotNull(response); - assertNotNull(response.getEntity()); - DefaultOAuth2AccessToken token = (DefaultOAuth2AccessToken) response.getEntity(); - assertNotNull(token); - assertTrue(!PojoUtil.isEmpty(token.getValue())); - assertNotNull(token.getRefreshToken()); - assertTrue(!PojoUtil.isEmpty(token.getRefreshToken().getValue())); - verify(profileLastModifiedDaoMock, times(1)).updateIndexingStatus(Mockito.any(), Mockito.any()); - } - - @Test - public void generateClientCredentialsAccessTokenTest() { - SecurityContextTestUtils.setUpSecurityContextForClientOnly(CLIENT_ID_1, ScopePathType.ACTIVITIES_UPDATE, ScopePathType.READ_LIMITED); - MultivaluedMap formParams = new MultivaluedHashMap(); - formParams.add("client_id", CLIENT_ID_1); - formParams.add("client_secret", "DhkFj5EI0qp6GsUKi55Vja+h+bsaKpBx"); - formParams.add("grant_type", "client_credentials"); - formParams.add("redirect_uri", "http://www.APP-5555555555555555.com/redirect/oauth"); - formParams.add("scope", "/orcid-profile/create"); - Response response = orcidClientCredentialEndPointDelegator.obtainOauth2Token(null, formParams); - assertNotNull(response); - assertNotNull(response.getEntity()); - DefaultOAuth2AccessToken token = (DefaultOAuth2AccessToken) response.getEntity(); - assertNotNull(token); - assertTrue(!PojoUtil.isEmpty(token.getValue())); - assertNotNull(token.getRefreshToken()); - assertTrue(!PojoUtil.isEmpty(token.getRefreshToken().getValue())); - - verify(profileLastModifiedDaoMock, times(0)).updateIndexingStatus(Arrays.asList(USER_ORCID), IndexingStatus.PENDING); - } - - @Test(expected = InvalidScopeException.class) - public void generateClientCredentialsAccessTokenWithInvalidTokenTest() { - SecurityContextTestUtils.setUpSecurityContextForClientOnly(CLIENT_ID_1, ScopePathType.ACTIVITIES_UPDATE); - MultivaluedMap formParams = new MultivaluedHashMap(); - formParams.add("client_id", CLIENT_ID_1); - formParams.add("client_secret", "DhkFj5EI0qp6GsUKi55Vja+h+bsaKpBx"); - formParams.add("grant_type", "client_credentials"); - formParams.add("redirect_uri", "http://www.APP-5555555555555555.com/redirect/oauth"); - formParams.add("scope", "/activities/update"); - orcidClientCredentialEndPointDelegator.obtainOauth2Token(null, formParams); - fail(); - } - - @Test - public void generateRefreshTokenTest() { - // Generate the access token - SecurityContextTestUtils.setUpSecurityContextForClientOnly(CLIENT_ID_1, ScopePathType.ACTIVITIES_UPDATE, ScopePathType.READ_LIMITED); - OrcidOauth2AuthoriziationCodeDetail authCode = createAuthorizationCode("code-1", CLIENT_ID_1, "http://www.APP-5555555555555555.com/redirect/oauth", true, - "/activities/update"); - MultivaluedMap formParams = new MultivaluedHashMap(); - formParams.add("client_id", CLIENT_ID_1); - formParams.add("client_secret", "DhkFj5EI0qp6GsUKi55Vja+h+bsaKpBx"); - formParams.add("grant_type", "authorization_code"); - formParams.add("redirect_uri", "http://www.APP-5555555555555555.com/redirect/oauth"); - formParams.add("code", authCode.getId()); - Response response = orcidClientCredentialEndPointDelegator.obtainOauth2Token(null, formParams); - assertNotNull(response); - assertNotNull(response.getEntity()); - DefaultOAuth2AccessToken token = (DefaultOAuth2AccessToken) response.getEntity(); - assertNotNull(token); - assertTrue(!PojoUtil.isEmpty(token.getValue())); - assertNotNull(token.getRefreshToken()); - assertTrue(!PojoUtil.isEmpty(token.getRefreshToken().getValue())); - - // Generate the refresh token - MultivaluedMap refreshTokenformParams = new MultivaluedHashMap(); - refreshTokenformParams.add("client_id", CLIENT_ID_1); - refreshTokenformParams.add("client_secret", "DhkFj5EI0qp6GsUKi55Vja+h+bsaKpBx"); - refreshTokenformParams.add("grant_type", "refresh_token"); - refreshTokenformParams.add("redirect_uri", "http://www.APP-5555555555555555.com/redirect/oauth"); - refreshTokenformParams.add("refresh_token", token.getRefreshToken().getValue()); - String authorization = "bearer " + token.getValue(); - Response refreshTokenResponse = orcidClientCredentialEndPointDelegator.obtainOauth2Token(authorization, refreshTokenformParams); - assertNotNull(refreshTokenResponse); - assertNotNull(refreshTokenResponse.getEntity()); - DefaultOAuth2AccessToken refreshToken = (DefaultOAuth2AccessToken) refreshTokenResponse.getEntity(); - assertNotNull(refreshToken); - assertTrue(!PojoUtil.isEmpty(refreshToken.getValue())); - assertNotNull(refreshToken.getRefreshToken()); - assertTrue(!PojoUtil.isEmpty(refreshToken.getRefreshToken().getValue())); - - // Assert that both tokens expires at the same time - assertEquals(token.getExpiration(), refreshToken.getExpiration()); - - // Try to generate another one, and fail, because parent token was - // disabled - try { - orcidClientCredentialEndPointDelegator.obtainOauth2Token(authorization, refreshTokenformParams); - } catch (InvalidTokenException e) { - assertTrue(e.getMessage().contains("Parent token is disabled")); - } - } - - @Test - public void generateRefreshTokenThatExpireAfterParentTokenTest() { - // Generate the access token - SecurityContextTestUtils.setUpSecurityContextForClientOnly(CLIENT_ID_1, ScopePathType.ACTIVITIES_UPDATE, ScopePathType.READ_LIMITED); - OrcidOauth2AuthoriziationCodeDetail authCode = createAuthorizationCode("code-1", CLIENT_ID_1, "http://www.APP-5555555555555555.com/redirect/oauth", false, - "/activities/update"); - MultivaluedMap formParams = new MultivaluedHashMap(); - formParams.add("client_id", CLIENT_ID_1); - formParams.add("client_secret", "DhkFj5EI0qp6GsUKi55Vja+h+bsaKpBx"); - formParams.add("grant_type", "authorization_code"); - formParams.add("redirect_uri", "http://www.APP-5555555555555555.com/redirect/oauth"); - formParams.add("code", authCode.getId()); - Response response = orcidClientCredentialEndPointDelegator.obtainOauth2Token(null, formParams); - assertNotNull(response); - assertNotNull(response.getEntity()); - DefaultOAuth2AccessToken token = (DefaultOAuth2AccessToken) response.getEntity(); - assertNotNull(token); - assertTrue(!PojoUtil.isEmpty(token.getValue())); - assertNotNull(token.getRefreshToken()); - assertTrue(!PojoUtil.isEmpty(token.getRefreshToken().getValue())); - - // Generate the refresh token that expires after parent token - MultivaluedMap refreshTokenformParams = new MultivaluedHashMap(); - refreshTokenformParams.add("client_id", CLIENT_ID_1); - refreshTokenformParams.add("client_secret", "DhkFj5EI0qp6GsUKi55Vja+h+bsaKpBx"); - refreshTokenformParams.add("grant_type", "refresh_token"); - refreshTokenformParams.add("redirect_uri", "http://www.APP-5555555555555555.com/redirect/oauth"); - refreshTokenformParams.add("refresh_token", token.getRefreshToken().getValue()); - refreshTokenformParams.add("expires_in", String.valueOf(2 * 60 * 60)); - String authorization = "bearer " + token.getValue(); - try { - orcidClientCredentialEndPointDelegator.obtainOauth2Token(authorization, refreshTokenformParams); - } catch (IllegalArgumentException e) { - assertTrue(e.getMessage().contains("Token expiration can't be after")); - } - - // Try again with a valid expiration value - refreshTokenformParams = new MultivaluedHashMap(); - refreshTokenformParams.add("client_id", CLIENT_ID_1); - refreshTokenformParams.add("client_secret", "DhkFj5EI0qp6GsUKi55Vja+h+bsaKpBx"); - refreshTokenformParams.add("grant_type", "refresh_token"); - refreshTokenformParams.add("redirect_uri", "http://www.APP-5555555555555555.com/redirect/oauth"); - refreshTokenformParams.add("refresh_token", token.getRefreshToken().getValue()); - refreshTokenformParams.add("expires_in", String.valueOf(60 * 30)); - response = orcidClientCredentialEndPointDelegator.obtainOauth2Token(authorization, refreshTokenformParams); - assertNotNull(response); - assertNotNull(response.getEntity()); - DefaultOAuth2AccessToken refreshToken = (DefaultOAuth2AccessToken) response.getEntity(); - assertNotNull(refreshToken); - assertTrue(!PojoUtil.isEmpty(refreshToken.getValue())); - assertNotNull(refreshToken.getRefreshToken()); - assertTrue(!PojoUtil.isEmpty(refreshToken.getRefreshToken().getValue())); - - assertTrue(token.getExpiration().getTime() > refreshToken.getExpiration().getTime()); - } - - @Test - public void obtainOauth2TokenSetCacheTest() { - // Enable cache - orcidClientCredentialEndPointDelegator.setTokenCacheEnabled(true); - SecurityContextTestUtils.setUpSecurityContextForClientOnly(CLIENT_ID_1, ScopePathType.ACTIVITIES_UPDATE, ScopePathType.READ_LIMITED); - OrcidOauth2AuthoriziationCodeDetail authCode = createAuthorizationCode("code-1", CLIENT_ID_1, "http://www.APP-5555555555555555.com/redirect/oauth", true, - "/activities/update"); - MultivaluedMap formParams = new MultivaluedHashMap(); - formParams.add("client_id", CLIENT_ID_1); - formParams.add("client_secret", "DhkFj5EI0qp6GsUKi55Vja+h+bsaKpBx"); - formParams.add("grant_type", "authorization_code"); - formParams.add("redirect_uri", "http://www.APP-5555555555555555.com/redirect/oauth"); - formParams.add("code", authCode.getId()); - Response response = orcidClientCredentialEndPointDelegator.obtainOauth2Token(null, formParams); - assertNotNull(response); - assertNotNull(response.getEntity()); - DefaultOAuth2AccessToken token = (DefaultOAuth2AccessToken) response.getEntity(); - assertNotNull(token); - assertTrue(!PojoUtil.isEmpty(token.getValue())); - - String tokenValue = token.getValue(); - - - Map tokenData = new HashMap(); - tokenData.put(OrcidOauth2Constants.ACCESS_TOKEN, tokenValue); - tokenData.put(OrcidOauth2Constants.TOKEN_EXPIRATION_TIME, String.valueOf(token.getExpiration().getTime())); - StringBuilder sb = new StringBuilder(); - token.getScope().forEach(x -> {sb.append(x); sb.append(' ');}); - tokenData.put(OrcidOauth2Constants.SCOPE_PARAM, sb.toString()); - tokenData.put(OrcidOauth2Constants.ORCID, (String) token.getAdditionalInformation().get(OrcidOauth2Constants.ORCID)); - tokenData.put(OrcidOauth2Constants.CLIENT_ID, CLIENT_ID_1); - tokenData.put(OrcidOauth2Constants.RESOURCE_IDS, OrcidOauth2Constants.ORCID); - tokenData.put(OrcidOauth2Constants.APPROVED, Boolean.TRUE.toString()); - - String tokenDataString = JsonUtils.convertToJsonString(tokenData); - - verify(redisClientMock, times(1)).set(Mockito.eq(tokenValue), Mockito.eq(tokenDataString)); - } - - @Test - public void obtainOauth2TokenSkipCacheTest() { - // Ensure cache is disabled - orcidClientCredentialEndPointDelegator.setTokenCacheEnabled(false); - - SecurityContextTestUtils.setUpSecurityContextForClientOnly(CLIENT_ID_1, ScopePathType.ACTIVITIES_UPDATE, ScopePathType.READ_LIMITED); - OrcidOauth2AuthoriziationCodeDetail authCode = createAuthorizationCode("code-1", CLIENT_ID_1, "http://www.APP-5555555555555555.com/redirect/oauth", true, - "/activities/update"); - MultivaluedMap formParams = new MultivaluedHashMap(); - formParams.add("client_id", CLIENT_ID_1); - formParams.add("client_secret", "DhkFj5EI0qp6GsUKi55Vja+h+bsaKpBx"); - formParams.add("grant_type", "authorization_code"); - formParams.add("redirect_uri", "http://www.APP-5555555555555555.com/redirect/oauth"); - formParams.add("code", authCode.getId()); - Response response = orcidClientCredentialEndPointDelegator.obtainOauth2Token(null, formParams); - assertNotNull(response); - assertNotNull(response.getEntity()); - DefaultOAuth2AccessToken token = (DefaultOAuth2AccessToken) response.getEntity(); - assertNotNull(token); - assertTrue(!PojoUtil.isEmpty(token.getValue())); - - verify(redisClientMock, never()).set(Mockito.any(), Mockito.any()); - } -} \ No newline at end of file diff --git a/orcid-core/src/test/java/org/orcid/core/utils/SecurityContextTestUtils.java b/orcid-core/src/main/java/org/orcid/core/utils/SecurityContextTestUtils.java similarity index 83% rename from orcid-core/src/test/java/org/orcid/core/utils/SecurityContextTestUtils.java rename to orcid-core/src/main/java/org/orcid/core/utils/SecurityContextTestUtils.java index ef9150bf326..aa5dcee2078 100644 --- a/orcid-core/src/test/java/org/orcid/core/utils/SecurityContextTestUtils.java +++ b/orcid-core/src/main/java/org/orcid/core/utils/SecurityContextTestUtils.java @@ -3,15 +3,12 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import java.io.Serializable; import java.util.*; import org.orcid.core.oauth.OrcidBearerTokenAuthentication; import org.orcid.jaxb.model.message.ScopePathType; -import org.orcid.persistence.jpa.entities.ProfileEntity; import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; @@ -86,4 +83,20 @@ static public void setUpSecurityContextForAnonymous() { securityContext.setAuthentication(anonToken); SecurityContextHolder.setContext(securityContext); } + + static public void setupSecurityContextForWebUser(String userId, String email) { + UserDetails details = new User(userId, email, List.of()); + UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(userId, "password"); + auth.setDetails(details); + SecurityContextImpl securityContext = new SecurityContextImpl(); + securityContext.setAuthentication(auth); + SecurityContextHolder.setContext(securityContext); + } + + static public void setUpSecurityContextForGroupIdClientOnly() { + Set scopes = new HashSet(); + scopes.add(ScopePathType.GROUP_ID_RECORD_READ.value()); + scopes.add(ScopePathType.GROUP_ID_RECORD_UPDATE.value()); + setUpSecurityContextForClientOnly("APP-5555555555555555", scopes); + } } \ No newline at end of file diff --git a/orcid-core/src/main/resources/orcid-core-context.xml b/orcid-core/src/main/resources/orcid-core-context.xml index 0cf31a587f4..a4f9b1413d3 100644 --- a/orcid-core/src/main/resources/orcid-core-context.xml +++ b/orcid-core/src/main/resources/orcid-core-context.xml @@ -1108,12 +1108,7 @@ - - - - - - + diff --git a/orcid-core/src/test/java/org/orcid/core/oauth/service/NamespacedRandomCodeGeneratorTest.java b/orcid-core/src/test/java/org/orcid/core/oauth/service/NamespacedRandomCodeGeneratorTest.java deleted file mode 100644 index 7ed6c44d3a6..00000000000 --- a/orcid-core/src/test/java/org/orcid/core/oauth/service/NamespacedRandomCodeGeneratorTest.java +++ /dev/null @@ -1,157 +0,0 @@ -package org.orcid.core.oauth.service; - -import static org.junit.Assert.*; - -import java.util.HashSet; -import java.util.Set; - -import org.junit.Test; - -import junit.framework.Assert; - -public class NamespacedRandomCodeGeneratorTest { - - private static final String base62 = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; - - @Test - public void test(){ - //check 1 node uses all chars - NamespacedRandomCodeGenerator g = new NamespacedRandomCodeGenerator(1,1); - Set unique = new HashSet(); - for(char c : base62.toCharArray()) { - unique.add(c); - } - for (int i=0;i<100000;i++){ - String code = g.nextRandomCode(); - if (!base62.contains(""+code.charAt(0))){ - fail("Char outside base 62"); - } - unique.remove(code.charAt(0)); - } - System.out.println(unique); - Assert.assertEquals(0,unique.size()); - - //check two nodes are 0-30 and 31-61 - NamespacedRandomCodeGenerator g1_2 = new NamespacedRandomCodeGenerator(1,2); - NamespacedRandomCodeGenerator g2_2 = new NamespacedRandomCodeGenerator(2,2); - Set unique1_2 = new HashSet(); - Set unique2_2 = new HashSet(); - int j = 0; - for(char c : base62.toCharArray()) { - if (j<31) - unique1_2.add(c); - else - unique2_2.add(c); - j++; - } - for (int i=0;i<100000;i++){ - String code = g1_2.nextRandomCode(); - if (!base62.contains(""+code.charAt(0))){ - fail("Char outside base 62"); - } - unique1_2.remove(code.charAt(0)); - unique2_2.remove(code.charAt(0)); - } - Assert.assertEquals(0,unique1_2.size()); //only removed from first half - Assert.assertEquals(31,unique2_2.size());//all still there - - //do it again for the second generator - j = 0; - for(char c : base62.toCharArray()) { - if (j<31) - unique1_2.add(c); - else - unique2_2.add(c); - j++; - } - for (int i=0;i<100000;i++){ - String code = g2_2.nextRandomCode(); - if (!base62.contains(""+code.charAt(0))){ - fail("Char outside base 62"); - } - unique1_2.remove(code.charAt(0)); - unique2_2.remove(code.charAt(0)); - } - Assert.assertEquals(31,unique1_2.size()); //all still there - Assert.assertEquals(0,unique2_2.size());//only removed from second half - - //--- now three nodes - //check two nodes are 0-30 and 31-61 - NamespacedRandomCodeGenerator g1_3 = new NamespacedRandomCodeGenerator(1,3); - NamespacedRandomCodeGenerator g2_3 = new NamespacedRandomCodeGenerator(2,3); - NamespacedRandomCodeGenerator g3_3 = new NamespacedRandomCodeGenerator(3,3); - Set unique1_3 = new HashSet(); - Set unique2_3 = new HashSet(); - Set unique3_3= new HashSet(); - j = 0; - for(char c : base62.toCharArray()) { - if (j<20) - unique1_3.add(c); - else if (j<40) - unique2_3.add(c); - else - unique3_3.add(c); // has the extra two chars! - j++; - } - for (int i=0;i<100000;i++){ - String code = g1_3.nextRandomCode(); - if (!base62.contains(""+code.charAt(0))){ - fail("Char outside base 62"); - } - unique1_3.remove(code.charAt(0)); - unique2_3.remove(code.charAt(0)); - unique3_3.remove(code.charAt(0)); - } - Assert.assertEquals(0,unique1_3.size()); //only removed from first third - Assert.assertEquals(20,unique2_3.size());//all still there - Assert.assertEquals(22,unique3_3.size());//all still there - - //node 2 of 3 - j = 0; - for(char c : base62.toCharArray()) { - if (j<20) - unique1_3.add(c); - else if (j<40) - unique2_3.add(c); - else - unique3_3.add(c); // has the extra two chars! - j++; - } - for (int i=0;i<100000;i++){ - String code = g2_3.nextRandomCode(); - if (!base62.contains(""+code.charAt(0))){ - fail("Char outside base 62"); - } - unique1_3.remove(code.charAt(0)); - unique2_3.remove(code.charAt(0)); - unique3_3.remove(code.charAt(0)); - } - Assert.assertEquals(20,unique1_3.size()); - Assert.assertEquals(0,unique2_3.size()); - Assert.assertEquals(22,unique3_3.size()); - - //node 3 of 3 - j = 0; - for(char c : base62.toCharArray()) { - if (j<20) - unique1_3.add(c); - else if (j<40) - unique2_3.add(c); - else - unique3_3.add(c); // has the extra two chars! - j++; - } - for (int i=0;i<100000;i++){ - String code = g3_3.nextRandomCode(); - if (!base62.contains(""+code.charAt(0))){ - fail("Char outside base 62"); - } - unique1_3.remove(code.charAt(0)); - unique2_3.remove(code.charAt(0)); - unique3_3.remove(code.charAt(0)); - } - Assert.assertEquals(20,unique1_3.size()); - Assert.assertEquals(20,unique2_3.size()); - Assert.assertEquals(0,unique3_3.size()); - } -} diff --git a/orcid-pub-web/src/test/java/org/orcid/api/filters/ApiRateLimitFilterTest.java b/orcid-pub-web/src/test/java/org/orcid/api/filters/ApiRateLimitFilterTest.java index 25d1849dba8..f3ab254adb5 100644 --- a/orcid-pub-web/src/test/java/org/orcid/api/filters/ApiRateLimitFilterTest.java +++ b/orcid-pub-web/src/test/java/org/orcid/api/filters/ApiRateLimitFilterTest.java @@ -7,7 +7,6 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.orcid.core.api.rate_limit.PapiRateLimitRedisClient; -import org.orcid.core.oauth.service.OrcidTokenStore; import org.orcid.test.OrcidJUnit4ClassRunner; import org.orcid.test.TargetProxyHelper; import org.springframework.mock.web.MockHttpServletRequest; @@ -33,9 +32,6 @@ public class ApiRateLimitFilterTest { @Mock private FilterChain filterChainMock; - @Mock - private OrcidTokenStore orcidTokenStoreMock; - @Mock private PapiRateLimitRedisClient papiRateLimitRedisMock; @@ -47,13 +43,11 @@ public class ApiRateLimitFilterTest { public void doFilterInternal_rateLimitingDisabledTest() throws ServletException, IOException { MockitoAnnotations.initMocks(this); TargetProxyHelper.injectIntoProxy(apiRateLimitFilter, "enableRateLimiting", false); - TargetProxyHelper.injectIntoProxy(apiRateLimitFilter, "orcidTokenStore", orcidTokenStoreMock); TargetProxyHelper.injectIntoProxy(apiRateLimitFilter, "papiRedisClient", papiRateLimitRedisMock); apiRateLimitFilter.doFilterInternal(httpServletRequestMock, httpServletResponseMock, filterChainMock); verify(filterChainMock, times(1)).doFilter(eq(httpServletRequestMock), eq(httpServletResponseMock)); - verify(orcidTokenStoreMock, never()).readClientId(anyString()); verify(papiRateLimitRedisMock, never()).getDailyLimitsForClient(anyString(), any()); verify(papiRateLimitRedisMock, never()).setTodayLimitsForClient(anyString(), any()); @@ -65,7 +59,6 @@ public void doFilterInternal_annonymousRequest_newEntry_X_FORWARDED_FOR_header_T String ip = "127.0.0.2"; TargetProxyHelper.injectIntoProxy(apiRateLimitFilter, "enableRateLimiting", true); - TargetProxyHelper.injectIntoProxy(apiRateLimitFilter, "orcidTokenStore", orcidTokenStoreMock); TargetProxyHelper.injectIntoProxy(apiRateLimitFilter, "papiRedisClient", papiRateLimitRedisMock); when(papiRateLimitRedisMock.getTodayDailyLimitsForClient(eq(ip))).thenReturn(null); @@ -73,7 +66,6 @@ public void doFilterInternal_annonymousRequest_newEntry_X_FORWARDED_FOR_header_T apiRateLimitFilter.doFilterInternal(httpServletRequestMock, httpServletResponseMock, filterChainMock); - verify(orcidTokenStoreMock, never()).readClientId(anyString()); verify(papiRateLimitRedisMock, times(1)).setTodayLimitsForClient(anyString(), any(JSONObject.class)); } @@ -83,7 +75,6 @@ public void doFilterInternal_annonymousRequest_newEntry_X_REAL_IP_header_Test() String ip = "127.0.0.2"; TargetProxyHelper.injectIntoProxy(apiRateLimitFilter, "enableRateLimiting", true); - TargetProxyHelper.injectIntoProxy(apiRateLimitFilter, "orcidTokenStore", orcidTokenStoreMock); TargetProxyHelper.injectIntoProxy(apiRateLimitFilter, "papiRedisClient", papiRateLimitRedisMock); when(papiRateLimitRedisMock.getTodayDailyLimitsForClient(eq(ip))).thenReturn(null); @@ -91,8 +82,6 @@ public void doFilterInternal_annonymousRequest_newEntry_X_REAL_IP_header_Test() apiRateLimitFilter.doFilterInternal(httpServletRequestMock, httpServletResponseMock, filterChainMock); - verify(orcidTokenStoreMock, never()).readClientId(anyString()); - verify(papiRateLimitRedisMock, never()).getDailyLimitsForClient(anyString(), any()); verify(papiRateLimitRedisMock, times(1)).setTodayLimitsForClient(anyString(), any(JSONObject.class)); } @@ -103,7 +92,6 @@ public void doFilterInternal_annonymousRequest_newEntry_whitelisted_IP_Test() th String ip = "127.0.0.1"; TargetProxyHelper.injectIntoProxy(apiRateLimitFilter, "enableRateLimiting", true); - TargetProxyHelper.injectIntoProxy(apiRateLimitFilter, "orcidTokenStore", orcidTokenStoreMock); TargetProxyHelper.injectIntoProxy(apiRateLimitFilter, "papiRedisClient", papiRateLimitRedisMock); when(papiRateLimitRedisMock.getTodayDailyLimitsForClient(eq(ip))).thenReturn(null); @@ -111,7 +99,6 @@ public void doFilterInternal_annonymousRequest_newEntry_whitelisted_IP_Test() th apiRateLimitFilter.doFilterInternal(httpServletRequestMock, httpServletResponseMock, filterChainMock); - verify(orcidTokenStoreMock, never()).readClientId(anyString()); verify(papiRateLimitRedisMock, never()).setTodayLimitsForClient(eq(ip), any()); } @@ -128,7 +115,6 @@ public void doFilterInternal_annonymousRequest_existingEntryTest() throws Servle dailyLimitsObj.put(PapiRateLimitRedisClient.KEY_LAST_MODIFIED, System.currentTimeMillis()); TargetProxyHelper.injectIntoProxy(apiRateLimitFilter, "enableRateLimiting", true); - TargetProxyHelper.injectIntoProxy(apiRateLimitFilter, "orcidTokenStore", orcidTokenStoreMock); TargetProxyHelper.injectIntoProxy(apiRateLimitFilter, "papiRedisClient", papiRateLimitRedisMock); when(papiRateLimitRedisMock.getTodayDailyLimitsForClient(eq(ip))).thenReturn(dailyLimitsObj); @@ -136,9 +122,7 @@ public void doFilterInternal_annonymousRequest_existingEntryTest() throws Servle apiRateLimitFilter.doFilterInternal(httpServletRequestMock, httpServletResponseMock, filterChainMock); - verify(orcidTokenStoreMock, never()).readClientId(anyString()); verify(papiRateLimitRedisMock, times(1)).setTodayLimitsForClient(anyString(), any(JSONObject.class)); - } @Test @@ -148,9 +132,7 @@ public void doFilterInternal_clientRequest_newEntryTest() throws ServletExceptio String clientId = "clientId1"; httpServletRequestMock.addHeader("Authorization", "TEST_TOKEN"); - when(orcidTokenStoreMock.readClientId(eq("TEST_TOKEN"))).thenReturn(clientId); TargetProxyHelper.injectIntoProxy(apiRateLimitFilter, "enableRateLimiting", true); - TargetProxyHelper.injectIntoProxy(apiRateLimitFilter, "orcidTokenStore", orcidTokenStoreMock); TargetProxyHelper.injectIntoProxy(apiRateLimitFilter, "papiRedisClient", papiRateLimitRedisMock); when(papiRateLimitRedisMock.getTodayDailyLimitsForClient(eq(ip))).thenReturn(null); @@ -176,9 +158,7 @@ public void doFilterInternal_clientRequest_existingEntryTest() throws ServletExc dailyLimitsObj.put(PapiRateLimitRedisClient.KEY_LAST_MODIFIED, System.currentTimeMillis()); httpServletRequestMock.addHeader("Authorization", "TEST_TOKEN"); - when(orcidTokenStoreMock.readClientId(eq("TEST_TOKEN"))).thenReturn(clientId); TargetProxyHelper.injectIntoProxy(apiRateLimitFilter, "enableRateLimiting", true); - TargetProxyHelper.injectIntoProxy(apiRateLimitFilter, "orcidTokenStore", orcidTokenStoreMock); TargetProxyHelper.injectIntoProxy(apiRateLimitFilter, "papiRedisClient", papiRateLimitRedisMock); when(papiRateLimitRedisMock.getDailyLimitsForClient(eq(clientId), any())).thenReturn(dailyLimitsObj); @@ -203,7 +183,6 @@ public void doFilterInternal_checkLimitReachedTest() throws ServletException, IO dailyLimitsObj.put(PapiRateLimitRedisClient.KEY_LAST_MODIFIED, System.currentTimeMillis()); TargetProxyHelper.injectIntoProxy(apiRateLimitFilter, "enableRateLimiting", true); - TargetProxyHelper.injectIntoProxy(apiRateLimitFilter, "orcidTokenStore", orcidTokenStoreMock); TargetProxyHelper.injectIntoProxy(apiRateLimitFilter, "papiRedisClient", papiRateLimitRedisMock); when(papiRateLimitRedisMock.getTodayDailyLimitsForClient(eq(ip))).thenReturn(dailyLimitsObj); @@ -224,7 +203,6 @@ public void doFilterInternal_annonymousRequest_whitelisted_cidr_IP_Test() throws String ip_in_cidr = "10.0.0.0"; TargetProxyHelper.injectIntoProxy(apiRateLimitFilter, "enableRateLimiting", true); - TargetProxyHelper.injectIntoProxy(apiRateLimitFilter, "orcidTokenStore", orcidTokenStoreMock); TargetProxyHelper.injectIntoProxy(apiRateLimitFilter, "papiRedisClient", papiRateLimitRedisMock); when(papiRateLimitRedisMock.getTodayDailyLimitsForClient(eq(ip_in_cidr))).thenReturn(null); @@ -232,7 +210,6 @@ public void doFilterInternal_annonymousRequest_whitelisted_cidr_IP_Test() throws apiRateLimitFilter.doFilterInternal(httpServletRequestMock, httpServletResponseMock, filterChainMock); - verify(orcidTokenStoreMock, never()).readClientId(anyString()); verify(papiRateLimitRedisMock, never()).setTodayLimitsForClient(eq(ip_in_cidr), any()); } @@ -242,7 +219,6 @@ public void doFilterInternal_annonymousRequest_not_whitelisted_cidr_IP_Test() th String ip_not_cidr = "20.0.0.0"; TargetProxyHelper.injectIntoProxy(apiRateLimitFilter, "enableRateLimiting", true); - TargetProxyHelper.injectIntoProxy(apiRateLimitFilter, "orcidTokenStore", orcidTokenStoreMock); TargetProxyHelper.injectIntoProxy(apiRateLimitFilter, "papiRedisClient", papiRateLimitRedisMock); when(papiRateLimitRedisMock.getTodayDailyLimitsForClient(eq(ip_not_cidr))).thenReturn(null); @@ -250,7 +226,6 @@ public void doFilterInternal_annonymousRequest_not_whitelisted_cidr_IP_Test() th apiRateLimitFilter.doFilterInternal(httpServletRequestMock, httpServletResponseMock, filterChainMock); - verify(orcidTokenStoreMock, never()).readClientId(anyString()); verify(papiRateLimitRedisMock, times(1)).setTodayLimitsForClient(eq(ip_not_cidr), any(JSONObject.class)); } } diff --git a/orcid-web/src/main/resources/orcid-core-context-spam.xml b/orcid-web/src/main/resources/orcid-core-context-spam.xml index 1be336d1966..0455d459698 100644 --- a/orcid-web/src/main/resources/orcid-core-context-spam.xml +++ b/orcid-web/src/main/resources/orcid-core-context-spam.xml @@ -1068,12 +1068,7 @@ - - - - - - + diff --git a/orcid-web/src/test/java/org/orcid/frontend/email/RecordEmailSenderTest.java b/orcid-web/src/test/java/org/orcid/frontend/email/RecordEmailSenderTest.java index 8a8fc8a7c3e..71fd7f37513 100644 --- a/orcid-web/src/test/java/org/orcid/frontend/email/RecordEmailSenderTest.java +++ b/orcid-web/src/test/java/org/orcid/frontend/email/RecordEmailSenderTest.java @@ -30,7 +30,6 @@ import org.orcid.core.manager.v3.EmailManager; import org.orcid.core.manager.v3.RecordNameManager; import org.orcid.core.manager.v3.SourceManager; -import org.orcid.core.oauth.OrcidOauth2TokenDetailService; import org.orcid.jaxb.model.common.AvailableLocales; import org.orcid.jaxb.model.v3.release.record.Email; import org.orcid.persistence.dao.GenericDao; @@ -39,7 +38,6 @@ import org.orcid.persistence.dao.ProfileEventDao; import org.orcid.persistence.jpa.entities.EmailEventEntity; import org.orcid.persistence.jpa.entities.ProfileEntity; -import org.orcid.persistence.jpa.entities.ProfileEventEntity; import org.orcid.pojo.EmailListChange; import org.orcid.test.OrcidJUnit4ClassRunner; import org.orcid.utils.email.MailGunManager; @@ -57,39 +55,15 @@ public class RecordEmailSenderTest { @Mock private ProfileEventDao mockProfileEventDao; - @Mock - private SourceManager sourceManager; - - @Mock - private NotificationDao mockNotificationDao; - @Mock private MailGunManager mockMailGunManager; - @Mock - private OrcidOauth2TokenDetailService mockOrcidOauth2TokenDetailService; - @Mock private ProfileEntityCacheManager mockProfileEntityCacheManager; @Mock private EmailManager mockEmailManager; - @Mock - private ProfileDao mockProfileDao; - - @Mock - private ProfileDao mockProfileDaoReadOnly; - - @Mock - private JpaJaxbNotificationAdapter mockNotificationAdapter; - - @Mock - public GenericDao mockEmailEventDao; - - @Mock - public EmailFrequencyManager mockEmailFrequencyManager; - @Mock public RecordNameManager mockRecordNameManager; diff --git a/orcid-web/src/test/java/org/orcid/frontend/oauth2/RevokeControllerTest.java b/orcid-web/src/test/java/org/orcid/frontend/oauth2/RevokeControllerTest.java index 6571af97cf1..f2cd808a240 100644 --- a/orcid-web/src/test/java/org/orcid/frontend/oauth2/RevokeControllerTest.java +++ b/orcid-web/src/test/java/org/orcid/frontend/oauth2/RevokeControllerTest.java @@ -1,29 +1,24 @@ package org.orcid.frontend.oauth2; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.times; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.core.Response; -import org.junit.After; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; +import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.orcid.core.oauth.OrcidOauth2TokenDetailService; -import org.orcid.core.togglz.Features; +import org.orcid.core.oauth.authorizationServer.AuthorizationServerUtil; import org.orcid.core.utils.SecurityContextTestUtils; -import org.orcid.persistence.jpa.entities.OrcidOauth2TokenDetail; -import org.togglz.junit.TogglzRule; - -import java.io.IOException; -import java.net.URISyntaxException; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.context.SecurityContextHolder; public class RevokeControllerTest { @@ -31,126 +26,82 @@ public class RevokeControllerTest { private HttpServletRequest request; @Mock - private OrcidOauth2TokenDetailService mockOrcidOauth2TokenDetailService; - - @Resource - private OrcidOauth2TokenDetailService orcidOauth2TokenDetailService; + private AuthorizationServerUtil authorizationServerUtil; - @Rule - public TogglzRule togglzRule = TogglzRule.allDisabled(Features.class); - - private RevokeController revokeController = new RevokeController(); + @InjectMocks + private RevokeController revokeController; @Before public void before() { MockitoAnnotations.initMocks(this); - revokeController.setOrcidOauth2TokenDetailService(mockOrcidOauth2TokenDetailService); - when(request.getParameter("token")).thenReturn("token-value"); - OrcidOauth2TokenDetail token = new OrcidOauth2TokenDetail(); - token.setClientDetailsId("client-id"); - token.setTokenValue("token-value"); - token.setRefreshTokenValue("refresh-token-value"); - token.setTokenDisabled(false); - when(mockOrcidOauth2TokenDetailService.findIgnoringDisabledByTokenValue("token-value")).thenReturn(token); - when(mockOrcidOauth2TokenDetailService.findByRefreshTokenValue("refresh-token-value")).thenReturn(token); - SecurityContextTestUtils.setUpSecurityContextForClientOnly("client-id"); } - - @After - public void after() { - revokeController.setOrcidOauth2TokenDetailService(orcidOauth2TokenDetailService); + + @Test + public void testRevokeWithClientIdAndSecret() throws Exception { + String clientId = "APP-1234567890123456"; + String clientSecret = "some-secret"; + String token = "some-token"; + + SecurityContextTestUtils.setUpSecurityContextForClientOnly(clientId); + + when(request.getParameter("token")).thenReturn(token); + when(request.getParameter("client_secret")).thenReturn(clientSecret); + when(request.getHeader("Authorization")).thenReturn(null); + + Response mockResponse = mock(Response.class); + when(mockResponse.getStatus()).thenReturn(200); + when(mockResponse.getEntity()).thenReturn("Success"); + + when(authorizationServerUtil.forwardTokenRevocationRequest(clientId, clientSecret, token)).thenReturn(mockResponse); + + ResponseEntity result = revokeController.revoke(request); + + assertNotNull(result); + assertEquals(200, result.getStatusCodeValue()); + assertEquals("Success", result.getBody()); + + verify(authorizationServerUtil).forwardTokenRevocationRequest(clientId, clientSecret, token); } - + @Test - public void noTokenTest() { + public void testRevokeWithBasicAuth() throws Exception { + String authorization = "Basic Y2xpZW50LWlkOnNlY3JldA=="; + String token = "some-token"; + + when(request.getParameter("token")).thenReturn(token); + when(request.getHeader("Authorization")).thenReturn(authorization); + + Response mockResponse = mock(Response.class); + when(mockResponse.getStatus()).thenReturn(200); + when(mockResponse.getEntity()).thenReturn("Success"); + + when(authorizationServerUtil.forwardTokenRevocationRequest(authorization, token)).thenReturn(mockResponse); + + ResponseEntity result = revokeController.revoke(request); + + assertNotNull(result); + assertEquals(200, result.getStatusCodeValue()); + assertEquals("Success", result.getBody()); + + verify(authorizationServerUtil).forwardTokenRevocationRequest(authorization, token); + } + + @Test + public void testRevokeNoToken() throws Exception { when(request.getParameter("token")).thenReturn(null); try { revokeController.revoke(request); - fail(); + fail("Should have thrown IllegalArgumentException"); } catch (IllegalArgumentException e) { assertEquals("Please provide the token to be param", e.getMessage()); - } catch (Exception e) { - fail(); } when(request.getParameter("token")).thenReturn(""); try { revokeController.revoke(request); - fail(); + fail("Should have thrown IllegalArgumentException"); } catch (IllegalArgumentException e) { assertEquals("Please provide the token to be param", e.getMessage()); - } catch (Exception e) { - fail(); - } - - when(request.getParameter("token")).thenReturn("token-value"); - try { - revokeController.revoke(request); - } catch (Exception e) { - fail(); } } - - @Test - public void notOwnerTest() throws IOException, URISyntaxException, InterruptedException { - SecurityContextTestUtils.setUpSecurityContextForClientOnly("other-client-id"); - - revokeController.revoke(request); - - verify(mockOrcidOauth2TokenDetailService, times(1)).findIgnoringDisabledByTokenValue("token-value"); - verify(mockOrcidOauth2TokenDetailService, times(0)).findByRefreshTokenValue(anyString()); - verify(mockOrcidOauth2TokenDetailService, times(0)).revokeAccessToken(anyString()); - } - - @Test - public void tokenAlreadyDisabledOrNonExistingTest() throws IOException, URISyntaxException, InterruptedException { - when(request.getParameter("token")).thenReturn("disabled-or-unexisting"); - - revokeController.revoke(request); - - verify(mockOrcidOauth2TokenDetailService, times(1)).findIgnoringDisabledByTokenValue("disabled-or-unexisting"); - verify(mockOrcidOauth2TokenDetailService, times(1)).findByRefreshTokenValue("disabled-or-unexisting"); - verify(mockOrcidOauth2TokenDetailService, times(0)).revokeAccessToken(anyString()); - } - - @Test - public void disableByTokenTest() throws IOException, URISyntaxException, InterruptedException { - when(request.getParameter("token")).thenReturn("token-value"); - - revokeController.revoke(request); - - verify(mockOrcidOauth2TokenDetailService, times(1)).findIgnoringDisabledByTokenValue("token-value"); - verify(mockOrcidOauth2TokenDetailService, times(0)).findByRefreshTokenValue(anyString()); - verify(mockOrcidOauth2TokenDetailService, times(1)).revokeAccessToken("token-value"); - } - - @Test - public void disableByRefreshTokenTest() throws IOException, URISyntaxException, InterruptedException { - when(request.getParameter("token")).thenReturn("refresh-token-value"); - - revokeController.revoke(request); - - verify(mockOrcidOauth2TokenDetailService, times(1)).findIgnoringDisabledByTokenValue("refresh-token-value"); - verify(mockOrcidOauth2TokenDetailService, times(1)).findByRefreshTokenValue("refresh-token-value"); - verify(mockOrcidOauth2TokenDetailService, times(1)).revokeAccessToken("token-value"); - } - - @Test - public void refreshTokenAlreadyDisabledTest() throws IOException, URISyntaxException, InterruptedException { - OrcidOauth2TokenDetail token = new OrcidOauth2TokenDetail(); - token.setClientDetailsId("client-id"); - token.setTokenValue("token-value"); - token.setRefreshTokenValue("refresh-token-value"); - token.setTokenDisabled(true); - when(mockOrcidOauth2TokenDetailService.findIgnoringDisabledByTokenValue("token-value")).thenReturn(token); - when(mockOrcidOauth2TokenDetailService.findByRefreshTokenValue("refresh-token-value")).thenReturn(token); - when(request.getParameter("token")).thenReturn("refresh-token-value"); - - revokeController.revoke(request); - - verify(mockOrcidOauth2TokenDetailService, times(1)).findIgnoringDisabledByTokenValue("refresh-token-value"); - verify(mockOrcidOauth2TokenDetailService, times(1)).findByRefreshTokenValue("refresh-token-value"); - // It should not call the disable function since it is already disabled - verify(mockOrcidOauth2TokenDetailService, times(0)).revokeAccessToken("refresh-token-value"); - } } diff --git a/orcid-web/src/test/java/org/orcid/frontend/web/controllers/OauthGenericCallsControllerTest.java b/orcid-web/src/test/java/org/orcid/frontend/web/controllers/OauthGenericCallsControllerTest.java deleted file mode 100644 index 92cca1481d8..00000000000 --- a/orcid-web/src/test/java/org/orcid/frontend/web/controllers/OauthGenericCallsControllerTest.java +++ /dev/null @@ -1,73 +0,0 @@ -package org.orcid.frontend.web.controllers; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.when; - -import javax.ws.rs.core.Response; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.orcid.api.common.oauth.OrcidClientCredentialEndPointDelegator; -import org.orcid.core.exception.LockedException; -import org.orcid.core.oauth.OAuthError; -import org.orcid.core.togglz.Features; -import org.springframework.http.ResponseEntity; -import org.springframework.mock.web.MockHttpServletRequest; -import org.togglz.junit.TogglzRule; - -import java.io.IOException; -import java.net.URISyntaxException; - -public class OauthGenericCallsControllerTest { - - @Mock - private OrcidClientCredentialEndPointDelegator orcidClientCredentialEndPointDelegator; - - @InjectMocks - private OauthGenericCallsController controller; - - @Rule - public TogglzRule togglzRule = TogglzRule.allDisabled(Features.class); - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - } - - @Test - public void testObtainOauth2TokenPost() throws IOException, URISyntaxException, InterruptedException { - when(orcidClientCredentialEndPointDelegator.obtainOauth2Token(isNull(), any())).thenReturn( - Response.ok("some-success-entity").build()); - MockHttpServletRequest mockHttpServletRequest = new MockHttpServletRequest(); - mockHttpServletRequest.addParameter("grant_type", "client_credentials"); - ResponseEntity responseEntity = controller.obtainOauth2TokenPost(mockHttpServletRequest); - assertNotNull(responseEntity); - assertNotNull(responseEntity.getBody()); - assertEquals("some-success-entity", responseEntity.getBody()); - } - - @Test - public void testObtainOauth2TokenPostLockedClient() throws IOException, URISyntaxException, InterruptedException { - when(orcidClientCredentialEndPointDelegator.obtainOauth2Token(isNull(), any())).thenThrow( - new LockedException("Client is locked")); - MockHttpServletRequest mockHttpServletRequest = new MockHttpServletRequest(); - mockHttpServletRequest.addParameter("grant_type", "client_credentials"); - ResponseEntity responseEntity = controller.obtainOauth2TokenPost(mockHttpServletRequest); - assertNotNull(responseEntity); - assertNotNull(responseEntity.getBody()); - assertTrue(responseEntity.getBody() instanceof OAuthError); - - OAuthError error = (OAuthError) responseEntity.getBody(); - assertEquals(OAuthError.UNAUTHORIZED_CLIENT, error.getError()); - assertEquals("Client is locked", error.getErrorDescription()); - } - -} diff --git a/orcid-web/src/test/java/org/orcid/frontend/web/controllers/OauthRegistrationControllerTest.java b/orcid-web/src/test/java/org/orcid/frontend/web/controllers/OauthRegistrationControllerTest.java deleted file mode 100644 index d6f9a7e3fef..00000000000 --- a/orcid-web/src/test/java/org/orcid/frontend/web/controllers/OauthRegistrationControllerTest.java +++ /dev/null @@ -1,135 +0,0 @@ -package org.orcid.frontend.web.controllers; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.io.UnsupportedEncodingException; -import java.security.Principal; -import java.util.Arrays; -import java.util.List; -import java.util.Locale; - -import javax.annotation.Resource; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; - -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Matchers; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; -import org.orcid.core.oauth.OrcidOAuth2Authentication; -import org.orcid.core.oauth.service.OrcidAuthorizationEndpoint; -import org.orcid.frontend.util.RequestInfoFormLocalCache; -import org.orcid.jaxb.model.message.CreationMethod; -import org.orcid.pojo.ajaxForm.Checkbox; -import org.orcid.pojo.ajaxForm.OauthRegistrationForm; -import org.orcid.pojo.ajaxForm.Registration; -import org.orcid.pojo.ajaxForm.RequestInfoForm; -import org.orcid.pojo.ajaxForm.Text; -import org.orcid.test.DBUnitTest; -import org.orcid.test.OrcidJUnit4ClassRunner; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.core.Authentication; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.web.WebAppConfiguration; -import org.springframework.web.bind.support.SessionStatus; -import org.springframework.web.servlet.view.RedirectView; - -import com.google.common.collect.Lists; - -public class OauthRegistrationControllerTest { - OauthRegistrationController oauthRegistrationController = new OauthRegistrationController(); - - @Mock - RegistrationController registrationController; - - @Mock - OrcidAuthorizationEndpoint authorizationEndpoint; - - @Mock - AuthenticationManager authenticationManager; - - @Mock - private HttpServletRequest servletRequest; - - @Mock - private HttpServletResponse servletResponse; - - @Mock - private RequestInfoFormLocalCache requestInfoFormLocalCache; - - @Before - public void before() { - MockitoAnnotations.initMocks(this); - oauthRegistrationController.setRegistrationController(registrationController); - oauthRegistrationController.setAuthorizationEndpoint(authorizationEndpoint); - oauthRegistrationController.setAuthenticationManager(authenticationManager); - } - - @Test - public void testStripHtmlFromNames() throws UnsupportedEncodingException { - RequestInfoForm rf = new RequestInfoForm(); - HttpSession session = mock(HttpSession.class); - requestInfoFormLocalCache = mock(RequestInfoFormLocalCache.class); - when(requestInfoFormLocalCache.get(any())).thenReturn(rf); - oauthRegistrationController.setRequestInfoFormLocalCache(requestInfoFormLocalCache); - RedirectView mv = new RedirectView(); - when(servletRequest.getSession()).thenReturn(session); - when(session.getId()).thenReturn("ID1"); - when(authorizationEndpoint.approveOrDeny(Matchers.anyMap(), Matchers.anyMap(), any(SessionStatus.class), any(Principal.class))).thenReturn(mv); - when(authenticationManager.authenticate(any(Authentication.class))).thenAnswer(new Answer(){ - @Override - public Authentication answer(InvocationOnMock invocation) throws Throwable { - OrcidOAuth2Authentication mockedAuthentication = mock(OrcidOAuth2Authentication.class); - return mockedAuthentication; - } - }); - - Text email = Text.valueOf(System.currentTimeMillis() + "@test.orcid.org"); - - OauthRegistrationForm reg = new OauthRegistrationForm(); - org.orcid.pojo.ajaxForm.Visibility fv = new org.orcid.pojo.ajaxForm.Visibility(); - fv.setVisibility(org.orcid.jaxb.model.v3.release.common.Visibility.PUBLIC); - reg.setActivitiesVisibilityDefault(fv); - reg.setEmail(email); - reg.setEmailConfirm(email); - reg.setFamilyNames(Text.valueOf("")); - reg.setGivenNames(Text.valueOf("")); - reg.setPassword(Text.valueOf("1234abcd")); - reg.setPasswordConfirm(Text.valueOf("1234abcd")); - reg.setValNumClient(2L); - reg.setValNumServer(4L); - reg.setApproved(true); - Checkbox c = new Checkbox(); - c.setValue(true); - reg.setTermsOfUse(c); - reg.setCreationType(Text.valueOf(CreationMethod.DIRECT.value())); - reg.setPersistentTokenEnabled(true); - oauthRegistrationController.registerAndAuthorize(servletRequest, servletResponse, reg); - - ArgumentCaptor argument1 = ArgumentCaptor.forClass(HttpServletRequest.class); - ArgumentCaptor argument2 = ArgumentCaptor.forClass(Registration.class); - ArgumentCaptor argument3 = ArgumentCaptor.forClass(Boolean.class); - ArgumentCaptor argument4 = ArgumentCaptor.forClass(Locale.class); - ArgumentCaptor argument5 = ArgumentCaptor.forClass(String.class); - - verify(registrationController).createMinimalRegistration(argument1.capture(), argument2.capture(), argument3.capture(), argument4.capture(), argument5.capture()); - assertNotNull(argument2.getValue()); - Registration registration = argument2.getValue(); - assertEquals(email.getValue(), registration.getEmail().getValue()); - assertEquals("Given Names", registration.getGivenNames().getValue()); - assertEquals("Family Name", registration.getFamilyNames().getValue()); - } -} diff --git a/orcid-web/src/test/java/org/orcid/frontend/web/util/ThirdPartyLinkManagerTest.java b/orcid-web/src/test/java/org/orcid/frontend/web/util/ThirdPartyLinkManagerTest.java index 7b545fda591..0bd26970589 100644 --- a/orcid-web/src/test/java/org/orcid/frontend/web/util/ThirdPartyLinkManagerTest.java +++ b/orcid-web/src/test/java/org/orcid/frontend/web/util/ThirdPartyLinkManagerTest.java @@ -18,8 +18,8 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.orcid.core.locale.LocaleManager; -import org.orcid.core.oauth.OrcidOauth2TokenDetailService; import org.orcid.core.manager.ClientDetailsEntityCacheManager; +import org.orcid.core.manager.v3.read_only.ClientDetailsManagerReadOnly; import org.orcid.core.utils.cache.redis.RedisClient; import org.orcid.jaxb.model.clientgroup.RedirectUriType; import org.orcid.persistence.dao.ClientRedirectDao; @@ -38,10 +38,10 @@ public class ThirdPartyLinkManagerTest { private ClientDetailsEntityCacheManager clientDetailsEntityCacheManager; @Mock - private LocaleManager localeManager; + private ClientDetailsManagerReadOnly clientDetailsManagerReadOnly; @Mock - private OrcidOauth2TokenDetailService orcidOauth2TokenDetailService; + private LocaleManager localeManager; @Mock private RedisClient redisClient; @@ -222,8 +222,8 @@ public void findSearchAndLinkWizardClients_cacheMiss_buildsListAndSetsConnected( initSearchAndLinkWizardData(); Mockito.when(redisClient.get(eq("works-search-and-link-wizard-clients"))).thenReturn(null); - Mockito.when(orcidOauth2TokenDetailService.doesClientKnowUser(eq("APP-00001"), eq("0000-0000-0000-0001"))).thenReturn(true); - Mockito.when(orcidOauth2TokenDetailService.doesClientKnowUser(eq("APP-00002"), eq("0000-0000-0000-0001"))).thenReturn(false); + Mockito.when(clientDetailsManagerReadOnly.doesClientKnowUser(eq("APP-00001"), eq("0000-0000-0000-0001"))).thenReturn(true); + Mockito.when(clientDetailsManagerReadOnly.doesClientKnowUser(eq("APP-00002"), eq("0000-0000-0000-0001"))).thenReturn(false); List forms = thirdPartyLinkManager.findSearchAndLinkWizardClients("0000-0000-0000-0001"); assertNotNull(forms); @@ -253,7 +253,7 @@ public void findSearchAndLinkWizardClients_cacheHit_usesCachedValueAndSkipsDao() String cachedJson = "[{\"id\":\"APP-00001\",\"name\":\"Client One\",\"redirectUri\":\"https://test.orcid.org/search-link/1\",\"scopes\":\"/read-limited\",\"redirectUriMetadata\":{\"certified\":true}}]"; Mockito.when(redisClient.get(eq("works-search-and-link-wizard-clients"))).thenReturn(cachedJson); - Mockito.when(orcidOauth2TokenDetailService.doesClientKnowUser(eq("APP-00001"), eq("0000-0000-0000-0001"))).thenReturn(true); + Mockito.when(clientDetailsManagerReadOnly.doesClientKnowUser(eq("APP-00001"), eq("0000-0000-0000-0001"))).thenReturn(true); List forms = thirdPartyLinkManager.findSearchAndLinkWizardClients("0000-0000-0000-0001"); assertNotNull(forms); @@ -272,8 +272,8 @@ public void findSearchAndLinkWizardClients_badCachedValue_rebuildsAndStores() { ReflectionTestUtils.setField(thirdPartyLinkManager, "worksSearchAndLinkWizardCacheTtl", 99); Mockito.when(redisClient.get(eq("works-search-and-link-wizard-clients"))).thenReturn("{bad json"); - Mockito.when(orcidOauth2TokenDetailService.doesClientKnowUser(eq("APP-00001"), eq("0000-0000-0000-0001"))).thenReturn(true); - Mockito.when(orcidOauth2TokenDetailService.doesClientKnowUser(eq("APP-00002"), eq("0000-0000-0000-0001"))).thenReturn(false); + Mockito.when(clientDetailsManagerReadOnly.doesClientKnowUser(eq("APP-00001"), eq("0000-0000-0000-0001"))).thenReturn(true); + Mockito.when(clientDetailsManagerReadOnly.doesClientKnowUser(eq("APP-00002"), eq("0000-0000-0000-0001"))).thenReturn(false); List forms = thirdPartyLinkManager.findSearchAndLinkWizardClients("0000-0000-0000-0001"); assertNotNull(forms); From 6e5c6f05f98e3c912afc36bb7f49be8ea90658b7 Mon Sep 17 00:00:00 2001 From: amontenegro Date: Tue, 31 Mar 2026 10:32:09 -0600 Subject: [PATCH 12/16] Member API starts, client authentication beans are now in the orcid-api-common package --- .../api/common/OrcidApiCommonEndpoints.java | 4 ++ .../ClientDetailsUserDetailsService.java | 32 ++++++++++++++++ .../oauth/APIAuthenticationEntryPoint.java | 18 +++++++++ ...rcidMultiSecretAuthenticationProvider.java | 26 +++++-------- .../orcid-oauth2-api-common-config.xml | 18 ++++++++- .../resources/orcid-api-security-context.xml | 34 +++-------------- .../main/resources/orcid-api-web-context.xml | 10 ----- orcid-api-web/src/main/webapp/WEB-INF/web.xml | 10 ----- .../core/userDetails/MultiSecretClient.java | 38 +++++++++++++++++++ .../src/main/resources/orcid-core-context.xml | 4 -- .../orcid-internal-api-security-context.xml | 4 -- .../resources/orcid-t1-security-context.xml | 4 -- .../resources/orcid-frontend-security.xml | 4 -- 13 files changed, 124 insertions(+), 82 deletions(-) create mode 100644 orcid-api-common/src/main/java/org/orcid/api/common/provider/client/ClientDetailsUserDetailsService.java rename {orcid-core/src/main/java/org/orcid/core => orcid-api-common/src/main/java/org/orcid/api/common/security}/oauth/OrcidMultiSecretAuthenticationProvider.java (54%) create mode 100644 orcid-core/src/main/java/org/orcid/core/userDetails/MultiSecretClient.java diff --git a/orcid-api-common/src/main/java/org/orcid/api/common/OrcidApiCommonEndpoints.java b/orcid-api-common/src/main/java/org/orcid/api/common/OrcidApiCommonEndpoints.java index f2b59e60c7b..441440b7b4a 100644 --- a/orcid-api-common/src/main/java/org/orcid/api/common/OrcidApiCommonEndpoints.java +++ b/orcid-api-common/src/main/java/org/orcid/api/common/OrcidApiCommonEndpoints.java @@ -47,6 +47,10 @@ public Response obtainOauth2TokenPost(@HeaderParam("Authorization") @DefaultValu // Token delegation is not implemented in the authorization server if(grantType == null) { throw new IllegalArgumentException("grant_type is missing"); + } else if(clientId == null || clientId.length() > 50 || StringUtils.isBlank(clientId)) { + throw new IllegalArgumentException("client_id is missing or invalid"); + } else if(clientSecret == null || clientSecret.length() > 100 || StringUtils.isBlank(clientSecret)) { + throw new IllegalArgumentException("client_secret is missing or invalid"); } Response response = null; diff --git a/orcid-api-common/src/main/java/org/orcid/api/common/provider/client/ClientDetailsUserDetailsService.java b/orcid-api-common/src/main/java/org/orcid/api/common/provider/client/ClientDetailsUserDetailsService.java new file mode 100644 index 00000000000..713341c5af3 --- /dev/null +++ b/orcid-api-common/src/main/java/org/orcid/api/common/provider/client/ClientDetailsUserDetailsService.java @@ -0,0 +1,32 @@ +package org.orcid.api.common.provider.client; + +import org.apache.commons.lang.StringUtils; +import org.orcid.core.manager.v3.read_only.ClientDetailsManagerReadOnly; +import org.orcid.core.userDetails.MultiSecretClient; +import org.orcid.persistence.jpa.entities.ClientDetailsEntity; +import org.orcid.persistence.jpa.entities.ClientSecretEntity; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; + +import javax.annotation.Resource; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public class ClientDetailsUserDetailsService implements UserDetailsService { + + @Resource(name = "clientDetailsManagerReadOnlyV3") + private ClientDetailsManagerReadOnly clientDetailsManagerReadOnly; + + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + if(clientDetailsManagerReadOnly.exists(username)) { + ClientDetailsEntity client = clientDetailsManagerReadOnly.findByClientId(username); + Set secrets = client.getClientSecrets(); + List secretsList = secrets.stream().map(s -> new MultiSecretClient.Secret(s.getClientSecret(), s.isPrimary())).collect(Collectors.toList()); + return new MultiSecretClient(client.getClientId(), StringUtils.EMPTY, client.getAuthorities(), secretsList); + } else { + throw new UsernameNotFoundException("Client with id " + username + " not found"); + } + } +} diff --git a/orcid-api-common/src/main/java/org/orcid/api/common/security/oauth/APIAuthenticationEntryPoint.java b/orcid-api-common/src/main/java/org/orcid/api/common/security/oauth/APIAuthenticationEntryPoint.java index 49516adba88..45579cb450f 100644 --- a/orcid-api-common/src/main/java/org/orcid/api/common/security/oauth/APIAuthenticationEntryPoint.java +++ b/orcid-api-common/src/main/java/org/orcid/api/common/security/oauth/APIAuthenticationEntryPoint.java @@ -1,5 +1,7 @@ package org.orcid.api.common.security.oauth; +import org.orcid.jaxb.model.v3.release.error.OrcidError; +import org.springframework.http.HttpStatus; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.AuthenticationEntryPoint; @@ -11,6 +13,22 @@ public class APIAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { + try { + //TODO: This is NOT ready, exceptions should be handled and the proper error message returned + OrcidError orcidError = new OrcidError(); + orcidError.setResponseCode(response.getStatus()); + orcidError.setDeveloperMessage("GENERIC ERROR MESSAGE"); + response.setStatus(HttpStatus.FORBIDDEN.value()); + response.getWriter().write(orcidError.toString()); + response.flushBuffer(); + } catch (IOException e) { + throw e; + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + // Wrap other Exceptions. These are not expected to happen + throw new RuntimeException(e); + } } } diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/OrcidMultiSecretAuthenticationProvider.java b/orcid-api-common/src/main/java/org/orcid/api/common/security/oauth/OrcidMultiSecretAuthenticationProvider.java similarity index 54% rename from orcid-core/src/main/java/org/orcid/core/oauth/OrcidMultiSecretAuthenticationProvider.java rename to orcid-api-common/src/main/java/org/orcid/api/common/security/oauth/OrcidMultiSecretAuthenticationProvider.java index 1498f8c7377..c4b5c32a17f 100644 --- a/orcid-core/src/main/java/org/orcid/core/oauth/OrcidMultiSecretAuthenticationProvider.java +++ b/orcid-api-common/src/main/java/org/orcid/api/common/security/oauth/OrcidMultiSecretAuthenticationProvider.java @@ -1,11 +1,9 @@ -package org.orcid.core.oauth; +package org.orcid.api.common.security.oauth; import javax.annotation.Resource; -import org.orcid.core.manager.ClientDetailsManager; import org.orcid.core.manager.EncryptionManager; -import org.orcid.persistence.jpa.entities.ClientDetailsEntity; -import org.orcid.persistence.jpa.entities.ClientSecretEntity; +import org.orcid.core.userDetails.MultiSecretClient; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.dao.DaoAuthenticationProvider; @@ -19,33 +17,29 @@ */ public class OrcidMultiSecretAuthenticationProvider extends DaoAuthenticationProvider { - @Resource - private ClientDetailsManager clientDetailsManager; - @Resource private EncryptionManager encryptionManager; - @SuppressWarnings("deprecation") @Override protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { if (authentication.getCredentials() == null) { logger.debug("Authentication failed: no credentials provided"); - throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); + throw new BadCredentialsException("No credentials provided for " + userDetails.getUsername()); } String presentedPassword = authentication.getCredentials().toString(); + if(!presentedPassword.startsWith("{noop}")){ + presentedPassword = "{noop}" + presentedPassword; + } - ClientDetailsEntity clientDetailsEntity = clientDetailsManager.findByClientId(userDetails.getUsername()); - for (ClientSecretEntity clientSecretEntity : clientDetailsEntity.getClientSecrets()) { - if(!presentedPassword.startsWith("{noop}")){ - presentedPassword = "{noop}" + presentedPassword; - } - if (getPasswordEncoder().matches(encryptionManager.decryptForInternalUse(clientSecretEntity.getClientSecret()), presentedPassword)) { + MultiSecretClient clientDetails = (MultiSecretClient) this.getUserDetailsService().loadUserByUsername(userDetails.getUsername()); + for (MultiSecretClient.Secret secret : clientDetails.getSecrets()) { + if (getPasswordEncoder().matches(encryptionManager.decryptForInternalUse(secret.getEncryptedSecret()), presentedPassword)) { return; } } logger.debug("Authentication failed: password does not match any value"); - throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); + throw new BadCredentialsException("Invalid client credentials provided for " + userDetails.getUsername()); } } \ No newline at end of file diff --git a/orcid-api-common/src/main/resources/orcid-oauth2-api-common-config.xml b/orcid-api-common/src/main/resources/orcid-oauth2-api-common-config.xml index 5eb39c9a220..e68aefde292 100644 --- a/orcid-api-common/src/main/resources/orcid-oauth2-api-common-config.xml +++ b/orcid-api-common/src/main/resources/orcid-oauth2-api-common-config.xml @@ -1,9 +1,10 @@ + http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-5.6.xsd"> + + + + + + + + + + + + + \ No newline at end of file diff --git a/orcid-api-web/src/main/resources/orcid-api-security-context.xml b/orcid-api-web/src/main/resources/orcid-api-security-context.xml index 82e441238a5..e712b733b06 100644 --- a/orcid-api-web/src/main/resources/orcid-api-security-context.xml +++ b/orcid-api-web/src/main/resources/orcid-api-security-context.xml @@ -25,46 +25,24 @@ - + - + + - - - - - - - - - - - - - - - - - - + + - - - - - - - - + diff --git a/orcid-api-web/src/main/resources/orcid-api-web-context.xml b/orcid-api-web/src/main/resources/orcid-api-web-context.xml index 2c8855ad949..70bdeb8f10f 100644 --- a/orcid-api-web/src/main/resources/orcid-api-web-context.xml +++ b/orcid-api-web/src/main/resources/orcid-api-web-context.xml @@ -12,16 +12,6 @@ - - diff --git a/orcid-api-web/src/main/webapp/WEB-INF/web.xml b/orcid-api-web/src/main/webapp/WEB-INF/web.xml index a37cb5608e1..50980d05d16 100644 --- a/orcid-api-web/src/main/webapp/WEB-INF/web.xml +++ b/orcid-api-web/src/main/webapp/WEB-INF/web.xml @@ -105,16 +105,6 @@ /* - - clientCredentialsTokenEndpointFilter - org.springframework.web.filter.DelegatingFilterProxy - - - - clientCredentialsTokenEndpointFilter - /oauth/token - - springSecurityFilterChain org.springframework.web.filter.DelegatingFilterProxy diff --git a/orcid-core/src/main/java/org/orcid/core/userDetails/MultiSecretClient.java b/orcid-core/src/main/java/org/orcid/core/userDetails/MultiSecretClient.java new file mode 100644 index 00000000000..870c13f430e --- /dev/null +++ b/orcid-core/src/main/java/org/orcid/core/userDetails/MultiSecretClient.java @@ -0,0 +1,38 @@ +package org.orcid.core.userDetails; + +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.User; + +import java.util.Collection; +import java.util.List; + +public class MultiSecretClient extends User { + private List secrets; + + public MultiSecretClient(String username, String password, Collection authorities, List secrets) { + super(username, password, authorities); + this.secrets = secrets; + } + + public List getSecrets() { + return secrets; + } + + public static class Secret { + private final String encryptedSecret; + private final boolean primary; + + public Secret(String encryptedSecret, boolean primary) { + this.encryptedSecret = encryptedSecret; + this.primary = primary; + } + + public String getEncryptedSecret() { + return encryptedSecret; + } + + public boolean isPrimary() { + return primary; + } + } +} diff --git a/orcid-core/src/main/resources/orcid-core-context.xml b/orcid-core/src/main/resources/orcid-core-context.xml index a4f9b1413d3..171227c1073 100644 --- a/orcid-core/src/main/resources/orcid-core-context.xml +++ b/orcid-core/src/main/resources/orcid-core-context.xml @@ -28,10 +28,6 @@ - - - - diff --git a/orcid-internal-api/src/main/resources/orcid-internal-api-security-context.xml b/orcid-internal-api/src/main/resources/orcid-internal-api-security-context.xml index 0acdab44be1..08ec77e2ea4 100644 --- a/orcid-internal-api/src/main/resources/orcid-internal-api-security-context.xml +++ b/orcid-internal-api/src/main/resources/orcid-internal-api-security-context.xml @@ -58,10 +58,6 @@ - - - - diff --git a/orcid-pub-web/src/main/resources/orcid-t1-security-context.xml b/orcid-pub-web/src/main/resources/orcid-t1-security-context.xml index f2843b0f82f..ba149e70628 100644 --- a/orcid-pub-web/src/main/resources/orcid-t1-security-context.xml +++ b/orcid-pub-web/src/main/resources/orcid-t1-security-context.xml @@ -67,10 +67,6 @@ - - - - diff --git a/orcid-web/src/main/resources/orcid-frontend-security.xml b/orcid-web/src/main/resources/orcid-frontend-security.xml index e469dae90c9..a6df4fa3525 100644 --- a/orcid-web/src/main/resources/orcid-frontend-security.xml +++ b/orcid-web/src/main/resources/orcid-frontend-security.xml @@ -441,10 +441,6 @@ - - - - From 0390a8288256d30a61a21b2909d873c44c43879e Mon Sep 17 00:00:00 2001 From: amontenegro Date: Tue, 31 Mar 2026 12:28:59 -0600 Subject: [PATCH 13/16] Small update --- .../src/main/resources/orcid-api-security-context.xml | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/orcid-api-web/src/main/resources/orcid-api-security-context.xml b/orcid-api-web/src/main/resources/orcid-api-security-context.xml index e712b733b06..afd87b43516 100644 --- a/orcid-api-web/src/main/resources/orcid-api-security-context.xml +++ b/orcid-api-web/src/main/resources/orcid-api-security-context.xml @@ -16,10 +16,8 @@ - - + - @@ -27,12 +25,7 @@ - - - - - - + From 9954e1f0e1349398c0d9e38d79858fb822cd8bc7 Mon Sep 17 00:00:00 2001 From: amontenegro Date: Tue, 31 Mar 2026 17:59:29 -0600 Subject: [PATCH 14/16] The member API is working and the token introspection is working on the member API! --- orcid-api-common/pom.xml | 2 +- .../api/common/filter/TokenTargetFilter.java | 56 +------ .../oauth/OrcidBearerTokenFilter.java | 147 ++++++++++++++++++ .../orcid-oauth2-api-common-config.xml | 2 + .../resources/orcid-api-security-context.xml | 3 +- .../v3/impl/OrcidSecurityManagerImpl.java | 1 + .../oauth/OrcidBearerTokenAuthentication.java | 2 +- 7 files changed, 160 insertions(+), 53 deletions(-) create mode 100644 orcid-api-common/src/main/java/org/orcid/api/common/security/oauth/OrcidBearerTokenFilter.java diff --git a/orcid-api-common/pom.xml b/orcid-api-common/pom.xml index 01c4a1504be..a32f06f3e90 100644 --- a/orcid-api-common/pom.xml +++ b/orcid-api-common/pom.xml @@ -44,7 +44,7 @@ org.springframework spring-aop
- + org.glassfish.jersey.containers diff --git a/orcid-api-common/src/main/java/org/orcid/api/common/filter/TokenTargetFilter.java b/orcid-api-common/src/main/java/org/orcid/api/common/filter/TokenTargetFilter.java index 4954b775468..4d632d73b1c 100644 --- a/orcid-api-common/src/main/java/org/orcid/api/common/filter/TokenTargetFilter.java +++ b/orcid-api-common/src/main/java/org/orcid/api/common/filter/TokenTargetFilter.java @@ -7,63 +7,28 @@ import javax.ws.rs.container.ContainerRequestFilter; import javax.ws.rs.ext.Provider; -import org.orcid.core.exception.OrcidUnauthorizedException; import org.orcid.core.oauth.OrcidBearerTokenAuthentication; import org.orcid.utils.OrcidStringUtils; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.web.context.request.RequestAttributes; -import org.springframework.web.context.request.RequestContextHolder; -@Deprecated @Provider public class TokenTargetFilter implements ContainerRequestFilter { + //TODO: this method is doing exactly the same that the OrcidSecutiryManagerImpl.isMyToken does, so, lets review it and leave only one. + @Override public void filter(ContainerRequestContext request) { Matcher m = OrcidStringUtils.orcidPattern.matcher(request.getUriInfo().getPath()); if (m.find()) { - validateTargetRecord(m.group(), request); + validateTargetRecord(m.group()); } return ; } - private void validateTargetRecord(String targetOrcid, ContainerRequestContext request) { + private void validateTargetRecord(String targetOrcid) { // Verify if it is the owner of the token - - //TODO: CAN WE DELETE THIS FILTER???? - - System.out.println("------------------------------------------------------------"); - System.out.println("------------------------------------------------------------"); - System.out.println("------------------------------------------------------------"); - System.out.println("------------------------------------------------------------"); - System.out.println("------------------------------------------------------------"); - System.out.println("------------------------------------------------------------"); - System.out.println("------------------------------------------------------------"); - System.out.println("------------------------------------------------------------"); - System.out.println("------------------------------------------------------------"); - System.out.println("------------------------------------------------------------"); - System.out.println("------------------------------------------------------------"); - System.out.println("------------------------------------------------------------"); - System.out.println("------------------------------------------------------------"); - - System.out.println("TokenTargetFilter"); - - System.out.println("------------------------------------------------------------"); - System.out.println("------------------------------------------------------------"); - System.out.println("------------------------------------------------------------"); - System.out.println("------------------------------------------------------------"); - System.out.println("------------------------------------------------------------"); - System.out.println("------------------------------------------------------------"); - System.out.println("------------------------------------------------------------"); - System.out.println("------------------------------------------------------------"); - System.out.println("------------------------------------------------------------"); - System.out.println("------------------------------------------------------------"); - System.out.println("------------------------------------------------------------"); - System.out.println("------------------------------------------------------------"); - System.out.println("------------------------------------------------------------"); - SecurityContext context = SecurityContextHolder.getContext(); if (context != null && context.getAuthentication() != null) { Authentication authentication = context.getAuthentication(); @@ -71,20 +36,11 @@ private void validateTargetRecord(String targetOrcid, ContainerRequestContext re OrcidBearerTokenAuthentication authDetails = (OrcidBearerTokenAuthentication) authentication; if (authDetails != null) { if (!targetOrcid.equals(authDetails.getUserOrcid())) { - throwException(); + throw new AccessControlException("You do not have the required permissions."); } } } } } - - private void throwException() { - RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); - String apiVersion = (String) requestAttributes.getAttribute(ApiVersionFilter.API_VERSION_REQUEST_ATTRIBUTE_NAME, RequestAttributes.SCOPE_REQUEST); - if(apiVersion.equals("1.2")) { - throw new AccessControlException("You do not have the required permissions."); - } else { - throw new OrcidUnauthorizedException("Access token is for a different record"); - } - } + } diff --git a/orcid-api-common/src/main/java/org/orcid/api/common/security/oauth/OrcidBearerTokenFilter.java b/orcid-api-common/src/main/java/org/orcid/api/common/security/oauth/OrcidBearerTokenFilter.java new file mode 100644 index 00000000000..0ba3e2d9bd8 --- /dev/null +++ b/orcid-api-common/src/main/java/org/orcid/api/common/security/oauth/OrcidBearerTokenFilter.java @@ -0,0 +1,147 @@ +package org.orcid.api.common.security.oauth; + + +import com.google.common.collect.Sets; +import org.apache.commons.lang.StringUtils; +import org.apache.log4j.Logger; +import org.codehaus.jettison.json.JSONException; +import org.codehaus.jettison.json.JSONObject; +import org.orcid.core.exception.InvalidTokenException; +import org.orcid.core.oauth.OrcidBearerTokenAuthentication; +import org.orcid.core.oauth.authorizationServer.AuthorizationServerUtil; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.annotation.Resource; +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.net.URISyntaxException; +import java.security.AccessControlException; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.Set; +import java.util.stream.Collectors; + +public class OrcidBearerTokenFilter implements Filter { + private static final Logger logger = Logger.getLogger(OrcidBearerTokenFilter.class); + + @Resource + private AuthorizationServerUtil authorizationServerUtil; + + @Resource + private APIAuthenticationEntryPoint apiAuthenticationEntryPoint; + + @Override + public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { + final HttpServletRequest request = (HttpServletRequest) req; + final HttpServletResponse response = (HttpServletResponse) res; + + String tokenValue = extractToken(request); + if(StringUtils.isBlank(tokenValue)) { + apiAuthenticationEntryPoint.commence(request, response, null); + return; + } + + try { + JSONObject tokenData = authorizationServerUtil.tokenIntrospection(tokenValue); + OrcidBearerTokenAuthentication authentication = validateTokenData(tokenValue, tokenData); + SecurityContextHolder.getContext().setAuthentication(authentication); + chain.doFilter(request, response); + } catch (URISyntaxException | InterruptedException | JSONException e) { + //TODO: Define error message and add exception type to it + apiAuthenticationEntryPoint.commence(request, response, null); + return; + } + } + + private OrcidBearerTokenAuthentication validateTokenData(String accessTokenValue, JSONObject tokenInfo) { + try { + if(tokenInfo == null) { + throw new AccessControlException("Invalid access token: Unable to obtain information from the authorization server"); + } + + boolean isTokenActive = tokenInfo.getBoolean("active"); + if(isTokenActive) { + // If the token is user revoked it might be used for DELETE requests + return buildAuthentication(accessTokenValue, tokenInfo); + } else { + ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + if(RequestMethod.DELETE.name().equals(attr.getRequest().getMethod())) { + if(tokenInfo.has("USER_REVOKED") && tokenInfo.getBoolean("USER_REVOKED") == true) { + return buildAuthentication(accessTokenValue, tokenInfo); + } else { + throw new AccessControlException("Invalid access token: " + accessTokenValue); + } + } else { + throw new AccessControlException("Invalid access token: " + accessTokenValue); + } + } + } catch(AccessControlException i) { + throw i; + } catch(Exception e) { + logger.error("Exception validating token from authorization server", e); + throw new RuntimeException("Exception validating token from authorization server", e); + } + } + + private OrcidBearerTokenAuthentication buildAuthentication(String accessTokenValue, JSONObject tokenInfo) throws JSONException { + String clientId = tokenInfo.getString("client_id"); + String userOrcid = tokenInfo.getString("username"); + + OrcidBearerTokenAuthentication.Builder builder = OrcidBearerTokenAuthentication.builder(clientId, userOrcid, accessTokenValue); + + Set scopes = Arrays.stream(tokenInfo.getString("scope").split("[\\s,]+")) + .collect(Collectors.toSet()); + builder.scopes(scopes); + + // Set granted authorities + if(tokenInfo.has("clientGrantedAuthority")) { + builder.authorities(Sets.newHashSet(new SimpleGrantedAuthority(tokenInfo.getString("clientGrantedAuthority")))); + } + + if(tokenInfo.has("OBO_CLIENT_ID")) { + String oboClientId = tokenInfo.getString("OBO_CLIENT_ID"); + builder.oboClientId(oboClientId); + } + builder.authenticated(true); + return builder.build(); + } + + private String extractToken(HttpServletRequest request) { + // first check the header... + String token = extractHeaderToken(request); + + // bearer type allows a request parameter as well + if (token == null) { + logger.debug("Token not found in headers. Trying request parameters."); + token = request.getParameter("access_token"); + if (token == null) { + logger.debug("Token not found in request parameters. Not an OAuth2 request."); + } + } + + return token; + } + + private String extractHeaderToken(HttpServletRequest request) { + Enumeration headers = request.getHeaders("Authorization"); + while (headers.hasMoreElements()) { // typically there is only one (most servers enforce that) + String value = headers.nextElement(); + if ((value.toLowerCase().startsWith("bearer"))) { + String authHeaderValue = value.substring("bearer".length()).trim(); + int commaIndex = authHeaderValue.indexOf(','); + if (commaIndex > 0) { + authHeaderValue = authHeaderValue.substring(0, commaIndex); + } + return authHeaderValue; + } + } + + return null; + } +} diff --git a/orcid-api-common/src/main/resources/orcid-oauth2-api-common-config.xml b/orcid-api-common/src/main/resources/orcid-oauth2-api-common-config.xml index e68aefde292..03e8c14c50b 100644 --- a/orcid-api-common/src/main/resources/orcid-oauth2-api-common-config.xml +++ b/orcid-api-common/src/main/resources/orcid-oauth2-api-common-config.xml @@ -72,4 +72,6 @@ + + \ No newline at end of file diff --git a/orcid-api-web/src/main/resources/orcid-api-security-context.xml b/orcid-api-web/src/main/resources/orcid-api-security-context.xml index afd87b43516..d187ed7b203 100644 --- a/orcid-api-web/src/main/resources/orcid-api-security-context.xml +++ b/orcid-api-web/src/main/resources/orcid-api-security-context.xml @@ -31,7 +31,8 @@ - + + diff --git a/orcid-core/src/main/java/org/orcid/core/manager/v3/impl/OrcidSecurityManagerImpl.java b/orcid-core/src/main/java/org/orcid/core/manager/v3/impl/OrcidSecurityManagerImpl.java index bfd0e746e89..a66c6ef0b6f 100644 --- a/orcid-core/src/main/java/org/orcid/core/manager/v3/impl/OrcidSecurityManagerImpl.java +++ b/orcid-core/src/main/java/org/orcid/core/manager/v3/impl/OrcidSecurityManagerImpl.java @@ -777,6 +777,7 @@ private void checkVisibility(VisibilityType element, ScopePathType requiredScope } } + //TODO: this method is doing exactly the same that the TokenTargetFilter does, so, lets review it and leave only one. private void isMyToken(String orcid) { Authentication authentication = getAuthentication(); if (authentication == null) { diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/OrcidBearerTokenAuthentication.java b/orcid-core/src/main/java/org/orcid/core/oauth/OrcidBearerTokenAuthentication.java index 5ac29a59814..fe23c616974 100644 --- a/orcid-core/src/main/java/org/orcid/core/oauth/OrcidBearerTokenAuthentication.java +++ b/orcid-core/src/main/java/org/orcid/core/oauth/OrcidBearerTokenAuthentication.java @@ -50,7 +50,7 @@ private OrcidBearerTokenAuthentication(Builder builder) { */ @Override public Collection getAuthorities() { - return this.getAuthorities(); + return this.authorities; } /** From ec8be95fc4a765a31ab6a7fe313e231579a30535 Mon Sep 17 00:00:00 2001 From: amontenegro Date: Tue, 31 Mar 2026 18:54:51 -0600 Subject: [PATCH 15/16] Public API workinggit status! --- .../api/common/filter/TokenTargetFilter.java | 2 +- .../oauth/OrcidBearerTokenFilter.java | 4 +- .../orcid-oauth2-api-common-config.xml | 2 + .../resources/orcid-api-security-context.xml | 4 +- orcid-api-web/src/main/webapp/WEB-INF/web.xml | 1 - .../OrcidRandomValueTokenServicesImpl.java | 120 ------------------ .../orcid-internal-api-security-context.xml | 15 +-- .../src/main/webapp/WEB-INF/web.xml | 10 -- .../impl/PublicV2ApiServiceDelegatorImpl.java | 12 -- ...licV2ApiServiceVersionedDelegatorImpl.java | 8 +- .../impl/PublicV3ApiServiceDelegatorImpl.java | 12 -- .../resources/orcid-t1-security-context.xml | 76 ++--------- .../main/resources/orcid-t1-web-context.xml | 3 +- orcid-pub-web/src/main/webapp/WEB-INF/web.xml | 11 -- .../resources/orcid-frontend-security.xml | 15 --- orcid-web/src/main/webapp/WEB-INF/web.xml | 10 -- 16 files changed, 20 insertions(+), 285 deletions(-) delete mode 100644 orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidRandomValueTokenServicesImpl.java diff --git a/orcid-api-common/src/main/java/org/orcid/api/common/filter/TokenTargetFilter.java b/orcid-api-common/src/main/java/org/orcid/api/common/filter/TokenTargetFilter.java index 4d632d73b1c..3c0a5c7f558 100644 --- a/orcid-api-common/src/main/java/org/orcid/api/common/filter/TokenTargetFilter.java +++ b/orcid-api-common/src/main/java/org/orcid/api/common/filter/TokenTargetFilter.java @@ -17,7 +17,7 @@ public class TokenTargetFilter implements ContainerRequestFilter { //TODO: this method is doing exactly the same that the OrcidSecutiryManagerImpl.isMyToken does, so, lets review it and leave only one. - + @Override public void filter(ContainerRequestContext request) { Matcher m = OrcidStringUtils.orcidPattern.matcher(request.getUriInfo().getPath()); diff --git a/orcid-api-common/src/main/java/org/orcid/api/common/security/oauth/OrcidBearerTokenFilter.java b/orcid-api-common/src/main/java/org/orcid/api/common/security/oauth/OrcidBearerTokenFilter.java index 0ba3e2d9bd8..18728a5c4cd 100644 --- a/orcid-api-common/src/main/java/org/orcid/api/common/security/oauth/OrcidBearerTokenFilter.java +++ b/orcid-api-common/src/main/java/org/orcid/api/common/security/oauth/OrcidBearerTokenFilter.java @@ -43,8 +43,8 @@ public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) String tokenValue = extractToken(request); if(StringUtils.isBlank(tokenValue)) { - apiAuthenticationEntryPoint.commence(request, response, null); - return; + // If the token is not present, continue the chain + chain.doFilter(request, response); } try { diff --git a/orcid-api-common/src/main/resources/orcid-oauth2-api-common-config.xml b/orcid-api-common/src/main/resources/orcid-oauth2-api-common-config.xml index 03e8c14c50b..00e6f07ad83 100644 --- a/orcid-api-common/src/main/resources/orcid-oauth2-api-common-config.xml +++ b/orcid-api-common/src/main/resources/orcid-oauth2-api-common-config.xml @@ -74,4 +74,6 @@ + + \ No newline at end of file diff --git a/orcid-api-web/src/main/resources/orcid-api-security-context.xml b/orcid-api-web/src/main/resources/orcid-api-security-context.xml index d187ed7b203..0978cb354e7 100644 --- a/orcid-api-web/src/main/resources/orcid-api-security-context.xml +++ b/orcid-api-web/src/main/resources/orcid-api-security-context.xml @@ -52,7 +52,5 @@ - - - + \ No newline at end of file diff --git a/orcid-api-web/src/main/webapp/WEB-INF/web.xml b/orcid-api-web/src/main/webapp/WEB-INF/web.xml index 50980d05d16..2c59261c3b0 100644 --- a/orcid-api-web/src/main/webapp/WEB-INF/web.xml +++ b/orcid-api-web/src/main/webapp/WEB-INF/web.xml @@ -128,7 +128,6 @@ clientIdAttributeFilter * - jersey diff --git a/orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidRandomValueTokenServicesImpl.java b/orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidRandomValueTokenServicesImpl.java deleted file mode 100644 index 65389872441..00000000000 --- a/orcid-core/src/main/java/org/orcid/core/oauth/service/OrcidRandomValueTokenServicesImpl.java +++ /dev/null @@ -1,120 +0,0 @@ -package org.orcid.core.oauth.service; - -import java.util.*; -import java.util.stream.Collectors; - -import javax.annotation.Resource; -import javax.persistence.PersistenceException; - -import javassist.compiler.SyntaxError; -import org.apache.commons.lang3.StringUtils; -import org.codehaus.jettison.json.JSONException; -import org.codehaus.jettison.json.JSONObject; -import org.hibernate.exception.ConstraintViolationException; -import org.orcid.core.constants.OrcidOauth2Constants; -import org.orcid.core.constants.RevokeReason; -import org.orcid.core.exception.ClientDeactivatedException; -import org.orcid.core.exception.InvalidTokenException; -import org.orcid.core.exception.LockedException; -import org.orcid.core.manager.ClientDetailsEntityCacheManager; -import org.orcid.core.manager.ProfileEntityManager; -import org.orcid.core.oauth.*; -import org.orcid.core.oauth.authorizationServer.AuthorizationServerUtil; -import org.orcid.core.togglz.Features; -import org.orcid.core.utils.JsonUtils; -import org.orcid.core.utils.cache.redis.RedisClient; -import org.orcid.jaxb.model.message.ScopePathType; -import org.orcid.persistence.dao.OrcidOauth2AuthoriziationCodeDetailDao; -import org.orcid.persistence.dao.OrcidOauth2TokenDetailDao; -import org.orcid.persistence.jpa.entities.ClientDetailsEntity; -import org.orcid.persistence.jpa.entities.OrcidOauth2TokenDetail; -import org.orcid.persistence.jpa.entities.ProfileEntity; -import org.orcid.pojo.ajaxForm.PojoUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; -import org.orcid.core.exception.OrcidInvalidScopeException; -import com.google.common.collect.Sets; - -/** - * @author Declan Newman (declan) Date: 11/05/2012 - */ -@Deprecated -//TODO: This have to be removed, just a placeholder to remember where we validate the tokens!!!! -public class OrcidRandomValueTokenServicesImpl { - private static final Logger LOGGER = LoggerFactory.getLogger(OrcidRandomValueTokenServicesImpl.class); - - @Value("${org.orcid.core.token.write_validity_seconds:3600}") - private int writeValiditySeconds; - @Value("${org.orcid.core.token.read_validity_seconds:631138519}") - private int readValiditySeconds; - @Value("${org.orcid.core.token.implicit_validity_seconds:600}") - private int implicitValiditySeconds; - @Value("${org.orcid.core.token.write_validity_seconds:3600}") - private int ietfTokenExchangeValiditySeconds; - - @Resource - private AuthorizationServerUtil authorizationServerUtil; - - public OrcidBearerTokenAuthentication loadAuthentication(String accessTokenValue) throws AuthenticationException { - try { - JSONObject tokenInfo = authorizationServerUtil.tokenIntrospection(accessTokenValue); - if(tokenInfo == null) { - throw new InvalidTokenException("Invalid access token: Unable to obtain information from the authorization server"); - } - - boolean isTokenActive = tokenInfo.getBoolean("active"); - if(isTokenActive) { - // If the token is user revoked it might be used for DELETE requests - return buildAuthentication(accessTokenValue, tokenInfo); - } else { - ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); - if(RequestMethod.DELETE.name().equals(attr.getRequest().getMethod())) { - if(tokenInfo.has("USER_REVOKED") && tokenInfo.getBoolean("USER_REVOKED") == true) { - return buildAuthentication(accessTokenValue, tokenInfo); - } else { - throw new InvalidTokenException("Invalid access token: " + accessTokenValue); - } - } else { - throw new InvalidTokenException("Invalid access token: " + accessTokenValue); - } - } - } catch(InvalidTokenException i) { - throw i; - } catch(Exception e) { - LOGGER.error("Exception validating token from authorization server", e); - throw new RuntimeException("Exception validating token from authorization server", e); - } - } - - private OrcidBearerTokenAuthentication buildAuthentication(String accessTokenValue, JSONObject tokenInfo) throws JSONException { - String clientId = tokenInfo.getString("client_id"); - String userOrcid = tokenInfo.getString("username"); - - OrcidBearerTokenAuthentication.Builder builder = OrcidBearerTokenAuthentication.builder(clientId, userOrcid, accessTokenValue); - - Set scopes = Arrays.stream(tokenInfo.getString("scope").split("[\\s,]+")) - .collect(Collectors.toSet()); - builder.scopes(scopes); - - // Set granted authorities - if(tokenInfo.has("clientGrantedAuthority")) { - builder.authorities(Sets.newHashSet(new SimpleGrantedAuthority(tokenInfo.getString("clientGrantedAuthority")))); - } - - if(tokenInfo.has("OBO_CLIENT_ID")) { - String oboClientId = tokenInfo.getString("OBO_CLIENT_ID"); - builder.oboClientId(oboClientId); - } - - return builder.build(); - } -} diff --git a/orcid-internal-api/src/main/resources/orcid-internal-api-security-context.xml b/orcid-internal-api/src/main/resources/orcid-internal-api-security-context.xml index 08ec77e2ea4..d73bb9b047a 100644 --- a/orcid-internal-api/src/main/resources/orcid-internal-api-security-context.xml +++ b/orcid-internal-api/src/main/resources/orcid-internal-api-security-context.xml @@ -63,20 +63,7 @@ - - - - - - - - - - - - - - + diff --git a/orcid-internal-api/src/main/webapp/WEB-INF/web.xml b/orcid-internal-api/src/main/webapp/WEB-INF/web.xml index 77461f9016c..c880a1f7026 100644 --- a/orcid-internal-api/src/main/webapp/WEB-INF/web.xml +++ b/orcid-internal-api/src/main/webapp/WEB-INF/web.xml @@ -74,16 +74,6 @@ /* - - clientCredentialsTokenEndpointFilter - org.springframework.web.filter.DelegatingFilterProxy - - - - clientCredentialsTokenEndpointFilter - /oauth/token - - springSecurityFilterChain org.springframework.web.filter.DelegatingFilterProxy diff --git a/orcid-pub-web/src/main/java/org/orcid/api/publicV2/server/delegator/impl/PublicV2ApiServiceDelegatorImpl.java b/orcid-pub-web/src/main/java/org/orcid/api/publicV2/server/delegator/impl/PublicV2ApiServiceDelegatorImpl.java index 3bb95275b73..3181c490f2e 100644 --- a/orcid-pub-web/src/main/java/org/orcid/api/publicV2/server/delegator/impl/PublicV2ApiServiceDelegatorImpl.java +++ b/orcid-pub-web/src/main/java/org/orcid/api/publicV2/server/delegator/impl/PublicV2ApiServiceDelegatorImpl.java @@ -150,9 +150,6 @@ public class PublicV2ApiServiceDelegatorImpl @Resource private ContributorUtils contributorUtilsReadOnly; - @Resource - private RecordManager recordManager; - @Resource private SourceUtils sourceUtils; @@ -168,9 +165,6 @@ public class PublicV2ApiServiceDelegatorImpl @Resource private LocaleManager localeManager; - @Resource - private OpenIDConnectKeyService openIDConnectKeyService; - @Resource private ClientManagerReadOnly clientManagerReadOnly; @@ -180,12 +174,6 @@ public class PublicV2ApiServiceDelegatorImpl @Resource private EventManager eventManager; - @Resource - private EmailDomainManager emailDomainManager; - - @Value("${org.orcid.core.baseUri}") - private String baseUrl; - @Override public Response viewStatusText() { return Response.ok(STATUS_OK_MESSAGE).build(); diff --git a/orcid-pub-web/src/main/java/org/orcid/api/publicV2/server/delegator/impl/PublicV2ApiServiceVersionedDelegatorImpl.java b/orcid-pub-web/src/main/java/org/orcid/api/publicV2/server/delegator/impl/PublicV2ApiServiceVersionedDelegatorImpl.java index 37f2da1489a..82698b5fcfb 100644 --- a/orcid-pub-web/src/main/java/org/orcid/api/publicV2/server/delegator/impl/PublicV2ApiServiceVersionedDelegatorImpl.java +++ b/orcid-pub-web/src/main/java/org/orcid/api/publicV2/server/delegator/impl/PublicV2ApiServiceVersionedDelegatorImpl.java @@ -27,16 +27,10 @@ public class PublicV2ApiServiceVersionedDelegatorImpl implements PublicV2ApiServ @Resource private V2VersionConverterChain v2_1VersionConverterChain; - - @Resource - private ProfileEntityCacheManager profileEntityCacheManager; @Resource private OrcidSecurityManager orcidSecurityManager; - - @Resource - private EventManager eventManager; - + @Override public Response viewStatusText() { return publicV2ApiServiceDelegator.viewStatusText(); diff --git a/orcid-pub-web/src/main/java/org/orcid/api/publicV3/server/delegator/impl/PublicV3ApiServiceDelegatorImpl.java b/orcid-pub-web/src/main/java/org/orcid/api/publicV3/server/delegator/impl/PublicV3ApiServiceDelegatorImpl.java index 4b19fed80f3..972391ca771 100644 --- a/orcid-pub-web/src/main/java/org/orcid/api/publicV3/server/delegator/impl/PublicV3ApiServiceDelegatorImpl.java +++ b/orcid-pub-web/src/main/java/org/orcid/api/publicV3/server/delegator/impl/PublicV3ApiServiceDelegatorImpl.java @@ -181,9 +181,6 @@ public class PublicV3ApiServiceDelegatorImpl implements @Resource(name = "clientDetailsManagerReadOnlyV3") private ClientDetailsManagerReadOnly clientDetailsManagerReadOnly; - @Resource - private OpenIDConnectKeyService openIDConnectKeyService; - @Resource private StatusManager statusManager; @@ -193,15 +190,6 @@ public class PublicV3ApiServiceDelegatorImpl implements @Resource private EventManager eventManager; - @Resource - private EmailDomainManager emailDomainManager; - - @Resource - private SourceEntityUtils sourceEntityUtils; - - @Value("${org.orcid.core.baseUri}") - private String baseUrl; - private Boolean filterVersionOfIdentifiers = false; public Boolean getFilterVersionOfIdentifiers() { diff --git a/orcid-pub-web/src/main/resources/orcid-t1-security-context.xml b/orcid-pub-web/src/main/resources/orcid-t1-security-context.xml index ba149e70628..7f0374fe4e8 100644 --- a/orcid-pub-web/src/main/resources/orcid-t1-security-context.xml +++ b/orcid-pub-web/src/main/resources/orcid-t1-security-context.xml @@ -2,12 +2,8 @@ + http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-5.6.xsd"> @@ -15,77 +11,27 @@ - - - - - - - - - + - - + + - - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - + + diff --git a/orcid-pub-web/src/main/resources/orcid-t1-web-context.xml b/orcid-pub-web/src/main/resources/orcid-t1-web-context.xml index 4189a06e33c..73ca81c1a98 100644 --- a/orcid-pub-web/src/main/resources/orcid-t1-web-context.xml +++ b/orcid-pub-web/src/main/resources/orcid-t1-web-context.xml @@ -12,7 +12,7 @@ - + @@ -30,7 +30,6 @@ - diff --git a/orcid-pub-web/src/main/webapp/WEB-INF/web.xml b/orcid-pub-web/src/main/webapp/WEB-INF/web.xml index 2d6ae77b317..1fdd778917a 100644 --- a/orcid-pub-web/src/main/webapp/WEB-INF/web.xml +++ b/orcid-pub-web/src/main/webapp/WEB-INF/web.xml @@ -136,16 +136,6 @@ /* - - clientCredentialsTokenEndpointFilter - org.springframework.web.filter.DelegatingFilterProxy - - - - clientCredentialsTokenEndpointFilter - /oauth/token - - rateLimitingFilter org.springframework.web.filter.DelegatingFilterProxy @@ -179,7 +169,6 @@ clientIdAttributeFilter * - jersey diff --git a/orcid-web/src/main/resources/orcid-frontend-security.xml b/orcid-web/src/main/resources/orcid-frontend-security.xml index a6df4fa3525..d78c36bddfa 100644 --- a/orcid-web/src/main/resources/orcid-frontend-security.xml +++ b/orcid-web/src/main/resources/orcid-frontend-security.xml @@ -453,23 +453,8 @@ - - - - - - - - - - - - - - - diff --git a/orcid-web/src/main/webapp/WEB-INF/web.xml b/orcid-web/src/main/webapp/WEB-INF/web.xml index 7edb221dfb9..7281f48a729 100644 --- a/orcid-web/src/main/webapp/WEB-INF/web.xml +++ b/orcid-web/src/main/webapp/WEB-INF/web.xml @@ -82,16 +82,6 @@ org.springframework.web.filter.DelegatingFilterProxy - - clientCredentialsTokenEndpointFilter - org.springframework.web.filter.DelegatingFilterProxy - - - - clientCredentialsTokenEndpointFilter - /oauth/token - - springSecurityFilterChain /* From 54c1105c0137d60bc1465d0e0c668834df6ef170 Mon Sep 17 00:00:00 2001 From: amontenegro Date: Tue, 31 Mar 2026 19:20:51 -0600 Subject: [PATCH 16/16] Working on the internal API --- .../resources/orcid-internal-api-context.xml | 2 +- .../orcid-internal-api-security-context.xml | 60 +++---------------- .../resources/orcid-t1-security-context.xml | 3 +- 3 files changed, 10 insertions(+), 55 deletions(-) diff --git a/orcid-internal-api/src/main/resources/orcid-internal-api-context.xml b/orcid-internal-api/src/main/resources/orcid-internal-api-context.xml index 7b24cf1c971..a7d4519e761 100644 --- a/orcid-internal-api/src/main/resources/orcid-internal-api-context.xml +++ b/orcid-internal-api/src/main/resources/orcid-internal-api-context.xml @@ -10,7 +10,7 @@ - + diff --git a/orcid-internal-api/src/main/resources/orcid-internal-api-security-context.xml b/orcid-internal-api/src/main/resources/orcid-internal-api-security-context.xml index d73bb9b047a..786b72f79da 100644 --- a/orcid-internal-api/src/main/resources/orcid-internal-api-security-context.xml +++ b/orcid-internal-api/src/main/resources/orcid-internal-api-security-context.xml @@ -2,72 +2,28 @@ + http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-5.6.xsd"> - - - - - - - - - + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - - - - \ No newline at end of file diff --git a/orcid-pub-web/src/main/resources/orcid-t1-security-context.xml b/orcid-pub-web/src/main/resources/orcid-t1-security-context.xml index 7f0374fe4e8..361c36c5934 100644 --- a/orcid-pub-web/src/main/resources/orcid-t1-security-context.xml +++ b/orcid-pub-web/src/main/resources/orcid-t1-security-context.xml @@ -13,8 +13,7 @@ - - +