Skip to content

Commit

Permalink
impersonated audit
Browse files Browse the repository at this point in the history
  • Loading branch information
Thumimku committed May 13, 2024
1 parent cb40459 commit d4a358b
Show file tree
Hide file tree
Showing 4 changed files with 190 additions and 12 deletions.
8 changes: 8 additions & 0 deletions components/org.wso2.carbon.identity.auth.service/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,14 @@
<groupId>org.wso2.carbon.identity.event.handler.accountlock</groupId>
<artifactId>org.wso2.carbon.identity.handler.event.account.lock</artifactId>
</dependency>
<dependency>
<groupId>org.json.wso2</groupId>
<artifactId>json</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon.identity.framework</groupId>
<artifactId>org.wso2.carbon.identity.central.log.mgt</artifactId>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,16 @@

package org.wso2.carbon.identity.auth.service.handler.impl;

import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import org.apache.catalina.connector.Request;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpHeaders;
import org.json.JSONObject;
import org.slf4j.MDC;
import org.wso2.carbon.CarbonConstants;
import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser;
import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants;
import org.wso2.carbon.identity.application.common.model.ProvisioningServiceProviderType;
Expand Down Expand Up @@ -51,14 +55,11 @@
import org.wso2.carbon.identity.oauth2.util.OAuth2Util;
import org.wso2.carbon.identity.oauth2.validators.RefreshTokenValidator;

import java.text.ParseException;
import java.util.Map;
import java.util.Optional;

import static org.wso2.carbon.identity.auth.service.util.AuthConfigurationUtil.isAuthHeaderMatch;
import static org.wso2.carbon.identity.auth.service.util.Constants.AUTHENTICATION_TYPE;
import static org.wso2.carbon.identity.auth.service.util.Constants.IDP_NAME;
import static org.wso2.carbon.identity.auth.service.util.Constants.IS_FEDERATED_USER;
import static org.wso2.carbon.identity.auth.service.util.Constants.OAUTH2_ALLOWED_SCOPES;
import static org.wso2.carbon.identity.auth.service.util.Constants.OAUTH2_VALIDATE_SCOPE;
import static org.wso2.carbon.identity.oauth2.OAuth2Constants.TokenBinderType.SSO_SESSION_BASED_TOKEN_BINDER;

/**
Expand All @@ -69,6 +70,7 @@
public class OAuth2AccessTokenHandler extends AuthenticationHandler {

private static final Log log = LogFactory.getLog(OAuth2AccessTokenHandler.class);
private static final Log AUDIT = CarbonConstants.AUDIT_LOG;
private final String OAUTH_HEADER = "Bearer";
private final String CONSUMER_KEY = "consumer-key";
private final String SERVICE_PROVIDER = "serviceProvider";
Expand Down Expand Up @@ -117,7 +119,7 @@ protected AuthenticationResult doAuthenticate(MessageContext messageContext) {
oAuth2TokenValidationService.buildIntrospectionResponse(requestDTO);

IdentityUtil.threadLocalProperties.get()
.put(AUTHENTICATION_TYPE, oAuth2IntrospectionResponseDTO.getAut());
.put(Constants.AUTHENTICATION_TYPE, oAuth2IntrospectionResponseDTO.getAut());

if (!oAuth2IntrospectionResponseDTO.isActive() ||
RefreshTokenValidator.TOKEN_TYPE_NAME.equals(oAuth2IntrospectionResponseDTO.getTokenType())) {
Expand All @@ -138,29 +140,32 @@ protected AuthenticationResult doAuthenticate(MessageContext messageContext) {
return authenticationResult;
}

handleImpersonatedAccessToken(authenticationContext, accessToken, oAuth2IntrospectionResponseDTO);

authenticationResult.setAuthenticationStatus(AuthenticationStatus.SUCCESS);

User authorizedUser = oAuth2IntrospectionResponseDTO.getAuthorizedUser();
if (authorizedUser != null) {
authenticationContext.setUser(authorizedUser);
if (authorizedUser instanceof AuthenticatedUser) {
IdentityUtil.threadLocalProperties.get()
.put(IS_FEDERATED_USER, ((AuthenticatedUser) authorizedUser).isFederatedUser());
.put(Constants.IS_FEDERATED_USER,
((AuthenticatedUser) authorizedUser).isFederatedUser());
IdentityUtil.threadLocalProperties.get()
.put(IDP_NAME, ((AuthenticatedUser) authorizedUser).getFederatedIdPName());
.put(Constants.IDP_NAME, ((AuthenticatedUser) authorizedUser).getFederatedIdPName());
} else {
AuthenticatedUser authenticatedUser = new AuthenticatedUser(authorizedUser);
IdentityUtil.threadLocalProperties.get()
.put(IS_FEDERATED_USER, authenticatedUser.isFederatedUser());
.put(Constants.IS_FEDERATED_USER, authenticatedUser.isFederatedUser());
IdentityUtil.threadLocalProperties.get()
.put(IDP_NAME, authenticatedUser.getFederatedIdPName());
.put(Constants.IDP_NAME, authenticatedUser.getFederatedIdPName());
}
}

authenticationContext.addParameter(CONSUMER_KEY, oAuth2IntrospectionResponseDTO.getClientId());
authenticationContext.addParameter(OAUTH2_ALLOWED_SCOPES,
authenticationContext.addParameter(Constants.OAUTH2_ALLOWED_SCOPES,
OAuth2Util.buildScopeArray(oAuth2IntrospectionResponseDTO.getScope()));
authenticationContext.addParameter(OAUTH2_VALIDATE_SCOPE,
authenticationContext.addParameter(Constants.OAUTH2_VALIDATE_SCOPE,
AuthConfigurationUtil.getInstance().isScopeValidationEnabled());
String serviceProvider = null;
try {
Expand Down Expand Up @@ -197,6 +202,77 @@ protected AuthenticationResult doAuthenticate(MessageContext messageContext) {
return authenticationResult;
}

private void handleImpersonatedAccessToken(AuthenticationContext authenticationContext,
String accessToken,
OAuth2IntrospectionResponseDTO introspectionResponseDTO) {

try {
// Extract claims from the access token
SignedJWT signedJWT = getSignedJWT(accessToken);
JWTClaimsSet claimsSet = getClaimSet(signedJWT);
String subject = resolveSubject(claimsSet);
String impersonator = resolveImpersonator(claimsSet);

// Check if the token represents an impersonation request
if (impersonator != null) {
String scope = introspectionResponseDTO.getScope();
String clientId = introspectionResponseDTO.getClientId();
String requestUri = authenticationContext.getAuthenticationRequest().getRequestUri();
String httpMethod = authenticationContext.getAuthenticationRequest().getMethod();

// Ensure it's not a GET request before logging
if (!Constants.GET.equals(httpMethod)) {
// Prepare data for audit log
JSONObject data = new JSONObject();
data.put(Constants.SUBJECT, subject);
data.put(Constants.IMPERSONATOR, impersonator);
data.put(Constants.RESOURCE_PATH, requestUri);
data.put(Constants.HTTP_METHOD, httpMethod);
data.put(Constants.CLIENT_ID, clientId);
data.put(Constants.SCOPE, scope);

String action;

switch (httpMethod) {
case Constants.PATCH:
action = Constants.IMPERSONATION_RESOURCE_MODIFICATION;
break;
case Constants.POST:
action = Constants.IMPERSONATION_RESOURCE_CREATION;
break;
case Constants.DELETE:
action = Constants.IMPERSONATION_RESOURCE_DELETION;
break;
default:
action = Constants.IMPERSONATION_RESOURCE_ACCESS;
break;
}
// Log the audit event
AUDIT.info(createAuditMessage(impersonator, action, subject, data, Constants.AUTHORIZED));
}
}
} catch (IdentityOAuth2Exception e) {
// Ignore IdentityOAuth2Exception since this is an audit log section
}
}

/**
* To create an audit message based on provided parameters.
*
* @param action Activity
* @param target Target affected by this activity.
* @param data Information passed along with the request.
* @param resultField Result value.
* @return Relevant audit log in Json format.
*/
private String createAuditMessage(String subject, String action, String target, JSONObject data, String resultField) {

String auditMessage =
Constants.INITIATOR + "=%s " + Constants.ACTION + "=%s " + Constants.TARGET + "=%s "
+ Constants.DATA + "=%s " + Constants.OUTCOME + "=%s";
return String.format(auditMessage, subject, action, target, data, resultField);
}

@Override
public void init(InitConfig initConfig) {

Expand Down Expand Up @@ -385,4 +461,66 @@ private void setProvisioningServiceProviderThreadLocal(String oauthAppConsumerKe
IdentityApplicationManagementUtil.setThreadLocalProvisioningServiceProvider(provisioningServiceProvider);
}
}

/**
* Get the SignedJWT by parsing the subjectToken.
*
* @param token Token sent in the request
* @return SignedJWT
* @throws IdentityOAuth2Exception Error when parsing the subjectToken
*/
private SignedJWT getSignedJWT(String token) throws IdentityOAuth2Exception {

SignedJWT signedJWT;
if (StringUtils.isEmpty(token)) {
return null;
}
try {
signedJWT = SignedJWT.parse(token);
} catch (ParseException e) {
throw new IdentityOAuth2Exception("Error while parsing the JWT", e);
}
return signedJWT;
}

/**
* Retrieve the JWTClaimsSet from the SignedJWT.
*
* @param signedJWT SignedJWT object
* @return JWTClaimsSet
* @throws IdentityOAuth2Exception Error when retrieving the JWTClaimsSet
*/
public static JWTClaimsSet getClaimSet(SignedJWT signedJWT) throws IdentityOAuth2Exception {

JWTClaimsSet claimsSet = null;
try {
claimsSet = signedJWT.getJWTClaimsSet();
} catch (ParseException e) {
throw new IdentityOAuth2Exception("Error when retrieving claimsSet from the JWT", e);
}
return claimsSet;
}

/**
* The default implementation creates the subject from the Sub attribute.
* To translate between the federated and local user store, this may need some mapping.
* Override if needed
*
* @param claimsSet all the JWT claims
* @return The subject, to be used
*/
private String resolveSubject(JWTClaimsSet claimsSet) {

return claimsSet.getSubject();
}

private String resolveImpersonator(JWTClaimsSet claimsSet) {

if (claimsSet.getClaim(Constants.ACT) != null) {

Map<String, String> mayActClaimSet = (Map) claimsSet.getClaim(Constants.ACT);
return mayActClaimSet.get(Constants.SUB);
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,30 @@ public class Constants {
public static final String RESOURCE_ACCESS_CONTROL_V2_FILE = "resource-access-control-v2.xml";
public static final String AUTHENTICATION_TYPE = "authenticationType";
public final static String VALIDATE_LEGACY_PERMISSIONS = "validateLegacyPermissions";

// Audit Log Constants.
public static final String INITIATOR = "Initiator";
public static final String ACTION = "Action";
public static final String TARGET = "Target";
public static final String DATA = "Data";
public static final String OUTCOME = "Outcome";

// Impersonation Constants.
public static final String ACT = "act";
public static final String SUB = "sub";
public static final String GET = "GET";
public static final String POST = "POST";
public static final String PATCH = "PATCH";
public static final String DELETE = "DELETE";
public static final String AUTHORIZED = "AUTHORIZED";
public static final String IMPERSONATION_RESOURCE_MODIFICATION = "resource-modification-via-impersonation";
public static final String IMPERSONATION_RESOURCE_ACCESS = "resource-access-via-impersonation";
public static final String IMPERSONATION_RESOURCE_DELETION = "resource-deletion-via-impersonation";
public static final String IMPERSONATION_RESOURCE_CREATION = "resource-creation-via-impersonation";
public static final String SUBJECT = "subject";
public static final String IMPERSONATOR = "impersonator";
public static final String RESOURCE_PATH = "ResourcePath";
public static final String HTTP_METHOD = "httpMethod";
public static final String CLIENT_ID = "clientId";
public static final String SCOPE = "scope";
}
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,12 @@
<version>${identity.framework.version}</version>
</dependency>

<dependency>
<groupId>org.wso2.carbon.identity.framework</groupId>
<artifactId>org.wso2.carbon.identity.central.log.mgt</artifactId>
<version>${identity.framework.version}</version>
</dependency>

<dependency>
<groupId>org.wso2.carbon.identity.auth.rest</groupId>
<artifactId>org.wso2.carbon.identity.auth.service</artifactId>
Expand Down

0 comments on commit d4a358b

Please sign in to comment.