Skip to content

Commit

Permalink
Merge branch 'main' into add-email-domains
Browse files Browse the repository at this point in the history
  • Loading branch information
auumgn committed Aug 8, 2024
2 parents 3dd0985 + 3aec5ec commit c8c117e
Show file tree
Hide file tree
Showing 11 changed files with 429 additions and 332 deletions.
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
## v2.63.3 - 2024-08-06

[Full Changelog](https://github.com/ORCID/ORCID-Source/compare/v2.63.2...v2.63.3)

## v2.63.2 - 2024-08-05

[Full Changelog](https://github.com/ORCID/ORCID-Source/compare/v2.63.1...v2.63.2)

## v2.63.1 - 2024-08-05

[Full Changelog](https://github.com/ORCID/ORCID-Source/compare/v2.63.0...v2.63.1)

## v2.63.0 - 2024-08-04

[Full Changelog](https://github.com/ORCID/ORCID-Source/compare/v2.62.6...v2.63.0)

## v2.62.6 - 2024-08-04

[Full Changelog](https://github.com/ORCID/ORCID-Source/compare/v2.62.5...v2.62.6)

## v2.62.5 - 2024-07-24

[Full Changelog](https://github.com/ORCID/ORCID-Source/compare/v2.62.4...v2.62.5)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ public Response toResponse(Throwable t) {
logShortError(t, clientId);
} else if (t instanceof InvalidPutCodeException) {
logShortError(t, clientId);
} else if (t instanceof MismatchedPutCodeException) {
logShortError(t, clientId);
} else {
LOGGER.error("An exception has occured processing request from client " + clientId, t);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,11 @@ public Response obtainOauth2Token(String authorization, MultivaluedMap<String, S
}
}

// 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);
setToCache(client.getName(), 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);
Expand Down Expand Up @@ -307,6 +310,8 @@ protected void removeMetadataFromToken(OAuth2AccessToken accessToken) {
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);
}
}

Expand Down
4 changes: 2 additions & 2 deletions orcid-api-web/tutorial/refresh_tokens.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ Example access token request response with access and refresh token:
Create a new token with the same scopes and expiration and revoke the old token

```
curl -d 'refresh_token=4470d1ff-c817-45c1-86d1-b9062669c7cb' -d 'grant_type=refresh_token' -d 'client_id=APP-5GG5N5YFOKGV5N0X' -d 'client_secret=ce4db92f-d535-4014-a250-b5cdc27c0984' -d 'revoke_old=true' https://api.sandbox.orcid.org/oauth/token
curl -d 'refresh_token=4470d1ff-c817-45c1-86d1-b9062669c7cb' -d 'grant_type=refresh_token' -d 'client_id=APP-5GG5N5YFOKGV5N0X' -d 'client_secret=ce4db92f-d535-4014-a250-b5cdc27c0984' -d 'revoke_old=true' https://sandbox.orcid.org/oauth/token
```

Create a new token with a subset of scopes, expiring in one day and do not revoke the old token

```
curl -d 'refresh_token=4470d1ff-c817-45c1-86d1-b9062669c7cb' -d 'grant_type=refresh_token' -d 'client_id=APP-5GG5N5YFOKGV5N0X' -d 'client_secret=ce4db92f-d535-4014-a250-b5cdc27c0984' -d 'scope=/read-limited' -d 'expires_in=86400' https://api.sandbox.orcid.org/oauth/token
curl -d 'refresh_token=4470d1ff-c817-45c1-86d1-b9062669c7cb' -d 'grant_type=refresh_token' -d 'client_id=APP-5GG5N5YFOKGV5N0X' -d 'client_secret=ce4db92f-d535-4014-a250-b5cdc27c0984' -d 'scope=/read-limited' -d 'expires_in=86400' https://sandbox.orcid.org/oauth/token
```
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public class OrcidOauth2Constants {
public static final String IETF_EXCHANGE_SUBJECT_TOKEN = "subject_token";
public static final String IETF_EXCHANGE_SUBJECT_TOKEN_TYPE = "subject_token_type";
public static final String IETF_EXCHANGE_REQUESTED_TOKEN_TYPE = "requested_token_type";
public static final String TOKEN_DISABLED = "token_disabled";

public static final String CODE_RESPONSE_TYPE = "code";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@
*/
public class IETFExchangeTokenGranter implements TokenGranter {

public static final String IETF_EXCHANGE = "urn:ietf:params:oauth:grant-type:token-exchange";
private AuthorizationServerTokenServices tokenServices;

@Resource(name = "orcidOauth2AuthoriziationCodeDetailDao")
Expand All @@ -75,7 +74,7 @@ public class IETFExchangeTokenGranter implements TokenGranter {
@Resource
OpenIDConnectTokenEnhancer openIDConnectTokenEnhancer;

private List<String> doNotAllowDeleteOnTheseRevokeReasons = List.of(RevokeReason.CLIENT_REVOKED.name(), RevokeReason.STAFF_REVOKED.name());
private final List<RevokeReason> doNotAllowDeleteOnTheseRevokeReasons = List.of(RevokeReason.CLIENT_REVOKED, RevokeReason.STAFF_REVOKED, RevokeReason.RECORD_DEACTIVATED, RevokeReason.AUTH_CODE_REUSED);

public IETFExchangeTokenGranter(AuthorizationServerTokenServices tokenServices) {
this.tokenServices = tokenServices;
Expand Down Expand Up @@ -239,10 +238,12 @@ private OAuth2AccessToken generateAccessToken(TokenRequest tokenRequest, String
Set<ScopePathType> inactiveScopesOBO = Sets.newHashSet();
boolean issueRevokedToken = false;
RevokeReason revokeReason = null;
// Lets consider token expiration time anything that goes beyond this date
Date now = new Date();
for (OrcidOauth2TokenDetail d : details) {
Set<ScopePathType> scopesInToken = ScopePathType.getScopesFromSpaceSeparatedString(d.getScope());
// If token is expired, we should ignore it
if (d.getTokenExpiration().after(new Date())) {
if (d.getTokenExpiration().after(now)) {
// If token is disabled, we should know if it have the /activities/update scope on it
if(d.getTokenDisabled() == null || !d.getTokenDisabled()) {
activeScopesOBO.addAll(scopesInToken);
Expand All @@ -257,8 +258,12 @@ private OAuth2AccessToken generateAccessToken(TokenRequest tokenRequest, String
// Keep only the /activities/update scope if the token was not revoked by a client or staff member
if(revokeReason == null || !doNotAllowDeleteOnTheseRevokeReasons.contains(revokeReason)) {
inactiveScopesOBO.add(ScopePathType.ACTIVITIES_UPDATE);
} else {
throw new OrcidInvalidScopeException("The id_token is disabled and does not contain any valid scope");
}
}
} else {
throw new OrcidInvalidScopeException("The id_token is disabled and does not contain any valid scope");
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

import com.nimbusds.jwt.SignedJWT;

import static org.orcid.core.constants.OrcidOauth2Constants.TOKEN_DISABLED;

public class IETFTokenExchangeResponse implements OAuth2AccessToken {

private Map<String, Object> additionalInformation = new HashMap<String, Object>();
Expand Down Expand Up @@ -47,6 +49,9 @@ public static IETFTokenExchangeResponse accessToken(OAuth2AccessToken accessTok
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");
}
return token;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
package org.orcid.core.oauth.service;

import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.*;

import javax.annotation.Resource;
import javax.persistence.PersistenceException;
Expand Down Expand Up @@ -465,6 +459,10 @@ public OAuth2AccessToken createRevokedAccessToken(OAuth2Authentication authentic
// 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -289,14 +289,14 @@ public void grantDisabledTokenDoesntWorkTest() throws NoSuchAlgorithmException,
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());
assertEquals("The id_token is disabled and does not contain any valid scope", oise.getMessage());
} catch (Exception e) {
fail();
}
}

@Test
public void grantDisabledTokenWithActivitiesReadLimitedGenerateDeactivatedTokenTest()
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());
Expand All @@ -309,6 +309,38 @@ public void grantDisabledTokenWithActivitiesReadLimitedGenerateDeactivatedTokenT
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("The id_token is disabled and does not contain any valid scope", 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("The id_token is disabled and does not contain any valid scope", e.getMessage());
} catch(Exception e) {
fail("Unhandled exception:" + e.getMessage());
}
}

@Test
public void grantDisabledTokenWithActivitiesUpdateAndOtherActiveTokenWithOtherScopesGenerateDeactivatedTokenTest()
throws NoSuchAlgorithmException, IOException, ParseException, URISyntaxException, JOSEException {
Expand Down
Loading

0 comments on commit c8c117e

Please sign in to comment.