Skip to content

[OB4] Removing session data key consent from the cache#915

Merged
hasithakn merged 1 commit intowso2:mainfrom
aka4rKO:main-remove-consent-data
Mar 13, 2026
Merged

[OB4] Removing session data key consent from the cache#915
hasithakn merged 1 commit intowso2:mainfrom
aka4rKO:main-remove-consent-data

Conversation

@aka4rKO
Copy link
Contributor

@aka4rKO aka4rKO commented Mar 12, 2026

[OB4] Removing session data key consent from the cache

Currently we are not removing the session data key consent from the cache during the persist flow, hence this endpoint can be invoked multiple times and can be used to update the consent status. This poses a security threat as the consent status can be updated event after persisting the consent.

Issue link: https://github.com/wso2-enterprise/wso2-ob-internal/issues/2382

Doc Issue: Optional, link issue from documentation repository

Applicable Labels: Spec, product, version, type (specify requested labels)


Development Checklist

  1. Built complete solution with pull request in place.
  2. Ran checkstyle plugin with pull request in place.
  3. Ran Findbugs plugin with pull request in place.
  4. Formatted code according to WSO2 code style.
  5. Migration scripts written (if applicable).

Secure Development Checklist

  1. Ran FindSecurityBugs plugin and verified report.
  2. Ran Dependency-check plugin and verified report for new dependencies added.
  3. Ran Dependency-check plugin and verified report for dependency version changes.
  4. Have you verify the PR does't commit any keys, passwords, tokens, usernames, or other secrets?
  5. Have you followed secure coding standards in WSO2 Secure Engineering Guidelines?

Testing Checklist

  1. Written unit tests.
  2. Documented test scenarios(link available in guides).
  3. Written automation tests (link available in guides).
  4. Verified tests in multiple database environments (if applicable).
  5. Verified tests in multiple deployed specifications (if applicable).
  6. Tested with OBBI enabled (if applicable).
  7. Tested with specification regulatory conformance suites (if applicable).

Automation Test Details

Test Suite Test Script IDs
Integration Suite TCXXXXX, TCXXXX

Conformance Tests Details

Test Suite Name Test Suite Version Scenarios Result
Security Suite VX.X Foo, Bar Passed

Summary by CodeRabbit

  • Bug Fixes
    • Improved consent persistence reliability by preventing duplicate processing attempts within the same session through enhanced cache management during the authorization flow.

@@ -256,6 +256,13 @@ public Response persist(@Context HttpServletRequest request, @Context HttpServle
throws ConsentException, ConsentManagementException, URISyntaxException {

ConsentData consentData = ConsentCache.getConsentDataFromCache(sessionDataKey);
Copy link
Contributor

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
ConsentData consentData = ConsentCache.getConsentDataFromCache(sessionDataKey);
ConsentData consentData = ConsentCache.getConsentDataFromCache(sessionDataKey);
if (consentData == null) {
log.warn("No consent data found in cache for sessionDataKey: {}", sessionDataKey);
} else {
log.debug("Retrieved consent data from cache for sessionDataKey: {}", sessionDataKey);
}

Remove consent data from the cache to prevent multiple calls to the persist endpoint.
This ensures the flow is completed and avoids further updates after a single persistence.
*/
ConsentCache.removeFromCache(sessionDataKey);
Copy link
Contributor

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
ConsentCache.removeFromCache(sessionDataKey);
ConsentCache.removeFromCache(sessionDataKey);
log.info("Removed consent data from cache for sessionDataKey: {}", sessionDataKey);

Comment on lines +230 to +234
*/
public static void removeFromCache(String cacheKey) {

ConsentCache.getInstance().removeFromCache(ConsentCacheKey.of(cacheKey));
}
Copy link
Contributor

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
*/
public static void removeFromCache(String cacheKey) {
ConsentCache.getInstance().removeFromCache(ConsentCacheKey.of(cacheKey));
}
public static void removeFromCache(String cacheKey) {
log.info("Removing consent from cache for key: " + cacheKey);
ConsentCache.getInstance().removeFromCache(ConsentCacheKey.of(cacheKey));
}

Copy link
Contributor

@wso2-engineering wso2-engineering bot left a comment

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

@coderabbitai
Copy link

coderabbitai bot commented Mar 12, 2026

Walkthrough

The PR adds cache cleanup functionality to the consent management flow by introducing a new removeFromCache() method in ConsentCache and calling it in the ConsentAuthorizeEndpoint persist endpoint to clear cached consent data after retrieval, preventing subsequent updates from the same session.

Changes

Cohort / File(s) Summary
Cache Cleanup Implementation
org.wso2.financial.services.accelerator.consent.mgt.endpoint/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/endpoint/utils/ConsentCache.java
Added new public static method removeFromCache(String cacheKey) to explicitly remove cache entries by session data key, delegating to the singleton instance.
Persist Endpoint Cache Integration
org.wso2.financial.services.accelerator.consent.mgt.endpoint/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/endpoint/api/ConsentAuthorizeEndpoint.java
Integrated cache cleanup by calling ConsentCache.removeFromCache(sessionDataKey) immediately after retrieving ConsentData to prevent multiple persists and finalize the flow.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 A burrow of data, so cluttered and deep,
We hop through the cache, our secrets to keep,
But when the moment of truth comes to pass,
We clear it all out—whoosh!—sweep the glass,
No echoes remain of what once was cached,
The flow is now final, the session detached!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: removing session data key consent from the cache during persist flow.
Description check ✅ Passed The description includes the required title, issue link, and explains the purpose/motivation. However, critical security testing checklists and secure development verification items are largely unchecked, and no concrete test results are provided.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
financial-services-accelerator/internal-webapps/org.wso2.financial.services.accelerator.consent.mgt.endpoint/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/endpoint/api/ConsentAuthorizeEndpoint.java (1)

268-292: ⚠️ Potential issue | 🟠 Major

Pre-existing NPE risk when consent data is unavailable.

This block is reached when consentData is null, yet lines 273 and 289 access consentData.getRedirectURI() and consentData.getState(), which will throw NullPointerException. This is pre-existing code, but your cache removal change may increase the likelihood of hitting this path (e.g., on retries or race conditions).

Consider addressing this in a follow-up to avoid masking the root cause with an NPE:

🐛 Suggested fix for null safety
 if (consentData == null) {
     if (ConsentConstants.STORE_CONSENT) {
         Map<String, String> consentDetailsMap = consentCoreService
                 .getConsentAttributesByName(sessionDataKey);
         if (consentDetailsMap.isEmpty()) {
-            throw new ConsentException(consentData.getRedirectURI(),
-                    AuthErrorCode.SERVER_ERROR,
-                    "Unable to get consent data", consentData.getState());
+            throw new ConsentException(null,
+                    AuthErrorCode.SERVER_ERROR,
+                    "Unable to get consent data", null);
         }
         // ... retrieval logic ...
     } else {
-        throw new ConsentException(consentData.getRedirectURI(),
-                AuthErrorCode.SERVER_ERROR, "Unable to get consent data",
-                consentData.getState());
+        throw new ConsentException(null,
+                AuthErrorCode.SERVER_ERROR, "Unable to get consent data",
+                null);
     }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@financial-services-accelerator/internal-webapps/org.wso2.financial.services.accelerator.consent.mgt.endpoint/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/endpoint/api/ConsentAuthorizeEndpoint.java`
around lines 268 - 292, The null-pointer risk occurs inside
ConsentAuthorizeEndpoint when consentData is null but the code still calls
consentData.getRedirectURI() and consentData.getState() to construct
ConsentException; change the null-unsafe exception creation to use safe
fallbacks (e.g., extract redirectURI/state from consentDetailsMap or
consentDetails/ sessionDataKey, or pass null/empty strings) before throwing, and
ensure you only call ConsentUtils.getConsentDataFromAttributes and access
consentData methods after verifying consentData != null (references:
ConsentAuthorizeEndpoint, consentCoreService.getConsentAttributesByName,
ConsentUtils.getConsentDataFromAttributes, ConsentException).
🧹 Nitpick comments (1)
financial-services-accelerator/internal-webapps/org.wso2.financial.services.accelerator.consent.mgt.endpoint/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/endpoint/utils/ConsentCache.java (1)

227-234: Implementation looks good; consider null safety.

The method follows the established patterns in this class. One minor consideration: if cacheKey is null, the behavior depends on ConsentCacheKey.of() implementation. If upstream callers can pass null, a guard clause would make failures more explicit.

🛡️ Optional: Add null check
 public static void removeFromCache(String cacheKey) {
 
+    if (cacheKey == null) {
+        log.warn("Attempted to remove null key from cache");
+        return;
+    }
     ConsentCache.getInstance().removeFromCache(ConsentCacheKey.of(cacheKey));
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@financial-services-accelerator/internal-webapps/org.wso2.financial.services.accelerator.consent.mgt.endpoint/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/endpoint/utils/ConsentCache.java`
around lines 227 - 234, The removeFromCache method should guard against a null
cacheKey to avoid delegating a null to ConsentCacheKey.of(); update
ConsentCache.removeFromCache(String) to check if cacheKey is null (or add the
check in the static wrapper) and return early or throw an
IllegalArgumentException, e.g., validate cacheKey at the start of
removeFromCache and handle null explicitly so ConsentCacheKey.of(cacheKey) is
not called with null; refer to the static method removeFromCache and the
ConsentCacheKey.of(...) call when applying the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@financial-services-accelerator/internal-webapps/org.wso2.financial.services.accelerator.consent.mgt.endpoint/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/endpoint/api/ConsentAuthorizeEndpoint.java`:
- Around line 258-264: There is a race between
ConsentCache.getConsentDataFromCache(sessionDataKey) and
ConsentCache.removeFromCache(sessionDataKey) allowing two threads to both read
consentData; change the cache API to provide an atomic get-and-remove operation
(e.g., add and call ConsentCache.getAndRemove(sessionDataKey) or similar) and
update ConsentAuthorizeEndpoint to use that atomic method instead of separate
get and remove calls so a single thread obtains and clears the entry atomically.

---

Outside diff comments:
In
`@financial-services-accelerator/internal-webapps/org.wso2.financial.services.accelerator.consent.mgt.endpoint/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/endpoint/api/ConsentAuthorizeEndpoint.java`:
- Around line 268-292: The null-pointer risk occurs inside
ConsentAuthorizeEndpoint when consentData is null but the code still calls
consentData.getRedirectURI() and consentData.getState() to construct
ConsentException; change the null-unsafe exception creation to use safe
fallbacks (e.g., extract redirectURI/state from consentDetailsMap or
consentDetails/ sessionDataKey, or pass null/empty strings) before throwing, and
ensure you only call ConsentUtils.getConsentDataFromAttributes and access
consentData methods after verifying consentData != null (references:
ConsentAuthorizeEndpoint, consentCoreService.getConsentAttributesByName,
ConsentUtils.getConsentDataFromAttributes, ConsentException).

---

Nitpick comments:
In
`@financial-services-accelerator/internal-webapps/org.wso2.financial.services.accelerator.consent.mgt.endpoint/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/endpoint/utils/ConsentCache.java`:
- Around line 227-234: The removeFromCache method should guard against a null
cacheKey to avoid delegating a null to ConsentCacheKey.of(); update
ConsentCache.removeFromCache(String) to check if cacheKey is null (or add the
check in the static wrapper) and return early or throw an
IllegalArgumentException, e.g., validate cacheKey at the start of
removeFromCache and handle null explicitly so ConsentCacheKey.of(cacheKey) is
not called with null; refer to the static method removeFromCache and the
ConsentCacheKey.of(...) call when applying the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f45192ea-76ba-457d-b07d-7e6e2d33223b

📥 Commits

Reviewing files that changed from the base of the PR and between d08c5ec and adc5c88.

📒 Files selected for processing (2)
  • financial-services-accelerator/internal-webapps/org.wso2.financial.services.accelerator.consent.mgt.endpoint/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/endpoint/api/ConsentAuthorizeEndpoint.java
  • financial-services-accelerator/internal-webapps/org.wso2.financial.services.accelerator.consent.mgt.endpoint/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/endpoint/utils/ConsentCache.java

Comment on lines 258 to +264
ConsentData consentData = ConsentCache.getConsentDataFromCache(sessionDataKey);

/*
Remove consent data from the cache to prevent multiple calls to the persist endpoint.
This ensures the flow is completed and avoids further updates after a single persistence.
*/
ConsentCache.removeFromCache(sessionDataKey);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if the base cache class has an atomic getAndRemove operation that could be used
ast-grep --pattern $'class FinancialServicesBaseCache {
  $$$
}'

# Also check for any existing atomic cache operations in the codebase
rg -n "getAndRemove|removeAndGet|atomicRemove" --type java

Repository: wso2/financial-services-accelerator

Length of output: 61


🏁 Script executed:

# Find the ConsentCache class
fd -type f -name "*ConsentCache*" --type f

Repository: wso2/financial-services-accelerator

Length of output: 249


🏁 Script executed:

# Search for ConsentCache class definition and methods
rg -n "class ConsentCache|public.*get.*Cache|public.*remove.*Cache" --type java -A 3

Repository: wso2/financial-services-accelerator

Length of output: 26398


🏁 Script executed:

# Look at the complete flow in ConsentAuthorizeEndpoint to understand the race window
rg -n "getConsentDataFromCache|removeFromCache|persistConsent" --type java -B 2 -A 2

Repository: wso2/financial-services-accelerator

Length of output: 14580


🏁 Script executed:

# Get the full implementation of FinancialServicesBaseCache to understand synchronization
cat financial-services-accelerator/components/org.wso2.financial.services.accelerator.common/src/main/java/org/wso2/financial/services/accelerator/common/caching/FinancialServicesBaseCache.java | head -200

Repository: wso2/financial-services-accelerator

Length of output: 6279


🏁 Script executed:

# Check if there's any synchronization in the ConsentCache specifically
rg -n "synchronized" financial-services-accelerator/internal-webapps/org.wso2.financial.services.accelerator.consent.mgt.endpoint/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/endpoint/utils/ConsentCache.java

Repository: wso2/financial-services-accelerator

Length of output: 129


🏁 Script executed:

# Look at the complete ConsentAuthorizeEndpoint flow around the cache removal to understand persistence logic
sed -n '250,380p' financial-services-accelerator/internal-webapps/org.wso2.financial.services.accelerator.consent.mgt.endpoint/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/endpoint/api/ConsentAuthorizeEndpoint.java

Repository: wso2/financial-services-accelerator

Length of output: 7306


🏁 Script executed:

# Check the git history to see if lines 273, 284, 289 were modified in this PR
git log -p --follow -S "consentData.getRedirectURI()" financial-services-accelerator/internal-webapps/org.wso2.financial.services.accelerator.consent.mgt.endpoint/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/endpoint/api/ConsentAuthorizeEndpoint.java | head -100

Repository: wso2/financial-services-accelerator

Length of output: 61


🏁 Script executed:

# Show the exact context around line 273 and 284 to understand the NPE issue
sed -n '268,295p' financial-services-accelerator/internal-webapps/org.wso2.financial.services.accelerator.consent.mgt.endpoint/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/endpoint/api/ConsentAuthorizeEndpoint.java

Repository: wso2/financial-services-accelerator

Length of output: 1655


Race condition window remains between retrieval and removal.

The cache removal improves security but two concurrent requests can both retrieve valid consentData before either executes the removal. For stronger guarantees, consider implementing an atomic get-and-remove operation in the cache layer.

If atomic operations aren't feasible, this fix still meaningfully narrows the attack window and is acceptable for merge. The database cleanup in the finally block (lines 367-380) provides an additional layer of protection.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@financial-services-accelerator/internal-webapps/org.wso2.financial.services.accelerator.consent.mgt.endpoint/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/endpoint/api/ConsentAuthorizeEndpoint.java`
around lines 258 - 264, There is a race between
ConsentCache.getConsentDataFromCache(sessionDataKey) and
ConsentCache.removeFromCache(sessionDataKey) allowing two threads to both read
consentData; change the cache API to provide an atomic get-and-remove operation
(e.g., add and call ConsentCache.getAndRemove(sessionDataKey) or similar) and
update ConsentAuthorizeEndpoint to use that atomic method instead of separate
get and remove calls so a single thread obtains and clears the entry atomically.

@hasithakn hasithakn merged commit 3980f4a into wso2:main Mar 13, 2026
3 checks passed
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.

3 participants