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

Skip the password reset authentication handler and convert last password update time to correct format #898

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
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
2 changes: 2 additions & 0 deletions components/org.wso2.carbon.identity.password.expiry/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@
org.wso2.carbon.user.core.common; version="${carbon.kernel.package.import.version.range}",
org.wso2.carbon.user.core.listener; version="${carbon.kernel.package.import.version.range}",
org.wso2.carbon.user.core.model; version="${carbon.kernel.package.import.version.range}",
org.wso2.carbon.user.core.jdbc; version="${carbon.kernel.package.import.version.range}",
org.wso2.carbon.user.core.ldap; version="${carbon.kernel.package.import.version.range}",
org.wso2.carbon.context; version="${carbon.kernel.package.import.version.range}",
org.wso2.carbon.user.api.*; version="${carbon.user.api.imp.pkg.version.range}",
org.wso2.carbon.identity.application.common.model.*;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,17 @@ public PostAuthnHandlerFlowStatus handle(HttpServletRequest httpServletRequest,
authenticationContext.getCurrentAuthenticatedIdPs().get(PasswordPolicyConstants.AUTHENTICATOR_TYPE)
.getAuthenticators();

/*
Skip the Password Reset Authenticator Handler when the Password Reset Enforcer Authenticator is in the
authenticators list, as password expiration is already checked by the Password Reset Enforcer Authenticator.
*/
boolean isPostAuthenticationHandlerSkipped = authenticators.stream()
.anyMatch(authenticator -> PasswordPolicyConstants.PASSWORD_RESET_ENFORCER_AUTHENTICATOR
.equals(authenticator.getName()));
if (isPostAuthenticationHandlerSkipped) {
return PostAuthnHandlerFlowStatus.SUCCESS_COMPLETED;
}

for (AuthenticatorConfig authenticator : authenticators) {
if (PasswordPolicyConstants.BASIC_AUTHENTICATOR.equals(authenticator.getName())) {
if (!authenticatedUser.isFederatedUser()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ public class PasswordPolicyConstants {
public static final String PASSWORD_GRANT_POST_AUTHENTICATION_EVENT = "PASSWORD_GRANT_POST_AUTHENTICATION";
public static final String AUTHENTICATION_STATUS = "authenticationStatus";
public static final String BASIC_AUTHENTICATOR = "BasicAuthenticator";
public static final String PASSWORD_RESET_ENFORCER_AUTHENTICATOR = "password-reset-enforcer";
public static final String FALSE = "false";
public static final String TRUE = "true";
public static final String CONFIRMATION_QUERY_PARAM = "&confirmation=";
Expand All @@ -65,6 +66,10 @@ public class PasswordPolicyConstants {
public static final String PASSWORD_EXPIRY_RULES_PREFIX = "passwordExpiry.rule";
public static final Integer MAX_PASSWORD_EXPIRY_RULE_VALUES = 5;

// Time conversion constants.
public static final long WINDOWS_EPOCH_DIFF = 11644473600000L;
public static final long HUNDREDS_OF_NANOSECONDS = 10000L;

public enum ErrorMessages {
ERROR_WHILE_GETTING_USER_STORE_DOMAIN("80001",
"Error occurred while getting the user store domain."),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
import org.wso2.carbon.user.api.UserStoreException;
import org.wso2.carbon.user.core.UserStoreManager;
import org.wso2.carbon.user.core.common.AbstractUserStoreManager;
import org.wso2.carbon.user.core.jdbc.UniqueIDJDBCUserStoreManager;
import org.wso2.carbon.user.core.ldap.UniqueIDActiveDirectoryUserStoreManager;
import org.wso2.carbon.user.core.service.RealmService;
import org.wso2.carbon.user.core.util.UserCoreUtil;
import org.wso2.carbon.utils.multitenancy.MultitenantConstants;
Expand All @@ -60,7 +62,9 @@
import java.util.stream.Collectors;

import static org.wso2.carbon.identity.password.expiry.constants.PasswordPolicyConstants.CONNECTOR_CONFIG_NAME;
import static org.wso2.carbon.identity.password.expiry.constants.PasswordPolicyConstants.HUNDREDS_OF_NANOSECONDS;
import static org.wso2.carbon.identity.password.expiry.constants.PasswordPolicyConstants.PASSWORD_RESET_PAGE;
import static org.wso2.carbon.identity.password.expiry.constants.PasswordPolicyConstants.WINDOWS_EPOCH_DIFF;

/**
* Utilities for password change enforcing.
Expand Down Expand Up @@ -609,6 +613,11 @@ private static String getLastPasswordUpdatedTime(String tenantAwareUsername, Use
getLastPasswordUpdateTime(userStoreManager, claimURI, tenantAwareUsername);
}
}
// Check if the Identity datastore is set to Active Directory and do the conversion accordingly.
if (!lastPasswordUpdatedTime.isEmpty() &&
isUserStoreBasedIdentityDataStore() && isActiveDirectoryUserStore(userStoreManager)) {
lastPasswordUpdatedTime = convertWindowsFileTimeToUnixTime(lastPasswordUpdatedTime);
}
} catch (UserStoreException e) {
throw new PostAuthenticationFailedException(
PasswordPolicyConstants.ErrorMessages.ERROR_WHILE_GETTING_CLAIM_MAPPINGS.getCode(),
Expand Down Expand Up @@ -711,4 +720,57 @@ public static String getPasswordResetPageUrl(String tenantDomain) throws PostAut
PasswordPolicyConstants.ErrorMessages.ERROR_WHILE_BUILDING_PASSWORD_RESET_PAGE_URL.getMessage());
}
}
}

/**
* Check whether the user store is based on identity data store.
*
* @return true if the user store is based on identity data store.
*/
public static boolean isUserStoreBasedIdentityDataStore() {

return EnforcePasswordResetComponentDataHolder.getInstance().getIdentityDataStoreService()
.isUserStoreBasedIdentityDataStore();
}

/**
* Check whether the user store is based on Active Directory.
*
* @param userStoreManager The user store manager.
* @return true if the user store is based on Active Directory.
*/
public static boolean isActiveDirectoryUserStore(UserStoreManager userStoreManager) {

return userStoreManager instanceof UniqueIDJDBCUserStoreManager
&& userStoreManager.getSecondaryUserStoreManager() instanceof UniqueIDActiveDirectoryUserStoreManager;
}

/**
* Converts a Windows FileTime string to Unix time in milliseconds.
*
* Windows FileTime is a 64-bit value representing the number of 100-nanosecond
* intervals since January 1, 1601 (UTC).
*
* The conversion to Unix time (milliseconds since January 1, 1970, UTC) involves two steps:
*
* 1. Convert the Windows FileTime value from 100-nanosecond intervals to milliseconds:
* - This is done by dividing the FileTime value by 10,000 (HUNDREDS_OF_NANOSECONDS).
* - This converts the FileTime value from 100-nanosecond intervals to milliseconds.
*
* 2. Adjust for the difference in epoch start dates between Windows and Unix:
* - Windows epoch starts on January 1, 1601, while Unix epoch starts on January 1, 1970.
* - The difference between these two epochs is 11644473600000 milliseconds (WINDOWS_EPOCH_DIFF).
* - Subtracting this value aligns the converted milliseconds with the Unix epoch.
*
* The resulting value represents the number of milliseconds since the Unix epoch,
* which is returned as a string.
*
* @param windowsFileTime A string representing the Windows FileTime to be converted.
* @return A string representing the Unix time in milliseconds.
*/
public static String convertWindowsFileTimeToUnixTime(String windowsFileTime) {

long fileTime = Long.parseLong(windowsFileTime);
long millisSinceEpoch = (fileTime / HUNDREDS_OF_NANOSECONDS) - WINDOWS_EPOCH_DIFF;
return String.valueOf(millisSinceEpoch);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.wso2.carbon.identity.core.ServiceURL;
import org.wso2.carbon.identity.core.ServiceURLBuilder;
import org.wso2.carbon.identity.core.URLBuilderException;
import org.wso2.carbon.identity.governance.service.IdentityDataStoreService;
import org.wso2.carbon.identity.password.expiry.constants.PasswordPolicyConstants;
import org.wso2.carbon.identity.password.expiry.exceptions.ExpiredPasswordIdentificationException;
import org.wso2.carbon.identity.password.expiry.internal.EnforcePasswordResetComponentDataHolder;
Expand Down Expand Up @@ -94,6 +95,9 @@ public class PasswordPolicyUtilsTest {
@Mock
private ClaimManager claimManager;

@Mock
private IdentityDataStoreService identityDataStoreService;

@Mock
private org.wso2.carbon.user.core.UserRealm userRealm;
private MockedStatic<IdentityTenantUtil> mockedStaticIdentityTenantUtil;
Expand Down Expand Up @@ -150,6 +154,7 @@ public void setUp() {
EnforcePasswordResetComponentDataHolder.getInstance().setIdentityGovernanceService(identityGovernanceService);
EnforcePasswordResetComponentDataHolder.getInstance().setRealmService(realmService);
EnforcePasswordResetComponentDataHolder.getInstance().setRoleManagementService(roleManagementService);
EnforcePasswordResetComponentDataHolder.getInstance().setIdentityDataStoreService(identityDataStoreService);
}

@Test
Expand Down Expand Up @@ -263,6 +268,7 @@ public void testIsPasswordExpiredWithoutRules(Integer daysAgo, boolean expectedE
when(userRealm.getClaimManager()).thenReturn(claimManager);
when(UserCoreUtil.addDomainToName(any(), any())).thenReturn(tenantAwareUsername);
when(abstractUserStoreManager.getUserIDFromUserName(tenantAwareUsername)).thenReturn(userId);
when(identityDataStoreService.isUserStoreBasedIdentityDataStore()).thenReturn(false);

mockPasswordExpiryEnabled(identityGovernanceService, PasswordPolicyConstants.TRUE);

Expand Down Expand Up @@ -317,6 +323,7 @@ public void testIsPasswordExpiredWithRules(int daysAgo, String[] roles, String[]
when(abstractUserStoreManager.getUserIDFromUserName(tenantAwareUsername)).thenReturn(userId);
when(UserCoreUtil.addDomainToName(any(), any())).thenReturn(tenantAwareUsername);
when(roleManagementService.getRoleListOfUser(userId, tenantDomain)).thenReturn(getRoles(roles));
when(identityDataStoreService.isUserStoreBasedIdentityDataStore()).thenReturn(false);

mockPasswordExpiryEnabled(identityGovernanceService, PasswordPolicyConstants.TRUE);

Expand Down Expand Up @@ -367,6 +374,7 @@ public void testGetUserPasswordExpiryTime(Integer daysAgo, String[] roles, Strin
when(userRealm.getClaimManager()).thenReturn(claimManager);
when(abstractUserStoreManager.getUserIDFromUserName(tenantAwareUsername)).thenReturn(userId);
when(UserCoreUtil.addDomainToName(any(), any())).thenReturn(tenantAwareUsername);
when(identityDataStoreService.isUserStoreBasedIdentityDataStore()).thenReturn(false);

// Mock last password update time.
Long updateTime = daysAgo != null ? System.currentTimeMillis() - getDaysTimeInMillis(daysAgo) : null;
Expand Down
Loading