Skip to content

Timestamp attributes conversion logic Improvements.#4597

Closed
Thisara-Welmilla wants to merge 1 commit into
wso2:4.12.xfrom
Thisara-Welmilla:improve-timestamp-convertion-logic
Closed

Timestamp attributes conversion logic Improvements.#4597
Thisara-Welmilla wants to merge 1 commit into
wso2:4.12.xfrom
Thisara-Welmilla:improve-timestamp-convertion-logic

Conversation

@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 0e6602a8-4d65-49a1-91ab-e77863a86820

📥 Commits

Reviewing files that changed from the base of the PR and between aa667c8 and 530f6e0.

📒 Files selected for processing (5)
  • core/org.wso2.carbon.user.core/src/main/java/org/wso2/carbon/user/core/common/AbstractUserStoreManager.java
  • core/org.wso2.carbon.user.core/src/main/java/org/wso2/carbon/user/core/ldap/ReadOnlyLDAPUserStoreManager.java
  • core/org.wso2.carbon.user.core/src/main/java/org/wso2/carbon/user/core/ldap/UniqueIDReadOnlyLDAPUserStoreManager.java
  • core/org.wso2.carbon.user.core/src/main/java/org/wso2/carbon/user/core/util/LDAPUtil.java
  • core/org.wso2.carbon.user.core/src/test/java/org/wso2/carbon/user/core/ldap/UniqueIDReadOnlyLDAPUserStoreManagerTest.java
🚧 Files skipped from review as they are similar to previous changes (5)
  • core/org.wso2.carbon.user.core/src/main/java/org/wso2/carbon/user/core/ldap/ReadOnlyLDAPUserStoreManager.java
  • core/org.wso2.carbon.user.core/src/main/java/org/wso2/carbon/user/core/util/LDAPUtil.java
  • core/org.wso2.carbon.user.core/src/main/java/org/wso2/carbon/user/core/common/AbstractUserStoreManager.java
  • core/org.wso2.carbon.user.core/src/main/java/org/wso2/carbon/user/core/ldap/UniqueIDReadOnlyLDAPUserStoreManager.java
  • core/org.wso2.carbon.user.core/src/test/java/org/wso2/carbon/user/core/ldap/UniqueIDReadOnlyLDAPUserStoreManagerTest.java

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes

    • Improved error handling and logging during user attribute retrieval; certain attribute-processing errors are now surfaced or propagated more predictably.
  • Refactor

    • Centralized timestamp conversion with automatic format detection and support for multiple LDAP timestamp variants.
    • Streamlined post-retrieval attribute processing to separate exception-propagating and non-propagating flows.
  • Tests

    • Updated tests to validate the new timestamp utilities and revised exception behaviors.

Walkthrough

Centralizes LDAP timestamp parsing/normalization in LDAPUtil, adds exception-throwing post-retrieval attribute hooks in AbstractUserStoreManager, updates ReadOnly/UniqueID LDAP managers to use the new utilities and exception-aware hooks, and updates tests to call the utility directly.

Changes

LDAP attribute processing and timestamp handling

Layer / File(s) Summary
LDAPUtil timestamp format utilities
core/org/wso2/carbon/user.core/src/main/java/org/wso2/carbon/user/core/util/LDAPUtil.java
New convertToStandardTimeFormat(RealmConfiguration, String) parses LDAP generalized time using a realm-provided or derived DateTimeFormatter and throws UserStoreException on parse/unsupported formats. New deriveTimestampFormat(String) detects supported LDAP timestamp shapes with regex and returns the formatter pattern or null.
AbstractUserStoreManager exception-throwing hooks
core/org/wso2/carbon/user.core/src/main/java/org/wso2/carbon/user/core/common/AbstractUserStoreManager.java
Adds processAttributesAfterRetrievalWithException(...) and processAttributesAfterRetrievalWithIDWithException(...), marks the older non-throwing variants as @Deprecated, and updates username/ID single and bulk retrieval call sites to invoke the new exception-aware hooks.
ReadOnlyLDAPUserStoreManager exception handling and timestamp conversion
core/org/wso2/carbon/user.core/src/main/java/org/wso2/carbon/user/core/ldap/ReadOnlyLDAPUserStoreManager.java
Introduces a throwable private helper for post-retrieval processing; non-throwing override now catches and logs UserStoreException. Replaces stream-based timestamp conversions with explicit loops calling LDAPUtil.convertToStandardTimeFormat(...). Updates formatter creation to use DateTimeFormatter.ofPattern(...).
UniqueIDReadOnlyLDAPUserStoreManager consolidation
core/org/wso2/carbon/user.core/src/main/java/org/wso2/carbon/user/core/ldap/UniqueIDReadOnlyLDAPUserStoreManager.java
Removes class-local timestamp format derivation/conversion, delegates conversion/derivation to LDAPUtil, refactors post-retrieval processing into a throwable helper plus non-throwing wrapper that logs exceptions, and updates group/user timestamp mapping to use LDAPUtil.
Tests: use LDAPUtil directly
core/org/wso2/carbon/user.core/src/test/java/org/wso2/carbon/user/core/ldap/UniqueIDReadOnlyLDAPUserStoreManagerTest.java
Replaces reflection-based calls to private conversion methods with direct LDAPUtil.convertToStandardTimeFormat(...) calls, simplifies setup, and updates exception handling assertions to expect UserStoreException.

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is largely incomplete, providing only an issue link without addressing most required sections (Purpose, Goals, Approach, User stories, Release notes, Documentation, etc.). Fill in the description template comprehensively, including Purpose, Goals, Approach, Release notes, and other required sections to explain the timestamp conversion logic improvements.
Docstring Coverage ⚠️ Warning Docstring coverage is 62.96% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: improving timestamp attributes conversion logic across multiple files as evidenced by significant refactoring in LDAPUtil and related manager classes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ast-grep (0.42.3)
core/org.wso2.carbon.user.core/src/main/java/org/wso2/carbon/user/core/common/AbstractUserStoreManager.java

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment on lines +9204 to +9208
*/
protected void processAttributesAfterRetrievalWithException(String userName, Map<String, String> userAttributes,
String profileName) throws UserStoreException {

processAttributesAfterRetrieval(userName, userAttributes, profileName);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Log Improvement Suggestion No: 1

Suggested change
*/
protected void processAttributesAfterRetrievalWithException(String userName, Map<String, String> userAttributes,
String profileName) throws UserStoreException {
processAttributesAfterRetrieval(userName, userAttributes, profileName);
protected void processAttributesAfterRetrievalWithException(String userName, Map<String, String> userAttributes,
String profileName) throws UserStoreException {
if (log.isDebugEnabled()) {
log.debug("Processing attributes after retrieval for user: " + userName + ", profile: " + profileName);
}
processAttributesAfterRetrieval(userName, userAttributes, profileName);

Comment on lines +9246 to +9250
*/
protected void processAttributesAfterRetrievalWithIDWithException(String userID, Map<String, String> userAttributes,
String profileName) throws UserStoreException {

processAttributesAfterRetrievalWithID(userID, userAttributes, profileName);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Log Improvement Suggestion No: 2

Suggested change
*/
protected void processAttributesAfterRetrievalWithIDWithException(String userID, Map<String, String> userAttributes,
String profileName) throws UserStoreException {
processAttributesAfterRetrievalWithID(userID, userAttributes, profileName);
protected void processAttributesAfterRetrievalWithIDWithException(String userID, Map<String, String> userAttributes,
String profileName) throws UserStoreException {
if (log.isDebugEnabled()) {
log.debug("Processing attributes after retrieval for userID: " + userID + ", profile: " + profileName);
}
processAttributesAfterRetrievalWithID(userID, userAttributes, profileName);

Comment on lines 4857 to +4862
protected void processAttributesAfterRetrieval(String userName, Map<String, String> userStorePropertyValues,
String profileName) {

try {
processAttributesAfterRetrieval(userStorePropertyValues);
} catch (UserStoreException e) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Log Improvement Suggestion No: 3

Suggested change
protected void processAttributesAfterRetrieval(String userName, Map<String, String> userStorePropertyValues,
String profileName) {
try {
processAttributesAfterRetrieval(userStorePropertyValues);
} catch (UserStoreException e) {
protected void processAttributesAfterRetrieval(String userName, Map<String, String> userStorePropertyValues,
String profileName) {
if (logger.isDebugEnabled()) {
logger.debug("Processing attributes after retrieval for user: " + userName);
}
try {
processAttributesAfterRetrieval(userStorePropertyValues);

Comment on lines +4874 to +4876
private void processAttributesAfterRetrieval(Map<String, String> userStorePropertyValues)
throws UserStoreException {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Log Improvement Suggestion No: 4

Suggested change
private void processAttributesAfterRetrieval(Map<String, String> userStorePropertyValues)
throws UserStoreException {
throws UserStoreException {
if (logger.isDebugEnabled()) {
logger.debug("Processing attributes with timestamp conversions");
}
String timestampAttributesProperty = Optional.ofNullable(realmConfig

Comment on lines +3215 to +3220
protected void processAttributesAfterRetrievalWithID(String userID, Map<String,
String> userStorePropertyValues, String profileName) {

try {
processAttributesAfterRetrievalWithID(userStorePropertyValues);
} catch (UserStoreException e) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Log Improvement Suggestion No: 5

Suggested change
protected void processAttributesAfterRetrievalWithID(String userID, Map<String,
String> userStorePropertyValues, String profileName) {
try {
processAttributesAfterRetrievalWithID(userStorePropertyValues);
} catch (UserStoreException e) {
protected void processAttributesAfterRetrievalWithID(String userID, Map<String,
String> userStorePropertyValues, String profileName) {
log.debug("Processing attributes after retrieval for userID: " + userID);
try {
processAttributesAfterRetrievalWithID(userStorePropertyValues);
} catch (UserStoreException e) {

Comment on lines +3230 to 3233

private void processAttributesAfterRetrievalWithID(Map<String,
String> userStorePropertyValues) throws UserStoreException {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Log Improvement Suggestion No: 6

Suggested change
private void processAttributesAfterRetrievalWithID(Map<String,
String> userStorePropertyValues) throws UserStoreException {
private void processAttributesAfterRetrievalWithID(Map<String,
String> userStorePropertyValues) throws UserStoreException {
log.debug("Processing timestamp attributes for user store properties");
String timestampAttributesProperty = Optional.ofNullable(realmConfig

Comment on lines +146 to +148
public static String convertToStandardTimeFormat(RealmConfiguration realmConfig, String dateTimestamp)
throws UserStoreException {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Log Improvement Suggestion No: 7

Suggested change
public static String convertToStandardTimeFormat(RealmConfiguration realmConfig, String dateTimestamp)
throws UserStoreException {
public static String convertToStandardTimeFormat(RealmConfiguration realmConfig, String dateTimestamp)
throws UserStoreException {
if (log.isDebugEnabled()) {
log.debug("Converting LDAP timestamp to standard format");
}

Comment on lines +160 to +163
throw new UserStoreException("Invalid timestamp format for pattern: " + userstoreTimestampFormat, e);
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Log Improvement Suggestion No: 8

Suggested change
throw new UserStoreException("Invalid timestamp format for pattern: " + userstoreTimestampFormat, e);
}
}
throw new UserStoreException("Invalid timestamp format for pattern: " + userstoreTimestampFormat, e);
}
}
log.warn("Custom timestamp pattern not found or equals default, deriving pattern for timestamp");

@wso2-engineering wso2-engineering Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI Agent Log Improvement Checklist

⚠️ Warning: AI-Generated Review Comments

  • The log-related comments and suggestions in this review were generated by an AI tool to assist with identifying potential improvements. Purpose of reviewing the code for log improvements is to improve the troubleshooting capabilities of our products.
  • Please make sure to manually review and validate all suggestions before applying any changes. Not every code suggestion would make sense or add value to our purpose. Therefore, you have the freedom to decide which of the suggestions are helpful.

✅ Before merging this pull request:

  • Review all AI-generated comments for accuracy and relevance.
  • Complete and verify the table below. We need your feedback to measure the accuracy of these suggestions and the value they add. If you are rejecting a certain code suggestion, please mention the reason briefly in the suggestion for us to capture it.
Comment Accepted (Y/N) Reason
#### Log Improvement Suggestion No: 1
#### Log Improvement Suggestion No: 2
#### Log Improvement Suggestion No: 3
#### Log Improvement Suggestion No: 4
#### Log Improvement Suggestion No: 5
#### Log Improvement Suggestion No: 6
#### Log Improvement Suggestion No: 7
#### Log Improvement Suggestion No: 8

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
core/org.wso2.carbon.user.core/src/main/java/org/wso2/carbon/user/core/ldap/ReadOnlyLDAPUserStoreManager.java (1)

4917-4923: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Critical bug: Using date value as DateTimeFormatter pattern.

The method receives a date value (e.g., "20250813145607Z") but line 4919 uses this value as a DateTimeFormatter pattern via DateTimeFormatter.ofPattern(date). This will throw IllegalArgumentException at runtime because date values are not valid formatter patterns.

The original implementation likely used the constant GENARALIZE_DATE_TIME_FORMAT defined at line 143, or derivation from LDAPUtil.deriveTimestampFormat(...).

🐛 Proposed fix to use the correct pattern source

If this method should derive the pattern from the input value:

 protected String convertDateFormatFromLDAP(String date) {

-    DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(date);
+    String pattern = LDAPUtil.deriveTimestampFormat(date);
+    if (pattern == null) {
+        pattern = GENARALIZE_DATE_TIME_FORMAT;
+    }
+    DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(pattern);
     OffsetDateTime offsetDateTime = OffsetDateTime.parse(date, dateTimeFormatter);
     Instant instant = offsetDateTime.toInstant();
     return instant.toString();
 }

Or if you want to use the fixed generalized time format:

 protected String convertDateFormatFromLDAP(String date) {

-    DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(date);
+    DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(GENARALIZE_DATE_TIME_FORMAT);
     OffsetDateTime offsetDateTime = OffsetDateTime.parse(date, dateTimeFormatter);
     Instant instant = offsetDateTime.toInstant();
     return instant.toString();
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@core/org.wso2.carbon.user.core/src/main/java/org/wso2/carbon/user/core/ldap/ReadOnlyLDAPUserStoreManager.java`
around lines 4917 - 4923, The convertDateFormatFromLDAP method is mistakenly
passing the input date string as a DateTimeFormatter pattern; replace
DateTimeFormatter.ofPattern(date) with a real pattern source — e.g., use the
existing GENARALIZE_DATE_TIME_FORMAT constant or call
LDAPUtil.deriveTimestampFormat(date) to get the correct pattern, then build the
DateTimeFormatter from that pattern and parse the input (OffsetDateTime.parse or
the appropriate parse method) to produce the Instant and return its toString();
update references in convertDateFormatFromLDAP to use the derived/fixed pattern
instead of the date value.
core/org.wso2.carbon.user.core/src/main/java/org/wso2/carbon/user/core/ldap/UniqueIDReadOnlyLDAPUserStoreManager.java (1)

3243-3266: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Debug logs may expose PII.

The debug logging of userStorePropertyValues at lines 3245-3246 and 3262-3263 could expose sensitive user attributes such as email addresses, phone numbers, or other claim values. Even though debug logging is typically disabled in production, this poses a compliance risk (GDPR/CCPA) if debug is enabled during troubleshooting.

Consider logging only attribute names rather than the full map with values, or redacting sensitive claim types.

🛡️ Proposed fix to avoid logging PII
     if (ArrayUtils.isNotEmpty(timestampAttributes)) {
         if (logger.isDebugEnabled()) {
-            logger.debug("Retrieved user store properties before type conversions: " + userStorePropertyValues);
+            logger.debug("Retrieved user store properties before type conversions. Attribute keys: " + userStorePropertyValues.keySet());
         }

         Map<String, String> convertedTimestampAttributeValues = new HashMap<>();
         for (String attribute : timestampAttributes) {
             if (userStorePropertyValues.get(attribute) != null) {
                 convertedTimestampAttributeValues.put(attribute,
                         LDAPUtil.convertToStandardTimeFormat(realmConfig, userStorePropertyValues.get(attribute)));
             }
         }

         if (logger.isDebugEnabled()) {
             logger.debug("Converted timestamp attribute values: " + convertedTimestampAttributeValues);
         }

         userStorePropertyValues.putAll(convertedTimestampAttributeValues);

         if (logger.isDebugEnabled()) {
-            logger.debug("Retrieved user store properties after type conversions: " + userStorePropertyValues);
+            logger.debug("Retrieved user store properties after type conversions. Attribute keys: " + userStorePropertyValues.keySet());
         }
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@core/org.wso2.carbon.user.core/src/main/java/org/wso2/carbon/user/core/ldap/UniqueIDReadOnlyLDAPUserStoreManager.java`
around lines 3243 - 3266, The debug logs currently print full
userStorePropertyValues (in the block handling timestampAttributes) which can
expose PII; change the logger.debug calls that reference userStorePropertyValues
to log only non-sensitive information—e.g., log the set of attribute names
(userStorePropertyValues.keySet()) or a sanitized map with values redacted—while
leaving the conversion logic using timestampAttributes and
LDAPUtil.convertToStandardTimeFormat(realmConfig, ...) unchanged; ensure both
pre-conversion and post-conversion logger.debug calls for
userStorePropertyValues are replaced with the safer key-only or redacted
variant.
🧹 Nitpick comments (1)
core/org.wso2.carbon.user.core/src/main/java/org/wso2/carbon/user/core/common/AbstractUserStoreManager.java (1)

9198-9209: ⚡ Quick win

Inconsistent deprecation pattern: consider deprecating processAttributesAfterRetrieval.

You've added processAttributesAfterRetrievalWithException as the new exception-throwing variant and it delegates to processAttributesAfterRetrieval. However, unlike the WithID variants in hunk 2, you haven't deprecated the old processAttributesAfterRetrieval method. This creates an inconsistency:

  • processAttributesAfterRetrievalWithID → deprecated ✓
  • processAttributesAfterRetrieval → not deprecated ✗

For consistency and to guide subclass implementers toward the new exception-throwing API, consider marking processAttributesAfterRetrieval as @Deprecated with similar documentation pointing to processAttributesAfterRetrievalWithException.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@core/org.wso2.carbon.user.core/src/main/java/org/wso2/carbon/user/core/common/AbstractUserStoreManager.java`
around lines 9198 - 9209, Mark the old non-exception variant
processAttributesAfterRetrieval(...) as `@Deprecated` and update its Javadoc to
point implementers to the new processAttributesAfterRetrievalWithException(...)
method; keep the current delegation behavior (have
processAttributesAfterRetrieval call
processAttributesAfterRetrievalWithException or vice versa per existing
pattern), and ensure the deprecation Javadoc includes the recommended
replacement and exception guidance so subclasses are directed to implement the
exception-throwing API.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@core/org.wso2.carbon.user.core/src/main/java/org/wso2/carbon/user/core/util/LDAPUtil.java`:
- Around line 24-35: deriveTimestampFormat is failing to recognize 12-digit
timestamps without fractions (e.g., 202508131456Z); update LDAPUtil: add a
dedicated regex for 12-digit no-fraction timestamps (similar to
NO_FRACTION_TIMESTAMP_PATTERN but matching exactly 12 digits before the
timezone) and in deriveTimestampFormat(...) map that new pattern to the correct
formatter (e.g., "uuuuMMddHHmmX"/equivalent constant) alongside the existing
mappings for 14-digit and fractional patterns; also review and consolidate the
existing one-digit/two-digit/three-digit fraction patterns
(ONE_DIGIT_FRACTION_WITH_MINUTES_TIMESTAMP_PATTERN,
ONE_DIGIT_FRACTION_TIMESTAMP_PATTERN, etc.) to avoid duplicate/incorrect 12 vs
14 digit matches so the function returns the 12-digit no-fraction format instead
of falling through to the unsupported branch.
- Around line 153-161: The code only catches DateTimeParseException around the
OffsetDateTime.parse(... DateTimeFormatter.ofPattern(userstoreTimestampFormat)
...) call, but DateTimeFormatter.ofPattern(userstoreTimestampFormat) can throw
IllegalArgumentException for invalid pattern syntax; update the try/catch around
the DateTimeFormatter.ofPattern and OffsetDateTime.parse so that
IllegalArgumentException is caught and rethrown as a UserStoreException (similar
to the existing DateTimeParseException handling), referencing the
userstoreTimestampFormat variable and the DateTimeFormatter.ofPattern(...)
invocation to ensure malformed pattern errors are wrapped and reported properly.

---

Outside diff comments:
In
`@core/org.wso2.carbon.user.core/src/main/java/org/wso2/carbon/user/core/ldap/ReadOnlyLDAPUserStoreManager.java`:
- Around line 4917-4923: The convertDateFormatFromLDAP method is mistakenly
passing the input date string as a DateTimeFormatter pattern; replace
DateTimeFormatter.ofPattern(date) with a real pattern source — e.g., use the
existing GENARALIZE_DATE_TIME_FORMAT constant or call
LDAPUtil.deriveTimestampFormat(date) to get the correct pattern, then build the
DateTimeFormatter from that pattern and parse the input (OffsetDateTime.parse or
the appropriate parse method) to produce the Instant and return its toString();
update references in convertDateFormatFromLDAP to use the derived/fixed pattern
instead of the date value.

In
`@core/org.wso2.carbon.user.core/src/main/java/org/wso2/carbon/user/core/ldap/UniqueIDReadOnlyLDAPUserStoreManager.java`:
- Around line 3243-3266: The debug logs currently print full
userStorePropertyValues (in the block handling timestampAttributes) which can
expose PII; change the logger.debug calls that reference userStorePropertyValues
to log only non-sensitive information—e.g., log the set of attribute names
(userStorePropertyValues.keySet()) or a sanitized map with values redacted—while
leaving the conversion logic using timestampAttributes and
LDAPUtil.convertToStandardTimeFormat(realmConfig, ...) unchanged; ensure both
pre-conversion and post-conversion logger.debug calls for
userStorePropertyValues are replaced with the safer key-only or redacted
variant.

---

Nitpick comments:
In
`@core/org.wso2.carbon.user.core/src/main/java/org/wso2/carbon/user/core/common/AbstractUserStoreManager.java`:
- Around line 9198-9209: Mark the old non-exception variant
processAttributesAfterRetrieval(...) as `@Deprecated` and update its Javadoc to
point implementers to the new processAttributesAfterRetrievalWithException(...)
method; keep the current delegation behavior (have
processAttributesAfterRetrieval call
processAttributesAfterRetrievalWithException or vice versa per existing
pattern), and ensure the deprecation Javadoc includes the recommended
replacement and exception guidance so subclasses are directed to implement the
exception-throwing API.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 90c1896a-56b4-41b3-9cb6-500e11464afc

📥 Commits

Reviewing files that changed from the base of the PR and between 1e87b3c and 7b8d9ef.

📒 Files selected for processing (4)
  • core/org.wso2.carbon.user.core/src/main/java/org/wso2/carbon/user/core/common/AbstractUserStoreManager.java
  • core/org.wso2.carbon.user.core/src/main/java/org/wso2/carbon/user/core/ldap/ReadOnlyLDAPUserStoreManager.java
  • core/org.wso2.carbon.user.core/src/main/java/org/wso2/carbon/user/core/ldap/UniqueIDReadOnlyLDAPUserStoreManager.java
  • core/org.wso2.carbon.user.core/src/main/java/org/wso2/carbon/user/core/util/LDAPUtil.java

Comment on lines +24 to +35
// Regex patterns for LDAP timestamp formats.
private static final Pattern NO_FRACTION_TIMESTAMP_PATTERN = Pattern.compile("^\\d{14}([-+]\\d{4}|Z)$");
private static final Pattern THREE_DIGIT_FRACTION_TIMESTAMP_PATTERN =
Pattern.compile("^\\d{14}[,\\.]\\d{3}([-+]\\d{4}|Z)$");
private static final Pattern TWO_DIGIT_FRACTION_TIMESTAMP_PATTERN =
Pattern.compile("^\\d{14}[,\\.]\\d{2}([-+]\\d{4}|Z)$");
private static final Pattern ONE_DIGIT_FRACTION_TIMESTAMP_PATTERN =
Pattern.compile("^\\d{12}[,\\.]\\d{1}([-+]\\d{4}|Z)$");
private static final Pattern ONE_DIGIT_FRACTION_WITH_SECONDS_TIMESTAMP_PATTERN =
Pattern.compile("^\\d{14}[,\\.]\\d{1}([-+]\\d{4}|Z)$");
private static final Pattern ONE_DIGIT_FRACTION_WITH_MINUTES_TIMESTAMP_PATTERN =
Pattern.compile("^\\d{12}[,\\.]\\d{1}([-+]\\d{4}|Z)$");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

deriveTimestampFormat(...) misses 12-digit timestamps without fractions.

At Line 180, the documented/default pattern includes uuuuMMddHHmm[,S][.S]X, but implementation only recognizes the 12-digit variant when a 1-digit fraction exists (Lines 208-209). Inputs like 202508131456Z incorrectly fall through to Line 174 as unsupported.

💡 Proposed fix
+    private static final Pattern NO_SECONDS_NO_FRACTION_TIMESTAMP_PATTERN =
+            Pattern.compile("^\\d{12}([-+]\\d{4}|Z)$");
     private static final Pattern ONE_DIGIT_FRACTION_WITH_MINUTES_TIMESTAMP_PATTERN =
             Pattern.compile("^\\d{12}[,\\.]\\d{1}([-+]\\d{4}|Z)$");
         // Case 4: 14 digits with 1-digit fraction (seconds precision).
         else if (ONE_DIGIT_FRACTION_WITH_SECONDS_TIMESTAMP_PATTERN.matcher(timestamp).matches()) {
             return timestamp.contains(",") ? "uuuuMMddHHmmss,SX" : "uuuuMMddHHmmss.SX";
         }
-        // Case 5: 12 digits with 1-digit fraction (minutes precision).
+        // Case 5: 12 digits with no fractional seconds (minutes precision).
+        else if (NO_SECONDS_NO_FRACTION_TIMESTAMP_PATTERN.matcher(timestamp).matches()) {
+            return "uuuuMMddHHmmX";
+        }
+        // Case 6: 12 digits with 1-digit fraction (minutes precision).
         else if (ONE_DIGIT_FRACTION_WITH_MINUTES_TIMESTAMP_PATTERN.matcher(timestamp).matches()) {
             return timestamp.contains(",") ? "uuuuMMddHHmm,SX" : "uuuuMMddHHmm.SX";
         }

Also applies to: 191-210

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@core/org.wso2.carbon.user.core/src/main/java/org/wso2/carbon/user/core/util/LDAPUtil.java`
around lines 24 - 35, deriveTimestampFormat is failing to recognize 12-digit
timestamps without fractions (e.g., 202508131456Z); update LDAPUtil: add a
dedicated regex for 12-digit no-fraction timestamps (similar to
NO_FRACTION_TIMESTAMP_PATTERN but matching exactly 12 digits before the
timezone) and in deriveTimestampFormat(...) map that new pattern to the correct
formatter (e.g., "uuuuMMddHHmmX"/equivalent constant) alongside the existing
mappings for 14-digit and fractional patterns; also review and consolidate the
existing one-digit/two-digit/three-digit fraction patterns
(ONE_DIGIT_FRACTION_WITH_MINUTES_TIMESTAMP_PATTERN,
ONE_DIGIT_FRACTION_TIMESTAMP_PATTERN, etc.) to avoid duplicate/incorrect 12 vs
14 digit matches so the function returns the 12-digit no-fraction format instead
of falling through to the unsupported branch.

@Thisara-Welmilla Thisara-Welmilla force-pushed the improve-timestamp-convertion-logic branch from 7b8d9ef to aa667c8 Compare May 28, 2026 05:18

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@core/org.wso2.carbon.user.core/src/test/java/org/wso2/carbon/user/core/ldap/UniqueIDReadOnlyLDAPUserStoreManagerTest.java`:
- Around line 147-154: The test in UniqueIDReadOnlyLDAPUserStoreManagerTest is
nondeterministic because it treats both acceptance and rejection of the "+05"
timezone as passing; make it deterministic by asserting the expected behavior:
replace the try/catch block calling
LDAPUtil.convertToStandardTimeFormat(realmConfig, "20250813145607+05") with a
single assertion that it throws a UserStoreException (e.g., use
assertThrows(UserStoreException.class, () ->
LDAPUtil.convertToStandardTimeFormat(...))) so the test fails if behavior
drifts.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: a44bb96e-3a10-4210-b021-fde4c57c855b

📥 Commits

Reviewing files that changed from the base of the PR and between 7b8d9ef and aa667c8.

📒 Files selected for processing (5)
  • core/org.wso2.carbon.user.core/src/main/java/org/wso2/carbon/user/core/common/AbstractUserStoreManager.java
  • core/org.wso2.carbon.user.core/src/main/java/org/wso2/carbon/user/core/ldap/ReadOnlyLDAPUserStoreManager.java
  • core/org.wso2.carbon.user.core/src/main/java/org/wso2/carbon/user/core/ldap/UniqueIDReadOnlyLDAPUserStoreManager.java
  • core/org.wso2.carbon.user.core/src/main/java/org/wso2/carbon/user/core/util/LDAPUtil.java
  • core/org.wso2.carbon.user.core/src/test/java/org/wso2/carbon/user/core/ldap/UniqueIDReadOnlyLDAPUserStoreManagerTest.java
🚧 Files skipped from review as they are similar to previous changes (4)
  • core/org.wso2.carbon.user.core/src/main/java/org/wso2/carbon/user/core/util/LDAPUtil.java
  • core/org.wso2.carbon.user.core/src/main/java/org/wso2/carbon/user/core/ldap/ReadOnlyLDAPUserStoreManager.java
  • core/org.wso2.carbon.user.core/src/main/java/org/wso2/carbon/user/core/common/AbstractUserStoreManager.java
  • core/org.wso2.carbon.user.core/src/main/java/org/wso2/carbon/user/core/ldap/UniqueIDReadOnlyLDAPUserStoreManager.java

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant