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 1 commit
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,15 @@
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.user.core.UserStoreConfigConstants;
import org.wso2.carbon.utils.multitenancy.MultitenantUtils;

/**
* This is the abstract class for custom authentication handlers.
Expand All @@ -41,6 +47,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 +110,127 @@ 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 domainAwareUserName = IdentityUtil.addDomainToName(user.getUserName(),
user.getUserStoreDomain());
String associatedUserRequestHeader = authenticationContext.getAuthenticationRequest().
getHeader(ASSOCIATED_USER_ID_HEADER);
if (StringUtils.isNotEmpty(associatedUserRequestHeader)) {
User associatedUser = getAssociatedUser(authenticationResult, associatedUserRequestHeader);
if (associatedUser != null && !isSameUser(user, associatedUser)) {
setAssociatedUserInCarbonContext(user, associatedUser, authenticationResult);
return;
}
}
setUserInCarbonContext(domainAwareUserName);
}
}
}
}

private User getAssociatedUser(AuthenticationResult authenticationResult, String associatedUserRequestHeader) {

User associatedUser = getUser(associatedUserRequestHeader);
if (associatedUser == null) {
log.error("Invalid user provided with the header: " + ASSOCIATED_USER_ID_HEADER);
setFailedAuthentication(authenticationResult);
return null;
}
return associatedUser;
}

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

if (isUsersInSameTenant(user, associatedUser)) {
tharindu-b-hewage marked this conversation as resolved.
Show resolved Hide resolved
UserAccountConnector userAccountConnector = AuthenticationServiceHolder.getInstance()
.getUserAccountConnector();
if (userAccountConnector != null) {
try {
UserAccountAssociationDTO[] userAccountAssociations = userAccountConnector
.getAccountAssociationsOfUser(user.toFullQualifiedUsername());
for (UserAccountAssociationDTO userAccountAssociationDTO : userAccountAssociations) {
if (isSameUser(associatedUser, userAccountAssociationDTO)) {
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()));
return;
}
}
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);
} catch (UserAccountAssociationException e) {
log.error("Error while getting account associations of the user: "
+ user.toFullQualifiedUsername(), e);
setFailedAuthentication(authenticationResult);
}
} else {
log.error("Unable to get the UserAccountConnector service");
setFailedAuthentication(authenticationResult);
}
} else {
log.error("Cannot switch to an Associated user: " + associatedUser.toFullQualifiedUsername() + ", " +
"in a different tenant domain to the authenticated user: " + user.toFullQualifiedUsername());
setFailedAuthentication(authenticationResult);
}
}

private void setFailedAuthentication(AuthenticationResult authenticationResult) {

authenticationResult.setAuthenticationStatus(AuthenticationStatus.FAILED);
}

private boolean isUsersInSameTenant(User user, User associatedUser) {

return associatedUser.getTenantDomain().equals(user.getTenantDomain());
}

private boolean isSameUser(User user, UserAccountAssociationDTO userAccountAssociationDTO) {

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

private boolean isSameUser(User firstUser, User secondUser) {

return firstUser.toFullQualifiedUsername().equals(secondUser.toFullQualifiedUsername());
tharindu-b-hewage marked this conversation as resolved.
Show resolved Hide resolved
}

private User getUser(String fullyQualifiedUserName) {
tharindu-b-hewage marked this conversation as resolved.
Show resolved Hide resolved

String realm = UserStoreConfigConstants.PRIMARY;
String tenantDomain = MultitenantUtils.getTenantDomain(fullyQualifiedUserName);
String username;
String[] strComponent = MultitenantUtils.getTenantAwareUsername(fullyQualifiedUserName).split("/");

if (strComponent.length == 1) {
username = strComponent[0];
} else if (strComponent.length == 2) {
realm = strComponent[0];
username = strComponent[1];
} else {
return null;
}

User user = new User();
user.setUserName(username);
user.setUserStoreDomain(realm);
user.setTenantDomain(tenantDomain);
return user;
}

private void setUserInCarbonContext(String domainAwareUserName) {

PrivilegedCarbonContext.getThreadLocalCarbonContext().setUsername(domainAwareUserName);
}
}
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