Skip to content
Open
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 @@ -95,6 +95,7 @@

import static org.wso2.carbon.identity.application.authentication.framework.handler.request.PostAuthnHandlerFlowStatus.SUCCESS_COMPLETED;
import static org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants.ALLOW_LOGIN_TO_IDP;
import static org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants.RESOLVE_EXISTING_USER_BEFORE_CONSENT_PROMPT;
import static org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants.Config.SEND_ONLY_LOCALLY_MAPPED_ROLES_OF_IDP;
import static org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants.EMAIL_ADDRESS_CLAIM;
import static org.wso2.carbon.identity.application.authentication.framework.util.FrameworkErrorConstants.ErrorMessages.ERROR_WHILE_ENCRYPTING_TOTP_SECRET_KEY;
Expand Down Expand Up @@ -339,6 +340,16 @@
boolean isUserAllowsToLoginIdp = Boolean.parseBoolean(IdentityUtil
.getProperty(ALLOW_LOGIN_TO_IDP));

// When enabled, resolve and link the federated identity to an existing local account before the
// prompt-consent sign-up redirect, so an existing user is logged in instead of being redirected
// to the sign-up form. Applies only to IdPs that have associate-local-user enabled.
if (isResolveExistingUserBeforeConsentPromptEnabled()
&& externalIdPConfig.isAssociateLocalUserEnabled()
&& StringUtils.isEmpty(associatedLocalUser)
&& externalIdPConfig.isPromptConsentEnabled()) {
associatedLocalUser = resolveAndAssociateExistingLocalUser(externalIdPConfig, context,
localClaimValues, federatedClaimValues, stepConfig, externalSubject);
}
// If associatedLocalUser is null, that means relevant association not exist already.
if (StringUtils.isEmpty(associatedLocalUser) && externalIdPConfig.isPromptConsentEnabled()) {
if (log.isDebugEnabled()) {
Expand All @@ -355,71 +366,8 @@
return PostAuthnHandlerFlowStatus.INCOMPLETE;
}
if (StringUtils.isEmpty(associatedLocalUser) && externalIdPConfig.isAssociateLocalUserEnabled()) {

AccountLookupAttributeMappingConfig[] accountLookupAttributeMappingConfigs =
externalIdPConfig.getAccountLookupAttributeMappings();
boolean isEmailUsernameLookup =
ArrayUtils.isEmpty(accountLookupAttributeMappingConfigs);
if (log.isDebugEnabled()) {
log.debug("Account lookup attribute mappings are not configured for the IDP: "
+ externalIdPConfig.getIdPName() + ". Hence, using email address claim for account "
+ "lookup matching with local username.");
}
if (isEmailUsernameLookup &&
StringUtils.isNotBlank(localClaimValues.get(EMAIL_ADDRESS_CLAIM))) {
try {
String emailUsername = localClaimValues.get(EMAIL_ADDRESS_CLAIM);
UserRealm realm = getUserRealm(context.getTenantDomain());
AbstractUserStoreManager userStoreManager =
(AbstractUserStoreManager) getUserStoreManager(context.getExternalIdP()
.getProvisioningUserStoreId(), realm, emailUsername);
if (userStoreManager.isExistingUser(emailUsername)) {
org.wso2.carbon.user.core.common.User user =
userStoreManager.getUser(null, emailUsername);
//associate user
FrameworkUtils.getFederatedAssociationManager()
.createFederatedAssociation(new User(user),
stepConfig.getAuthenticatedIdP(),
externalSubject);
associatedLocalUser = user.getDomainQualifiedUsername();
}
} catch (UserStoreException e) {
handleExceptions(ErrorMessages.ERROR_WHILE_CHECKING_USERNAME_EXISTENCE.getMessage(),
"error.user.existence", e);
} catch (FrameworkException | FederatedAssociationManagerException e) {
handleExceptions(e.getMessage(), e.getErrorCode(), e);
}
} else {
if (log.isDebugEnabled()) {
log.debug("Account lookup attribute mappings configured for IDP: " +
externalIdPConfig.getIdPName() + ". Attempting to match user using mapped " +
"attributes.");
}
Map<String, String> localClaimValuesForLookup =
getLocalClaimsForAccountLookup(federatedClaimValues,
externalIdPConfig.getAccountLookupAttributeMappings());

org.wso2.carbon.user.core.common.User user = getLocalUser(context.getTenantDomain(),
externalIdPConfig.getProvisioningUserStoreId(), localClaimValuesForLookup);

try {
if (user != null) {
FrameworkUtils.getFederatedAssociationManager()
.createFederatedAssociation(new User(user),
stepConfig.getAuthenticatedIdP(),
externalSubject);
associatedLocalUser = user.getDomainQualifiedUsername();
} else {
if (log.isDebugEnabled()) {
log.debug("No local user found for the account lookup attributes: "
+ localClaimValuesForLookup + " in tenant domain: "
+ context.getTenantDomain());
}
}
} catch (FederatedAssociationManagerException | FrameworkException e) {
handleExceptions(e.getMessage(), e.getErrorCode(), e);
}
}
associatedLocalUser = resolveAndAssociateExistingLocalUser(externalIdPConfig, context,
localClaimValues, federatedClaimValues, stepConfig, externalSubject);
}
if (externalIdPConfig.isSkipJITOnAttrAccountLookupEnabled() &&
StringUtils.isEmpty(associatedLocalUser)) {
Expand Down Expand Up @@ -542,7 +490,7 @@
} else if (users.size() > 1) {
if (log.isDebugEnabled()) {
log.debug("Multiple users found with the claim: " + localClaimKey + " and value: "
+ localClaimValue + " in tenant domain: " + tenantDomain);

Check failure on line 493 in components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/handler/request/impl/JITProvisioningPostAuthenticationHandler.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal " in tenant domain: " 3 times.

See more on https://sonarcloud.io/project/issues?id=wso2_carbon-identity-framework&issues=AZ876SddtAXIUJFCv7uh&open=AZ876SddtAXIUJFCv7uh&pullRequest=8194
}
throw new PostAuthenticationFailedException(
ErrorMessages.ERROR_MULTIPLE_MATCHING_LOCAL_ACCOUNTS.getCode(),
Expand Down Expand Up @@ -826,6 +774,105 @@
throw new PostAuthenticationFailedException(errorCode, errorMessage, e);
}

/**
* Whether resolving an existing local user before the prompt-consent sign-up redirect is enabled.
*
* @return true if resolving existing users before prompt consent is enabled.
*/
private boolean isResolveExistingUserBeforeConsentPromptEnabled() {

return Boolean.parseBoolean(IdentityUtil.getProperty(RESOLVE_EXISTING_USER_BEFORE_CONSENT_PROMPT));
}

/**
* Resolve an existing local account for the federated user and, if found, create the federated association.
* Uses the IdP's account lookup attribute mappings when configured, otherwise falls back to matching the
* email address claim against the local username.
*
* @param externalIdPConfig Relevant external IDP.
* @param context Authentication context.
* @param localClaimValues Local claim values of the user.
* @param federatedClaimValues Federated claim values of the user.
* @param stepConfig The authentication step config.
* @param externalSubject The federated subject identifier.
* @return The domain-qualified username of the linked local account, or null when no matching account exists.
* @throws PostAuthenticationFailedException If the user-store lookup or association fails.
*/
private String resolveAndAssociateExistingLocalUser(ExternalIdPConfig externalIdPConfig,

Check failure on line 801 in components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/handler/request/impl/JITProvisioningPostAuthenticationHandler.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this method to reduce its Cognitive Complexity from 21 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=wso2_carbon-identity-framework&issues=AZ876SddtAXIUJFCv7ui&open=AZ876SddtAXIUJFCv7ui&pullRequest=8194
AuthenticationContext context,
Map<String, String> localClaimValues,
Map<ClaimMapping, String> federatedClaimValues,
StepConfig stepConfig, String externalSubject)
throws PostAuthenticationFailedException {

String associatedLocalUser = null;
AccountLookupAttributeMappingConfig[] accountLookupAttributeMappingConfigs =
externalIdPConfig.getAccountLookupAttributeMappings();
boolean isEmailUsernameLookup =
ArrayUtils.isEmpty(accountLookupAttributeMappingConfigs);
if (isEmailUsernameLookup &&
StringUtils.isNotBlank(localClaimValues.get(EMAIL_ADDRESS_CLAIM))) {
if (log.isDebugEnabled()) {
log.debug("Account lookup attribute mappings are not configured for the IDP: "
+ externalIdPConfig.getIdPName() + ". Hence, using email address claim for account "
+ "lookup matching with local username.");
}
try {
String emailUsername = localClaimValues.get(EMAIL_ADDRESS_CLAIM);
UserRealm realm = getUserRealm(context.getTenantDomain());
AbstractUserStoreManager userStoreManager =
(AbstractUserStoreManager) getUserStoreManager(context.getExternalIdP()
.getProvisioningUserStoreId(), realm, emailUsername);
if (userStoreManager.isExistingUser(emailUsername)) {
org.wso2.carbon.user.core.common.User user =
userStoreManager.getUser(null, emailUsername);
//associate user
FrameworkUtils.getFederatedAssociationManager()
.createFederatedAssociation(new User(user),
stepConfig.getAuthenticatedIdP(),
externalSubject);
associatedLocalUser = user.getDomainQualifiedUsername();
}
} catch (UserStoreException e) {
handleExceptions(ErrorMessages.ERROR_WHILE_CHECKING_USERNAME_EXISTENCE.getMessage(),
"error.user.existence", e);
} catch (FrameworkException | FederatedAssociationManagerException e) {
handleExceptions(e.getMessage(), e.getErrorCode(), e);
}
} else {
if (log.isDebugEnabled()) {
log.debug("Account lookup attribute mappings configured for IDP: " +
externalIdPConfig.getIdPName() + ". Attempting to match user using mapped " +
"attributes.");
}
Map<String, String> localClaimValuesForLookup =
getLocalClaimsForAccountLookup(federatedClaimValues,
externalIdPConfig.getAccountLookupAttributeMappings());

org.wso2.carbon.user.core.common.User user = getLocalUser(context.getTenantDomain(),
externalIdPConfig.getProvisioningUserStoreId(), localClaimValuesForLookup);

try {
if (user != null) {
FrameworkUtils.getFederatedAssociationManager()
.createFederatedAssociation(new User(user),
stepConfig.getAuthenticatedIdP(),
externalSubject);
associatedLocalUser = user.getDomainQualifiedUsername();
} else {
if (log.isDebugEnabled()) {
log.debug("No local user found for the account lookup attributes: "
+ localClaimValuesForLookup + " in tenant domain: "
+ context.getTenantDomain());
}
}
} catch (FederatedAssociationManagerException | FrameworkException e) {
handleExceptions(e.getMessage(), e.getErrorCode(), e);
}
}
return associatedLocalUser;
}

/**
* Call the relevant URL to add the new user.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,8 @@ public abstract class FrameworkConstants {
public static final String REQ_ATTR_RETRY_STATUS = "retryStatus";
public static final String IDP_MAPPED_USER_ROLES = "identityProviderMappedUserRoles";
public static final String ALLOW_ASSOCIATING_TO_EXISTING_USER = "JITProvisioning.AllowAssociatingToExistingUser";
public static final String RESOLVE_EXISTING_USER_BEFORE_CONSENT_PROMPT =
"JITProvisioning.ResolveExistingUserBeforeConsentPrompt";

// The constant to used as the attribute key or the property key of the federated tokens.
public static final String FEDERATED_TOKENS = "federated_tokens";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2346,6 +2346,9 @@
<AllowLoginToIDP>{{authentication.jit_provisioning.allow_idp_login}}</AllowLoginToIDP>
{% endif %}
<AllowAssociatingToExistingUser>{{authentication.jit_provisioning.associating_to_existing_user}}</AllowAssociatingToExistingUser>
{% if authentication.jit_provisioning.resolve_existing_user_before_consent_prompt is defined %}
<ResolveExistingUserBeforeConsentPrompt>{{authentication.jit_provisioning.resolve_existing_user_before_consent_prompt}}</ResolveExistingUserBeforeConsentPrompt>
{% endif %}
<EnableConfiguredIdpSubForFederatedUserAssociation>{{authentication.jit_provisioning.enable_configured_idp_sub_for_federated_user_association}}</EnableConfiguredIdpSubForFederatedUserAssociation>
<AllowNonStandardClaimURI>{{authentication.jit_provisioning.allow_non_Standard_claim_uri}}</AllowNonStandardClaimURI>
{% if authentication.jit_provisioning.show_failure_reason is defined %}
Expand Down
Loading