Skip to content

Commit

Permalink
Merge pull request #158 from ThaminduDilshan/thamindu-pw-mig-utils
Browse files Browse the repository at this point in the history
Add conditional authentication functions for dynamic password migration
  • Loading branch information
ThaminduDilshan authored May 21, 2024
2 parents 2d9ed3f + f952fc5 commit e703a8f
Show file tree
Hide file tree
Showing 20 changed files with 1,824 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ public static class ActionIDs {

public static final String RECEIVE_TOKEN = "receive-token";
public static final String RECEIVE_API_RESPONSE = "receive-api-response";
public static final String VALIDATE_INPUT_PARAMS = "validate-input-parameters";
public static final String UPDATE_USER_PASSWORD = "update-user-password";
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,16 @@
<artifactId>org.wso2.carbon.identity.conditional.auth.functions.test.utils</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.wso2.carbon.identity.conditional.auth.functions</groupId>
<artifactId>org.wso2.carbon.identity.conditional.auth.functions.common</artifactId>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.h2database.wso2</groupId>
<artifactId>h2-database-engine</artifactId>
Expand Down Expand Up @@ -116,6 +126,20 @@
<groupId>org.wso2.carbon.identity.organization.management.core</groupId>
<artifactId>org.wso2.carbon.identity.organization.management.service</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon.identity.framework</groupId>
<artifactId>org.wso2.carbon.identity.central.log.mgt</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon.identity.governance</groupId>
<artifactId>org.wso2.carbon.identity.governance</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.wso2.carbon.identity.framework</groupId>
<artifactId>org.wso2.carbon.identity.event</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down Expand Up @@ -197,10 +221,30 @@
org.wso2.carbon.identity.application.common.*; version="${carbon.identity.package.import.version.range}",
com.nimbusds.jose.*; version="${nimbusds.osgi.version.range}",
net.minidev.json; version="${net.minidev.json.imp.pkg.version.range}",
org.wso2.carbon.identity.central.log.mgt.utils; version="${carbon.identity.package.import.version.range}",
org.wso2.carbon.identity.conditional.auth.functions.common.utils,
org.wso2.carbon.utils;version="${carbon.kernel.package.import.version.range}",
</Import-Package>
</instructions>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven.surefire.plugin.version}</version>
<configuration>
<argLine>
--add-opens=java.base/java.lang=ALL-UNNAMED
--add-opens=java.base/java.util=ALL-UNNAMED
--add-opens java.xml/jdk.xml.internal=ALL-UNNAMED
--add-opens=java.base/java.io=ALL-UNNAMED
--add-opens=java.base/sun.nio.fs=ALL-UNNAMED
</argLine>
<suiteXmlFiles>
<suiteXmlFile>src/test/resources/testng.xml</suiteXmlFile>
</suiteXmlFiles>
</configuration>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.wso2.carbon.identity.conditional.auth.functions.user;

import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsAuthenticatedUser;

/**
* Function to update user password.
*/
@FunctionalInterface
public interface UpdateUserPasswordFunction {

/**
* Update user password.
*
* @param user Authenticated user.
* @param parameters Parameters. It is mandatory to provide the new password as the first parameter.
* Then an optional map of event handlers can be provided.
*/
void updateUserPassword(JsAuthenticatedUser user, Object... parameters);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
/*
* Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.wso2.carbon.identity.conditional.auth.functions.user;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.identity.application.authentication.framework.AsyncProcess;
import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.JsGraphBuilder;
import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsAuthenticatedUser;
import org.wso2.carbon.identity.application.authentication.framework.exception.FrameworkException;
import org.wso2.carbon.identity.central.log.mgt.utils.LoggerUtils;
import org.wso2.carbon.identity.conditional.auth.functions.common.utils.Constants;
import org.wso2.carbon.user.core.UserRealm;
import org.wso2.carbon.user.core.UserStoreException;
import org.wso2.carbon.user.core.UserStoreManager;
import org.wso2.carbon.utils.DiagnosticLog;

import java.util.Collections;
import java.util.Map;

/**
* Function to update user password.
*/
public class UpdateUserPasswordFunctionImpl implements UpdateUserPasswordFunction {

private static final Log LOG = LogFactory.getLog(UpdateUserPasswordFunctionImpl.class);

@Override
public void updateUserPassword(JsAuthenticatedUser user, Object... parameters) {

if (user == null) {
throw new IllegalArgumentException("User is not defined.");
}
if (parameters == null || parameters.length == 0) {
throw new IllegalArgumentException("Password is not defined.");
}

String newPassword = null;
Map<String, Object> eventHandlers = null;

if (parameters.length == 2) {
LOG.debug("Both password and event handlers are provided.");
newPassword = (String) parameters[0];

if (parameters[1] instanceof Map) {
eventHandlers = (Map<String, Object>) parameters[1];
} else {
throw new IllegalArgumentException("Invalid argument type. Expected eventHandlers " +
"(Map<String, Object>).");
}
} else {
LOG.debug("Only the password is provided.");
newPassword = (String) parameters[0];
}

if (StringUtils.isBlank(newPassword)) {
throw new IllegalArgumentException("The provided password is empty.");
}

if (eventHandlers != null) {
String finalNewPassword = newPassword;
AsyncProcess asyncProcess = new AsyncProcess((context, asyncReturn) -> {
try {
doUpdatePassword(user, finalNewPassword);
asyncReturn.accept(context, Collections.emptyMap(), Constants.OUTCOME_SUCCESS);
} catch (FrameworkException e) {
asyncReturn.accept(context, Collections.emptyMap(), Constants.OUTCOME_FAIL);
}
});
JsGraphBuilder.addLongWaitProcess(asyncProcess, eventHandlers);
} else {
try {
doUpdatePassword(user, newPassword);
} catch (FrameworkException e) {
// Ignore FrameworkException as the function is not expected to throw any.
}
}
}

private void doUpdatePassword(JsAuthenticatedUser user, String newPassword) throws FrameworkException {

try {
if (user.getWrapped() != null) {
String tenantDomain = user.getWrapped().getTenantDomain();
String userStoreDomain = user.getWrapped().getUserStoreDomain();
String username = user.getWrapped().getUserName();
String loggableUserId = user.getWrapped().getLoggableMaskedUserId();
UserRealm userRealm = Utils.getUserRealm(tenantDomain);

if (userRealm != null) {
UserStoreManager userStoreManager = Utils.getUserStoreManager(
tenantDomain, userRealm, userStoreDomain);

// Update the user password.
userStoreManager.updateCredentialByAdmin(username, newPassword);

if (LOG.isDebugEnabled()) {
LOG.debug(String.format("User password updated successfully for the user: %s " +
"in tenant: %s.", username, tenantDomain));
}
if (LoggerUtils.isDiagnosticLogsEnabled()) {
DiagnosticLog.DiagnosticLogBuilder diagnosticLogBuilder =
new DiagnosticLog.DiagnosticLogBuilder(
Constants.LogConstants.ADAPTIVE_AUTH_SERVICE,
Constants.LogConstants.ActionIDs.UPDATE_USER_PASSWORD
);
diagnosticLogBuilder.resultMessage("User password updated successfully.")
.inputParam("user", loggableUserId)
.logDetailLevel(DiagnosticLog.LogDetailLevel.APPLICATION)
.resultStatus(DiagnosticLog.ResultStatus.SUCCESS);
LoggerUtils.triggerDiagnosticLogEvent(diagnosticLogBuilder);
}
} else {
if (LOG.isDebugEnabled()) {
LOG.debug(String.format("Unable to find user realm for the user: %s " +
"in tenant: %s", username, tenantDomain));
}
String message = "Unable to find user realm for the user.";
if (LoggerUtils.isDiagnosticLogsEnabled()) {
DiagnosticLog.DiagnosticLogBuilder diagnosticLogBuilder =
new DiagnosticLog.DiagnosticLogBuilder(
Constants.LogConstants.ADAPTIVE_AUTH_SERVICE,
Constants.LogConstants.ActionIDs.UPDATE_USER_PASSWORD
);
diagnosticLogBuilder.resultMessage(message)
.inputParam("user", loggableUserId)
.inputParam("tenantDomain", tenantDomain)
.inputParam("userStoreDomain", userStoreDomain)
.logDetailLevel(DiagnosticLog.LogDetailLevel.APPLICATION)
.resultStatus(DiagnosticLog.ResultStatus.FAILED);
LoggerUtils.triggerDiagnosticLogEvent(diagnosticLogBuilder);
}

throw new FrameworkException(message);
}
} else {
String message = "Unable to get wrapped content for the user.";

LOG.debug(message);
if (LoggerUtils.isDiagnosticLogsEnabled()) {
DiagnosticLog.DiagnosticLogBuilder diagnosticLogBuilder = new DiagnosticLog.DiagnosticLogBuilder(
Constants.LogConstants.ADAPTIVE_AUTH_SERVICE,
Constants.LogConstants.ActionIDs.UPDATE_USER_PASSWORD
);
diagnosticLogBuilder.resultMessage(message)
.logDetailLevel(DiagnosticLog.LogDetailLevel.APPLICATION)
.resultStatus(DiagnosticLog.ResultStatus.FAILED);
LoggerUtils.triggerDiagnosticLogEvent(diagnosticLogBuilder);
}

throw new FrameworkException(message);
}
} catch (UserStoreException | FrameworkException e) {
String message = "Error occurred while updating the user password.";

LOG.error(message, e);
if (LoggerUtils.isDiagnosticLogsEnabled()) {
DiagnosticLog.DiagnosticLogBuilder diagnosticLogBuilder = new DiagnosticLog.DiagnosticLogBuilder(
Constants.LogConstants.ADAPTIVE_AUTH_SERVICE,
Constants.LogConstants.ActionIDs.UPDATE_USER_PASSWORD
);
diagnosticLogBuilder.resultMessage(message)
.logDetailLevel(DiagnosticLog.LogDetailLevel.APPLICATION)
.resultStatus(DiagnosticLog.ResultStatus.FAILED);
LoggerUtils.triggerDiagnosticLogEvent(diagnosticLogBuilder);
}

throw new FrameworkException(message, e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@
import org.wso2.carbon.identity.conditional.auth.functions.user.TerminateUserSessionImpl;
import org.wso2.carbon.identity.conditional.auth.functions.user.SetAccountAssociationToLocalUserImpl;
import org.wso2.carbon.identity.conditional.auth.functions.user.SetAccountAssociationToLocalUser;
import org.wso2.carbon.identity.conditional.auth.functions.user.UpdateUserPasswordFunction;
import org.wso2.carbon.identity.conditional.auth.functions.user.UpdateUserPasswordFunctionImpl;
import org.wso2.carbon.identity.role.v2.mgt.core.RoleManagementService;
import org.wso2.carbon.idp.mgt.IdpManager;
import org.wso2.carbon.user.core.service.RealmService;
Expand Down Expand Up @@ -91,6 +93,8 @@ protected void activate(ComponentContext ctxt) {
SetAccountAssociationToLocalUser setAccountAssociationToLocalUserImpl = new SetAccountAssociationToLocalUserImpl();
GetAuthenticatedApplicationsFunction getAuthenticatedApplicationsFunctionImp = new GetAuthenticatedAppsFuncImp();
MicrosoftEmailVerificationFunction microsoftEmailVerificationFunction = new MicrosoftEmailVerificationFunctionImpl();
UpdateUserPasswordFunction updateUserPasswordFunction = new UpdateUserPasswordFunctionImpl();

JsFunctionRegistry jsFunctionRegistry = UserFunctionsServiceHolder.getInstance().getJsFunctionRegistry();
jsFunctionRegistry.register(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "hasRole", hasRoleFunctionImpl);
jsFunctionRegistry.register(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "hasAnyOfTheRoles",
Expand Down Expand Up @@ -125,6 +129,8 @@ protected void activate(ComponentContext ctxt) {
getAuthenticatedApplicationsFunctionImp);
jsFunctionRegistry.register(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "checkMicrosoftEmailVerification",
microsoftEmailVerificationFunction);
jsFunctionRegistry.register(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "updateUserPassword",
updateUserPasswordFunction);
} catch (Throwable e) {
LOG.error("Error occurred during conditional authentication user functions bundle activation. ", e);
}
Expand All @@ -149,6 +155,7 @@ protected void deactivate(ComponentContext ctxt) {
jsFunctionRegistry.deRegister(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "doAssociationWithLocalUser");
jsFunctionRegistry.deRegister(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "isAnyOfTheRolesAssignedToUser");
jsFunctionRegistry.deRegister(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "checkMicrosoftEmailVerification");
jsFunctionRegistry.deRegister(JsFunctionRegistry.Subsystem.SEQUENCE_HANDLER, "updateUserPassword");
}
}

Expand Down
Loading

0 comments on commit e703a8f

Please sign in to comment.