Skip to content

Commit

Permalink
Add last password update value format when Active directory is set up
Browse files Browse the repository at this point in the history
  • Loading branch information
sandushi committed Jan 3, 2025
1 parent b93c4e3 commit b89b847
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 0 deletions.
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 @@ -66,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

0 comments on commit b89b847

Please sign in to comment.