Skip to content

Commit

Permalink
Allow user revoked tokens to delete (#7060)
Browse files Browse the repository at this point in the history
* Deactivated records should get 409 on GET requests

* On OBO, When a token is user disabled, it should be possible to use the short lived token to delete

* More unit tests
  • Loading branch information
amontenegro authored Aug 4, 2024
1 parent e4ca907 commit 37db49a
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 6 deletions.
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 @@ -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

0 comments on commit 37db49a

Please sign in to comment.