Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow API calls on-behalf of associated accounts #92

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions components/org.wso2.carbon.identity.auth.service/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@
<groupId>org.wso2.carbon.identity.inbound.auth.oauth2</groupId>
<artifactId>org.wso2.carbon.identity.oauth</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon.identity.association.account</groupId>
<artifactId>org.wso2.carbon.identity.user.account.association</artifactId>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
package org.wso2.carbon.identity.auth.service.handler;

import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpHeaders;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.identity.application.common.model.User;
import org.wso2.carbon.identity.auth.service.AuthenticationContext;
Expand All @@ -28,10 +29,16 @@
import org.wso2.carbon.identity.auth.service.exception.AuthClientException;
import org.wso2.carbon.identity.auth.service.exception.AuthServerException;
import org.wso2.carbon.identity.auth.service.exception.AuthenticationFailException;
import org.wso2.carbon.identity.auth.service.internal.AuthenticationServiceHolder;
import org.wso2.carbon.identity.core.bean.context.MessageContext;
import org.wso2.carbon.identity.core.handler.AbstractIdentityMessageHandler;
import org.wso2.carbon.identity.core.handler.AbstractIdentityMessageHandler;
import org.wso2.carbon.identity.core.util.IdentityUtil;
import org.wso2.carbon.identity.user.account.association.UserAccountConnector;
import org.wso2.carbon.identity.user.account.association.dto.UserAccountAssociationDTO;
import org.wso2.carbon.identity.user.account.association.exception.UserAccountAssociationException;
import org.wso2.carbon.utils.multitenancy.MultitenantUtils;

import static org.wso2.carbon.utils.multitenancy.MultitenantUtils.getTenantDomain;

/**
* This is the abstract class for custom authentication handlers.
Expand All @@ -41,6 +48,9 @@
*/
public abstract class AuthenticationHandler extends AbstractIdentityMessageHandler {

private static final Log log = LogFactory.getLog(AuthenticationHandler.class);
private static final String ASSOCIATED_USER_ID_HEADER = "AssociatedUserId";

public int getPriority(MessageContext messageContext, int defaultValue) {

int priority = super.getPriority(messageContext);
Expand Down Expand Up @@ -101,10 +111,104 @@ protected void postAuthenticate(MessageContext messageContext, AuthenticationRes

if (user.getTenantDomain() != null && user.getTenantDomain().equalsIgnoreCase(PrivilegedCarbonContext
.getThreadLocalCarbonContext().getTenantDomain())) {
PrivilegedCarbonContext.getThreadLocalCarbonContext().setUsername(IdentityUtil.addDomainToName
(user.getUserName(), user.getUserStoreDomain()));

String associatedUserRequestHeader = authenticationContext.getAuthenticationRequest().
getHeader(ASSOCIATED_USER_ID_HEADER);
if (StringUtils.isNotEmpty(associatedUserRequestHeader)) {
User associatedUser = getAssociatedUser(user, associatedUserRequestHeader);
if (associatedUser == null) {
log.error("Cannot get the associated user for the provided header value: "
+ associatedUserRequestHeader);
setFailedAuthentication(authenticationResult);
return;
}
if (!user.equals(associatedUser)) {
setAssociatedUserInCarbonContext(user, associatedUser, authenticationResult);
return;
}
}
String domainAwareUserName = IdentityUtil.addDomainToName(user.getUserName(),
user.getUserStoreDomain());
setUserInCarbonContext(domainAwareUserName);
}
}
}
}

private User getAssociatedUser(User user, String associatedUserRequestHeader) {

if (!user.getTenantDomain().equals(getTenantDomain(associatedUserRequestHeader))) {
if (log.isDebugEnabled()) {
log.debug("Provided associated user: " + associatedUserRequestHeader + ", " +
"has a different tenant domain to the authenticated user's tenant domain: "
+ user.getTenantDomain());
}
return null;
}
return User.getUserFromUserName(associatedUserRequestHeader);
}

private void setAssociatedUserInCarbonContext(User user, User associatedUser,
AuthenticationResult authenticationResult) {

UserAccountConnector userAccountConnector = AuthenticationServiceHolder.getInstance().getUserAccountConnector();
if (userAccountConnector == null) {
log.error("Unable to get the UserAccountConnector service");
setFailedAuthentication(authenticationResult);
return;
}

if (!hasValidAssociation(user, associatedUser, userAccountConnector)) {
log.error("Associated user: " + associatedUser.toFullQualifiedUsername() + ", sent with the " +
"header: " + ASSOCIATED_USER_ID_HEADER + ", does not have a valid association with the " +
"user: " + user.toFullQualifiedUsername());
setFailedAuthentication(authenticationResult);
return;
}

if (log.isDebugEnabled()) {
log.debug("Setting the Associated user: " + associatedUser.toFullQualifiedUsername()
+ ", sent with the header: " + ASSOCIATED_USER_ID_HEADER + ", in the carbon " +
"context since it is a valid association of the user: " + user.toFullQualifiedUsername());
}
setUserInCarbonContext(MultitenantUtils.getTenantAwareUsername(associatedUser.toFullQualifiedUsername()));
}

private boolean hasValidAssociation(User user, User associatedUser, UserAccountConnector userAccountConnector) {

try {
UserAccountAssociationDTO[] userAccountAssociations = userAccountConnector.getAccountAssociationsOfUser(
user.toFullQualifiedUsername());
for (UserAccountAssociationDTO userAccountAssociation : userAccountAssociations) {
if (associatedUser.equals(getUser(userAccountAssociation))) {
return true;
}
}
} catch (UserAccountAssociationException e) {
if (log.isDebugEnabled()) {
log.debug("Error while getting account associations of the user: " + user.toFullQualifiedUsername(),
e);
}
}
return false;
}

private void setFailedAuthentication(AuthenticationResult authenticationResult) {

authenticationResult.setAuthenticationStatus(AuthenticationStatus.FAILED);
}

private User getUser(UserAccountAssociationDTO userAccountAssociationDTO) {

User user = new User();
user.setTenantDomain(userAccountAssociationDTO.getTenantDomain());
user.setUserStoreDomain(userAccountAssociationDTO.getDomain());
user.setUserName(userAccountAssociationDTO.getUsername());
return user;
}

private void setUserInCarbonContext(String domainAwareUserName) {

PrivilegedCarbonContext.getThreadLocalCarbonContext().setUsername(domainAwareUserName);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@
import org.wso2.carbon.identity.core.bean.context.MessageContext;
import org.wso2.carbon.identity.core.handler.InitConfig;
import org.wso2.carbon.identity.core.util.IdentityTenantUtil;
import org.wso2.carbon.identity.core.util.IdentityUtil;
import org.wso2.carbon.user.api.UserRealm;
import org.wso2.carbon.user.core.UserStoreManager;
import org.wso2.carbon.user.core.util.UserCoreUtil;
import org.wso2.carbon.utils.multitenancy.MultitenantUtils;

import java.nio.charset.Charset;
Expand Down Expand Up @@ -97,9 +99,11 @@ protected AuthenticationResult doAuthenticate(MessageContext messageContext) thr
try {
int tenantId = IdentityTenantUtil.getTenantIdOfUser(userName);
String tenantDomain = MultitenantUtils.getTenantDomain(userName);
String tenantAwareUsername = MultitenantUtils.getTenantAwareUsername(userName);

User user = new User();
user.setUserName(MultitenantUtils.getTenantAwareUsername(userName));
user.setUserName(UserCoreUtil.removeDomainFromName(tenantAwareUsername));
user.setUserStoreDomain(IdentityUtil.extractDomainFromName(userName));
user.setTenantDomain(tenantDomain);

authenticationContext.setUser(user);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
import org.wso2.carbon.identity.auth.service.util.AuthConfigurationUtil;
import org.wso2.carbon.identity.core.bean.context.MessageContext;
import org.wso2.carbon.identity.core.handler.InitConfig;
import org.wso2.carbon.identity.core.util.IdentityUtil;
import org.wso2.carbon.user.core.util.UserCoreUtil;
import org.wso2.carbon.utils.multitenancy.MultitenantConstants;
import org.wso2.carbon.utils.multitenancy.MultitenantUtils;

Expand Down Expand Up @@ -133,6 +135,7 @@ protected AuthenticationResult doAuthenticate(MessageContext messageContext)
}
if (StringUtils.isNotEmpty(username)) {
String tenantDomain = MultitenantUtils.getTenantDomain(username);
String tenantAwareUserName = MultitenantUtils.getTenantAwareUsername(username);

// Get rid of the tenant domain name suffix, if the user belongs to the super tenant.
if (MultitenantConstants.SUPER_TENANT_DOMAIN_NAME.equals(tenantDomain)) {
Expand All @@ -145,7 +148,8 @@ protected AuthenticationResult doAuthenticate(MessageContext messageContext)
}

User user = new User();
user.setUserName(username);
user.setUserName(UserCoreUtil.removeDomainFromName(tenantAwareUserName));
user.setUserStoreDomain(IdentityUtil.extractDomainFromName(username));
user.setTenantDomain(tenantDomain);

authenticationContext.setUser(user);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,14 @@
import org.wso2.carbon.identity.auth.service.handler.AuthenticationHandler;
import org.wso2.carbon.identity.core.bean.context.MessageContext;
import org.wso2.carbon.identity.core.handler.InitConfig;
import org.wso2.carbon.identity.core.util.IdentityUtil;
import org.wso2.carbon.identity.oauth2.OAuth2TokenValidationService;
import org.wso2.carbon.identity.oauth2.dto.OAuth2ClientApplicationDTO;
import org.wso2.carbon.identity.oauth2.dto.OAuth2TokenValidationRequestDTO;
import org.wso2.carbon.identity.oauth2.dto.OAuth2TokenValidationResponseDTO;
import org.wso2.carbon.identity.oauth2.token.bindings.TokenBinding;
import org.wso2.carbon.identity.oauth2.util.OAuth2Util;
import org.wso2.carbon.user.core.util.UserCoreUtil;
import org.wso2.carbon.utils.multitenancy.MultitenantUtils;

import javax.servlet.http.Cookie;
Expand Down Expand Up @@ -109,8 +111,12 @@ protected AuthenticationResult doAuthenticate(MessageContext messageContext) {
authenticationResult.setAuthenticationStatus(AuthenticationStatus.SUCCESS);

if (StringUtils.isNotEmpty(responseDTO.getAuthorizedUser())) {
String tenantAwareUserName = MultitenantUtils.getTenantAwareUsername(
responseDTO.getAuthorizedUser());

User user = new User();
user.setUserName(MultitenantUtils.getTenantAwareUsername(responseDTO.getAuthorizedUser()));
user.setUserName(UserCoreUtil.removeDomainFromName(tenantAwareUserName));
user.setUserStoreDomain(IdentityUtil.extractDomainFromName(responseDTO.getAuthorizedUser()));
user.setTenantDomain(MultitenantUtils.getTenantDomain(responseDTO.getAuthorizedUser()));
authenticationContext.setUser(user);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
import org.wso2.carbon.identity.auth.service.AuthenticationStatus;
import org.wso2.carbon.identity.auth.service.handler.AuthenticationHandler;
import org.wso2.carbon.identity.core.bean.context.MessageContext;
import org.wso2.carbon.identity.core.util.IdentityUtil;
import org.wso2.carbon.user.core.util.UserCoreUtil;
import org.wso2.carbon.utils.ServerConstants;
import org.wso2.carbon.utils.multitenancy.MultitenantConstants;
import org.wso2.carbon.utils.multitenancy.MultitenantUtils;
Expand Down Expand Up @@ -110,8 +112,11 @@ private boolean isLoggedInUserExists(Request request) {

private User buildUser(String userName, String tenantDomain) {

String tenantAwareUserName = MultitenantUtils.getTenantAwareUsername(userName);

User user = new User();
user.setUserName(MultitenantUtils.getTenantAwareUsername(userName));
user.setUserName(UserCoreUtil.removeDomainFromName(tenantAwareUserName));
user.setUserStoreDomain(IdentityUtil.extractDomainFromName(userName));
user.setTenantDomain(tenantDomain);
return user;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.wso2.carbon.identity.auth.service.util.AuthConfigurationUtil;
import org.wso2.carbon.identity.core.handler.HandlerComparator;
import org.wso2.carbon.identity.core.util.IdentityCoreInitializedEvent;
import org.wso2.carbon.identity.user.account.association.UserAccountConnector;
import org.wso2.carbon.user.core.service.RealmService;
import java.util.Collections;
import java.util.List;
Expand Down Expand Up @@ -110,6 +111,23 @@ protected void removeAuthenticationHandler(AuthenticationHandler authenticationH
AuthenticationServiceHolder.getInstance().getAuthenticationHandlers().remove(authenticationHandler);
}

@Reference(
name = "org.wso2.carbon.identity.user.account.association.connector",
service = org.wso2.carbon.identity.user.account.association.UserAccountConnector.class,
cardinality = ReferenceCardinality.MULTIPLE,
policy = ReferencePolicy.DYNAMIC,
unbind = "removeUserAccountConnector")
protected void addUserAccountConnector(UserAccountConnector userAccountConnector) {
if (log.isDebugEnabled()) {
log.debug("UserAccountConnector acquired");
}
AuthenticationServiceHolder.getInstance().setUserAccountConnector(userAccountConnector);
}

protected void removeUserAccountConnector(UserAccountConnector userAccountConnector) {
AuthenticationServiceHolder.getInstance().setUserAccountConnector(null);
}

@Reference(
name = "org.wso2.carbon.identity.auth.service.handler.resource",
service = org.wso2.carbon.identity.auth.service.handler.ResourceHandler.class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.wso2.carbon.identity.auth.service.handler.AuthenticationHandler;
import org.wso2.carbon.identity.auth.service.handler.ResourceHandler;
import org.wso2.carbon.identity.core.handler.MessageHandlerComparator;
import org.wso2.carbon.identity.user.account.association.UserAccountConnector;
import org.wso2.carbon.user.core.service.RealmService;

import java.util.ArrayList;
Expand All @@ -38,7 +39,7 @@ public class AuthenticationServiceHolder {
private RealmService realmService = null;
private List<AuthenticationHandler> authenticationHandlers = new ArrayList<>();
private List<ResourceHandler> resourceHandlers = new ArrayList<>();

private UserAccountConnector userAccountConnector = null;

private AuthenticationServiceHolder() {

Expand Down Expand Up @@ -69,4 +70,14 @@ public List<ResourceHandler> getResourceHandlers() {
public List<AuthenticationHandler> getAuthenticationHandlers() {
return authenticationHandlers;
}

public UserAccountConnector getUserAccountConnector() {

return userAccountConnector;
}

public void setUserAccountConnector(UserAccountConnector userAccountConnector) {

this.userAccountConnector = userAccountConnector;
}
}
11 changes: 11 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,12 @@
<artifactId>org.wso2.carbon.identity.oauth</artifactId>
<version>${org.wso2.carbon.identity.oauth.version}</version>
</dependency>
<dependency>
<groupId>org.wso2.carbon.identity.association.account</groupId>
<artifactId>org.wso2.carbon.identity.user.account.association</artifactId>
<version>${org.wso2.carbon.identity.user.account.association.version}</version>
</dependency>

<dependency>
<groupId>org.json.wso2</groupId>
<artifactId>json</artifactId>
Expand Down Expand Up @@ -355,6 +361,11 @@
<org.wso2.carbon.identity.oauth.import.version.range>[6.2.18, 7.0.0)
</org.wso2.carbon.identity.oauth.import.version.range>

<org.wso2.carbon.identity.user.account.association.version>5.3.5
</org.wso2.carbon.identity.user.account.association.version>
<org.wso2.carbon.identity.user.account.association.import.version.range>[5.3.5, 6.0.0)
</org.wso2.carbon.identity.user.account.association.import.version.range>

<!-- OSGi framework component version -->
<osgi.service.component.imp.pkg.version.range>[1.2.0, 2.0.0)</osgi.service.component.imp.pkg.version.range>
<osgi.framework.imp.pkg.version.range>[1.7.0, 2.0.0)</osgi.framework.imp.pkg.version.range>
Expand Down