Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@
import org.wso2.carbon.identity.oauth2.dto.OAuth2AccessTokenReqDTO;
import org.wso2.carbon.identity.oauth2.internal.OAuth2ServiceComponentHolder;
import org.wso2.carbon.identity.oauth2.model.AccessTokenDO;
import org.wso2.carbon.identity.oauth2.model.AccessTokenExtendedAttributes;
import org.wso2.carbon.identity.oauth2.model.RefreshTokenValidationDataDO;
import org.wso2.carbon.identity.oauth2.token.OAuthTokenReqMessageContext;
import org.wso2.carbon.identity.oauth2.util.OAuth2Util;
import org.wso2.carbon.identity.openidconnect.OIDCClaimUtil;

import java.sql.Timestamp;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;

Expand Down Expand Up @@ -118,6 +120,16 @@ public AccessTokenDO createAccessTokenBean(OAuthTokenReqMessageContext tokReqMsg
tokReqMsgCtx.setConsentedToken(true);
}
}
if (log.isDebugEnabled()) {
log.debug("Setting access token extended attributes for token request in refresh token flow for client: "
+ tokenReq.getClientId() + " with token id: " + tokenId);
}
if (tokenReq.getAccessTokenExtendedAttributes() != null &&
tokenReq.getAccessTokenExtendedAttributes().getParameters() != null) {
accessTokenDO.setAccessTokenExtendedAttributes(
new AccessTokenExtendedAttributes(
new HashMap<>(tokenReq.getAccessTokenExtendedAttributes().getParameters())));
}
return accessTokenDO;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public static class TokenTypes {
public static final String OAUTH_CODE_PERSISTENCE_ENABLE = "OAuth.EnableAuthCodePersistence";
public static final String OAUTH_ENABLE_REVOKE_TOKEN_HEADERS = "OAuth.EnableRevokeTokenHeadersInResponse";
public static final String IMPERSONATED_REFRESH_TOKEN_ENABLE = "OAuth.ImpersonatedRefreshToken.Enable";
public static final boolean DEFAULT_IMPERSONATED_REFRESH_TOKEN_ENABLED = true;
public static final String CONSOLE_CALLBACK_URL_FROM_SERVER_CONFIGS = "Console.CallbackURL";
public static final String MY_ACCOUNT_CALLBACK_URL_FROM_SERVER_CONFIGS = "MyAccount.CallbackURL";
public static final String TENANT_DOMAIN_PLACEHOLDER = "{TENANT_DOMAIN}";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,10 @@ protected JWTClaimsSet createJWTClaimSet(OAuthAuthzReqMessageContext authAuthzRe
tokenReqMessageContext.getOauth2AccessTokenReqDTO().getAccessTokenExtendedAttributes()
.getParameters();
if (customClaims != null && !customClaims.isEmpty()) {
customClaims.remove(OAuthConstants.IMPERSONATING_ACTOR);
if (log.isDebugEnabled()) {
log.debug("Processing custom claims for JWT token. Total claims count: " + customClaims.size());
}
for (Map.Entry<String, String> entry : customClaims.entrySet()) {
jwtClaimsSetBuilder.claim(entry.getKey(), entry.getValue());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@
import static org.wso2.carbon.identity.oauth.common.OAuthConstants.TokenBindings.NONE;
import static org.wso2.carbon.identity.oauth.common.OAuthConstants.TokenStates.TOKEN_STATE_ACTIVE;
import static org.wso2.carbon.identity.oauth.config.OAuthServerConfiguration.JWT_TOKEN_TYPE;
import static org.wso2.carbon.identity.oauth2.OAuth2Constants.IMPERSONATED_REFRESH_TOKEN_ENABLE;
import static org.wso2.carbon.identity.oauth2.util.OAuth2Util.EXTENDED_REFRESH_TOKEN_DEFAULT_TIME;
import static org.wso2.carbon.identity.oauth2.util.OAuth2Util.JWT;

Expand Down Expand Up @@ -937,8 +936,13 @@ private OAuth2AccessTokenRespDTO createResponseWithTokenBean(OAuthTokenReqMessag
}
if (supportedGrantTypes.contains(OAuthConstants.GrantTypes.REFRESH_TOKEN)) {
if (!tokenReqMessageContext.isImpersonationRequest()
|| Boolean.parseBoolean(IdentityUtil.getProperty(IMPERSONATED_REFRESH_TOKEN_ENABLE))) {
|| OAuth2Util.isImpersonatedRefreshTokenEnabled()) {
tokenRespDTO.setRefreshToken(existingAccessTokenDO.getRefreshToken());
} else {
if (log.isDebugEnabled()) {
log.debug("Impersonation request is not allowed to have a refresh token for client_id : " +
consumerKey + ", therefore not issuing a refresh token.");
}
}
} else {
if (log.isDebugEnabled()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ private void setPropertiesForTokenGeneration(OAuthTokenReqMessageContext tokReqM
tokReqMsgCtx.setScope(validationBean.getScope());
tokReqMsgCtx.getOauth2AccessTokenReqDTO().setAccessTokenExtendedAttributes(
validationBean.getAccessTokenExtendedAttributes());
propagateImpersonationInfo(tokReqMsgCtx);
if (StringUtils.isNotBlank(validationBean.getTokenBindingReference()) && !NONE
.equals(validationBean.getTokenBindingReference())) {
Optional<TokenBinding> tokenBindingOptional = OAuthTokenPersistenceFactory.getInstance()
Expand Down Expand Up @@ -306,6 +307,24 @@ private void setPropertiesForTokenGeneration(OAuthTokenReqMessageContext tokReqM
}
}

private void propagateImpersonationInfo(OAuthTokenReqMessageContext tokenReqMessageContext) {

log.debug("Checking for impersonation information in token request");
if (tokenReqMessageContext != null && tokenReqMessageContext.getOauth2AccessTokenReqDTO() != null &&
tokenReqMessageContext.getOauth2AccessTokenReqDTO().getAccessTokenExtendedAttributes() != null) {
String impersonator = tokenReqMessageContext.getOauth2AccessTokenReqDTO()
.getAccessTokenExtendedAttributes().getParameters()
.get(OAuthConstants.IMPERSONATING_ACTOR);
if (StringUtils.isNotBlank(impersonator)) {
tokenReqMessageContext.setImpersonationRequest(true);
tokenReqMessageContext.addProperty(OAuthConstants.IMPERSONATING_ACTOR, impersonator);
if (log.isDebugEnabled()) {
log.debug("Impersonation request identified for the user: " + impersonator);
}
}
}
}

/**
* Return session context identifier from authorization grant cache. For authorization code flow, we mapped it
* against auth_code. For refresh token grant, we map the cache against the access token.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5882,6 +5882,23 @@ public static HashSet<ClientAuthenticationMethodModel> getSupportedAuthenticatio
return supportedClientAuthMethods;
}

/**
* Check if impersonated refresh token is enabled.
*
* @return True if impersonated refresh token is enabled.
*/
public static boolean isImpersonatedRefreshTokenEnabled() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be a app level config?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we are not introducing app level config with this PR, cause for app level config we have to change API, UI and integration tests and it would take a week to do it


if (IdentityUtil.getProperty(OAuth2Constants.IMPERSONATED_REFRESH_TOKEN_ENABLE) != null) {
return Boolean.parseBoolean(IdentityUtil.getProperty(OAuth2Constants.IMPERSONATED_REFRESH_TOKEN_ENABLE));
}
if (log.isDebugEnabled()) {
log.debug("Impersonated refresh token configuration not found, " +
"using default: " + OAuth2Constants.DEFAULT_IMPERSONATED_REFRESH_TOKEN_ENABLED);
}
return OAuth2Constants.DEFAULT_IMPERSONATED_REFRESH_TOKEN_ENABLED;
}

/**
* Check if token persistence is enabled.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
import org.wso2.carbon.identity.oauth2.dto.OAuth2AccessTokenReqDTO;
import org.wso2.carbon.identity.oauth2.dto.OAuth2AuthorizeReqDTO;
import org.wso2.carbon.identity.oauth2.internal.OAuth2ServiceComponentHolder;
import org.wso2.carbon.identity.oauth2.model.AccessTokenExtendedAttributes;
import org.wso2.carbon.identity.oauth2.token.bindings.TokenBinding;
import org.wso2.carbon.identity.oauth2.token.handlers.claims.JWTAccessTokenClaimProvider;
import org.wso2.carbon.identity.oauth2.token.handlers.grant.AuthorizationGrantHandler;
Expand Down Expand Up @@ -335,6 +336,13 @@ public Object[][] provideClaimSetData() {
OAuth2AccessTokenReqDTO tokenReqDTO = new OAuth2AccessTokenReqDTO();
tokenReqDTO.setGrantType(APPLICATION_ACCESS_TOKEN_GRANT_TYPE);
tokenReqDTO.setTenantDomain("super.wso2");
AccessTokenExtendedAttributes accessTokenExtendedAttributes = new AccessTokenExtendedAttributes();
accessTokenExtendedAttributes.setExtendedToken(true);
HashMap<String, String> params = new HashMap<>();
params.put("testExtendingKey", "testExtendingValue");
params.put(OAuthConstants.IMPERSONATING_ACTOR, "DUMMY_ACTOR");
accessTokenExtendedAttributes.setParameters(params);
tokenReqDTO.setAccessTokenExtendedAttributes(accessTokenExtendedAttributes);
OAuthTokenReqMessageContext tokenReqMessageContext = new OAuthTokenReqMessageContext(tokenReqDTO);
AuthenticatedUser authenticatedUserForTokenReq = new AuthenticatedUser(authenticatedUserForAuthz);
tokenReqMessageContext.setAuthorizedUser(authenticatedUserForTokenReq);
Expand Down Expand Up @@ -438,6 +446,12 @@ public void testCreateJWTClaimSet(Object authzReqMessageContext,
assertNotNull(jwtClaimSet.getIssueTime());
assertNotNull(jwtClaimSet.getExpirationTime());

if (tokenReqMessageContext != null) {
assertNotNull(jwtClaimSet.getClaim("testExtendingKey"));
assertEquals(jwtClaimSet.getClaim("testExtendingKey"), "testExtendingValue");
assertNull(jwtClaimSet.getClaim(OAuthConstants.IMPERSONATING_ACTOR));
}

if (tokenReqMessageContext != null
&& ((OAuthTokenReqMessageContext) tokenReqMessageContext).getProperty(EXPIRY_TIME_JWT)
!= null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3177,6 +3177,26 @@ public void testGetAppResidentTenantDomain(String appResidentOrgId, String expec
assertEquals(OAuth2Util.getAppResidentTenantDomain(), expected);
}

@DataProvider(name = "impersonatedRefreshTokenEnabledProvider")
public Object[][] impersonatedRefreshTokenEnabledProvider() {
return new Object[][]{
{"true", true},
{"false", false},
{null, true}
};
}

@Test(dataProvider = "impersonatedRefreshTokenEnabledProvider")
public void testIsImpersonatedRefreshTokenEnabled(String configValue, boolean expected) {

try (MockedStatic<IdentityUtil> identityUtil = mockStatic(IdentityUtil.class)) {

identityUtil.when(() -> IdentityUtil.getProperty(OAuth2Constants.IMPERSONATED_REFRESH_TOKEN_ENABLE))
.thenReturn(configValue);
assertEquals(OAuth2Util.isImpersonatedRefreshTokenEnabled(), expected);
}
}

@Test(expectedExceptions = IdentityOAuth2Exception.class,
expectedExceptionsMessageRegExp = "Error occurred while resolving the tenant domain for the " +
"organization id.")
Expand Down