Skip to content

Comments

Fix NPE during reset password#12585

Merged
DaanHoogland merged 2 commits intoapache:4.20from
shapeblue:reset-pswd-npe-fix
Feb 18, 2026
Merged

Fix NPE during reset password#12585
DaanHoogland merged 2 commits intoapache:4.20from
shapeblue:reset-pswd-npe-fix

Conversation

@sureshanaparti
Copy link
Contributor

Description

This PR fixes the NPE during reset password, and has some code improvements. Failed to throw proper error when there is no session while resetting password.

Fixes #12582

Types of changes

  • Breaking change (fix or feature that would cause existing functionality to change)
  • New feature (non-breaking change which adds functionality)
  • Bug fix (non-breaking change which fixes an issue)
  • Enhancement (improves an existing feature and functionality)
  • Cleanup (Code refactoring and cleanup, that may add test cases)
  • Build/CI
  • Test (unit or integration test code)

Feature/Enhancement Scale or Bug Severity

Feature/Enhancement Scale

  • Major
  • Minor

Bug Severity

  • BLOCKER
  • Critical
  • Major
  • Minor
  • Trivial

Screenshots (if appropriate):

How Has This Been Tested?

How did you try to break this feature and the system with this change?

@sureshanaparti sureshanaparti linked an issue Feb 4, 2026 that may be closed by this pull request
@sureshanaparti sureshanaparti added this to the 4.20.3 milestone Feb 4, 2026
@sureshanaparti
Copy link
Contributor Author

@blueorangutan package

@codecov
Copy link

codecov bot commented Feb 4, 2026

Codecov Report

❌ Patch coverage is 0% with 17 lines in your changes missing coverage. Please review.
✅ Project coverage is 16.25%. Comparing base (9ae696d) to head (ffebb3e).
⚠️ Report is 12 commits behind head on 4.20.

Files with missing lines Patch % Lines
...ack/api/command/SAML2LoginAPIAuthenticatorCmd.java 0.00% 6 Missing ⚠️
server/src/main/java/com/cloud/api/ApiServlet.java 0.00% 4 Missing and 1 partial ⚠️
...loud/api/auth/DefaultLoginAPIAuthenticatorCmd.java 0.00% 3 Missing ⚠️
...auth/DefaultForgotPasswordAPIAuthenticatorCmd.java 0.00% 2 Missing ⚠️
...th2/api/command/OauthLoginAPIAuthenticatorCmd.java 0.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               4.20   #12585      +/-   ##
============================================
- Coverage     16.26%   16.25%   -0.02%     
+ Complexity    13428    13419       -9     
============================================
  Files          5660     5662       +2     
  Lines        499963   500149     +186     
  Branches      60708    60731      +23     
============================================
- Hits          81330    81298      -32     
- Misses       409559   409768     +209     
- Partials       9074     9083       +9     
Flag Coverage Δ
uitests 4.15% <ø> (-0.01%) ⬇️
unittests 17.10% <0.00%> (-0.02%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Collaborator

@sudo87 sudo87 left a comment

Choose a reason for hiding this comment

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

clgtm

@DaanHoogland
Copy link
Contributor

@blueorangutan package

@blueorangutan
Copy link

@DaanHoogland a [SL] Jenkins job has been kicked to build packages. It will be bundled with KVM, XenServer and VMware SystemVM templates. I'll keep you posted as I make progress.

@blueorangutan
Copy link

Packaging result [SF]: ✔️ el8 ✔️ el9 ✔️ el10 ✔️ debian ✔️ suse15. SL-JID 16706

@DaanHoogland
Copy link
Contributor

@blueorangutan test

@blueorangutan
Copy link

@DaanHoogland a [SL] Trillian-Jenkins test job (ol8 mgmt + kvm-ol8) has been kicked to run smoke tests

@blueorangutan
Copy link

[SF] Trillian test result (tid-15383)
Environment: kvm-ol8 (x2), zone: Advanced Networking with Mgmt server ol8
Total time taken: 57460 seconds
Marvin logs: https://github.com/blueorangutan/acs-prs/releases/download/trillian/pr12585-t15383-kvm-ol8.zip
Smoke tests completed. 140 look OK, 1 have errors, 0 did not run
Only failed and skipped tests results shown below:

Test Result Time (s) Test File
test_LoginApiDomain Error 7.10 test_accounts.py

@kiranchavala
Copy link
Member

@sureshanaparti as disscussed currently there is no exception message thrown when a ldap/saml user tries to click on forgot password

logs

2026-02-11 08:14:13,377 DEBUG [c.c.a.ApiServlet] (qtp1390913202-353:[ctx-80c4b0bb]) (logid:1d4b29de) ===START===  10.0.3.251 -- POST  command=forgotPassword&response=json
2026-02-11 08:14:13,381 DEBUG [c.c.a.ApiServlet] (qtp1390913202-353:[ctx-80c4b0bb]) (logid:1d4b29de) ===END===  10.0.3.251 -- POST  command=forgotPassword&response=json


2026-02-11 08:14:13,828 DEBUG [c.c.a.ApiServlet] (qtp1390913202-19:[ctx-7af80760]) (logid:b89d08e9) ===START===  10.0.3.251 -- GET  command=forgotPassword&response=json
2026-02-11 08:14:13,828 DEBUG [c.c.a.ApiServlet] (qtp1390913202-19:[ctx-7af80760]) (logid:b89d08e9) Authentication failure: null
2026-02-11 08:14:13,829 DEBUG [c.c.a.ApiServlet] (qtp1390913202-19:[ctx-7af80760]) (logid:b89d08e9) ===END===  10.0.3.251 -- GET  command=forgotPassword&response=json

Copy link
Contributor

@shwstppr shwstppr left a comment

Choose a reason for hiding this comment

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

code lgtm

@sureshanaparti
Copy link
Contributor Author

sureshanaparti commented Feb 13, 2026

@sureshanaparti as disscussed currently there is no exception message thrown when a ldap/saml user tries to click on forgot password

logs

2026-02-11 08:14:13,377 DEBUG [c.c.a.ApiServlet] (qtp1390913202-353:[ctx-80c4b0bb]) (logid:1d4b29de) ===START===  10.0.3.251 -- POST  command=forgotPassword&response=json
2026-02-11 08:14:13,381 DEBUG [c.c.a.ApiServlet] (qtp1390913202-353:[ctx-80c4b0bb]) (logid:1d4b29de) ===END===  10.0.3.251 -- POST  command=forgotPassword&response=json


2026-02-11 08:14:13,828 DEBUG [c.c.a.ApiServlet] (qtp1390913202-19:[ctx-7af80760]) (logid:b89d08e9) ===START===  10.0.3.251 -- GET  command=forgotPassword&response=json
2026-02-11 08:14:13,828 DEBUG [c.c.a.ApiServlet] (qtp1390913202-19:[ctx-7af80760]) (logid:b89d08e9) Authentication failure: null
2026-02-11 08:14:13,829 DEBUG [c.c.a.ApiServlet] (qtp1390913202-19:[ctx-7af80760]) (logid:b89d08e9) ===END===  10.0.3.251 -- GET  command=forgotPassword&response=json

@kiranchavala updated logs as discussed.

This is usually logged when forgot password called without any username, to show/hide the ui button "Forgot password?" in login form (when the feature in enabled/disabled respectively) - added check to not log it to avoid any confusion.
> 2026-02-11 08:14:13,828 DEBUG [c.c.a.ApiServlet] (qtp1390913202-19:[ctx-7af80760]) (logid:b89d08e9) Authentication failure: null

api('forgotPassword', {}).then(response => {
this.forgotPasswordEnabled = response.forgotpasswordresponse.enabled
}).catch((err) => {
if (err?.response?.data === null) {
this.forgotPasswordEnabled = true
} else {
this.forgotPasswordEnabled = false
}
})

@sureshanaparti
Copy link
Contributor Author

@blueorangutan package

@blueorangutan
Copy link

@sureshanaparti a [SL] Jenkins job has been kicked to build packages. It will be bundled with KVM, XenServer and VMware SystemVM templates. I'll keep you posted as I make progress.

@blueorangutan
Copy link

Packaging result [SF]: ✔️ el8 ✔️ el9 ✔️ el10 ✔️ debian ✔️ suse15. SL-JID 16827

@DaanHoogland
Copy link
Contributor

@blueorangutan test

@blueorangutan
Copy link

@DaanHoogland a [SL] Trillian-Jenkins test job (ol8 mgmt + kvm-ol8) has been kicked to run smoke tests

@blueorangutan
Copy link

[SF] Trillian Build Failed (tid-15457)

@RosiKyu
Copy link
Collaborator

RosiKyu commented Feb 13, 2026

@blueorangutan test

@blueorangutan
Copy link

@RosiKyu a [SL] Trillian-Jenkins test job (ol8 mgmt + kvm-ol8) has been kicked to run smoke tests

@blueorangutan
Copy link

[SF] Trillian Build Failed (tid-15470)

@blueorangutan
Copy link

[SF] Trillian test result (tid-15459)
Environment: kvm-ol8 (x2), zone: Advanced Networking with Mgmt server ol8
Total time taken: 48031 seconds
Marvin logs: https://github.com/blueorangutan/acs-prs/releases/download/trillian/pr12585-t15459-kvm-ol8.zip
Smoke tests completed. 140 look OK, 1 have errors, 0 did not run
Only failed and skipped tests results shown below:

Test Result Time (s) Test File
test_LoginApiDomain Error 5.56 test_accounts.py

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes an NPE encountered during the password reset/forgot-password flow (notably when no HTTP session exists) and adds guardrails for password reset operations for externally-managed users (SAML/OAuth2/LDAP).

Changes:

  • Prevent ApiServlet.invalidateHttpSession from throwing when HttpSession is null.
  • Block forgotPassword for users sourced from SAML2/OAUTH2/LDAP and add related logging.
  • Minor parameter parsing hardening/cleanup in login authenticators and SAML2 login.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
server/src/main/java/com/cloud/api/auth/DefaultResetPasswordAPIAuthenticatorCmd.java Minor formatting cleanup.
server/src/main/java/com/cloud/api/auth/DefaultLoginAPIAuthenticatorCmd.java Refactors domain param parsing for login.
server/src/main/java/com/cloud/api/auth/DefaultForgotPasswordAPIAuthenticatorCmd.java Introduces APINAME const; blocks forgot-password for external auth sources; adds debug logging.
server/src/main/java/com/cloud/api/ApiServlet.java Avoid NPE by allowing null session in invalidateHttpSession; tweaks auth failure logging.
plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/api/command/SAML2LoginAPIAuthenticatorCmd.java Adds null/length checks when reading array params.
plugins/user-authenticators/oauth2/src/main/java/org/apache/cloudstack/oauth2/api/command/OauthLoginAPIAuthenticatorCmd.java Refactors domainId param parsing.
Comments suppressed due to low confidence (1)

server/src/main/java/com/cloud/api/auth/DefaultForgotPasswordAPIAuthenticatorCmd.java:120

  • _accountService.getActiveUserAccount(...) can return null; the code then calls _apiServer.forgotPassword(userAccount, userDomain) which dereferences userAccount and will throw an NPE. Please handle the null case explicitly (either return a generic success response without sending an email, or throw a controlled ServerApiException).
                final UserAccount userAccount = _accountService.getActiveUserAccount(username[0], domainId);
                if (userAccount != null && List.of(User.Source.SAML2, User.Source.OAUTH2, User.Source.LDAP).contains(userAccount.getSource())) {
                    logger.debug("Forgot Password is not allowed for the user {} from source {}", username[0], userAccount.getSource());
                    throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Forgot Password is not allowed for this user");
                }
                boolean success = _apiServer.forgotPassword(userAccount, userDomain);
                logger.debug("Forgot password request for user " + username[0] + " in domain " + domain + " is successful: " + success);

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

domainIdArr = (String[])params.get(ApiConstants.DOMAIN__ID);
}
final String[] domainName = (String[])params.get(ApiConstants.DOMAIN);
final String[] domainIdArr = (String[])params.get(ApiConstants.DOMAIN_ID);
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

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

domainId (ApiConstants.DOMAIN__ID) is still an accepted/annotated parameter name for login, but this code now only reads domainid (ApiConstants.DOMAIN_ID). This is a backward-incompatible change: requests sending domainId will no longer resolve the domain and may fail authentication. Please restore the fallback to also read ApiConstants.DOMAIN__ID (or normalize parameter keys in one place).

Suggested change
final String[] domainIdArr = (String[])params.get(ApiConstants.DOMAIN_ID);
String[] domainIdArr = (String[])params.get(ApiConstants.DOMAIN_ID);
if (domainIdArr == null || domainIdArr.length == 0) {
domainIdArr = (String[])params.get(ApiConstants.DOMAIN__ID);
}

Copilot uses AI. Check for mistakes.
@@ -177,12 +177,8 @@ private String doOauthAuthentication(HttpSession session, Long domainId, String

protected Long getDomainIdFromParams(Map<String, Object[]> params, StringBuilder auditTrailSb, String responseType) {
String[] domainIdArr = (String[])params.get(ApiConstants.DOMAIN_ID);
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

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

getDomainIdFromParams now only checks ApiConstants.DOMAIN_ID (domainid) but this API command also declares/accepts ApiConstants.DOMAIN__ID (domainId). Removing the fallback breaks clients using domainId. Please handle both keys (or centralize key normalization) to keep compatibility.

Suggested change
String[] domainIdArr = (String[])params.get(ApiConstants.DOMAIN_ID);
String[] domainIdArr = (String[])params.get(ApiConstants.DOMAIN_ID);
if (domainIdArr == null) {
// Fallback to support clients using the camelCase parameter name "domainId"
domainIdArr = (String[])params.get(ApiConstants.DOMAIN__ID);
}

Copilot uses AI. Check for mistakes.
Comment on lines 106 to 113
try {
final Domain userDomain = _domainService.findDomainByPath(domain);
if (userDomain != null) {
domainId = userDomain.getId();
} else {
logger.debug("Unable to find the domain from the path {}", domain);
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, String.format("Unable to find the domain from the path %s", domain));
}
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

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

The parameter description says that if no domain is passed, ROOT (/) is assumed, but this implementation calls _domainService.findDomainByPath(domain) and throws when domain is null/blank. Please default to ROOT when domain is missing (e.g., use findDomainByIdOrPath(null, domain) or explicitly set domainId to Domain.ROOT_DOMAIN).

Copilot uses AI. Check for mistakes.
Copy link
Member

@kiranchavala kiranchavala left a comment

Choose a reason for hiding this comment

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

LGTM

Tested manually , try forgot password on ldap user account ,

No NPE found

2026-02-18 12:04:36,869 DEBUG [c.c.a.ApiServlet] (qtp698741991-23:[ctx-654d9184]) (logid:ff1c91d0) ===START===  10.0.3.251 -- POST  command=forgotPassword&response=json
2026-02-18 12:04:36,872 DEBUG [c.c.a.a.DefaultForgotPasswordAPIAuthenticatorCmd] (qtp698741991-23:[ctx-654d9184]) (logid:ff1c91d0) Forgot Password is not allowed for the user kiran from source LDAP
2026-02-18 12:04:36,873 DEBUG [c.c.a.ApiServlet] (qtp698741991-23:[ctx-654d9184]) (logid:ff1c91d0) ===END===  10.0.3.251 -- POST  command=forgotPassword&response=json
2026-02-18 12:04:36,892 INFO  [c.c.c.ClusterManagerImpl] (Cluster-Heartbeat-1:[ctx-33571f8a]) (logid:9e1f1b1a) No inactive management server node found
2026-02-18 12:04:36,893 DEBUG [c.c.c.ClusterManagerImpl] (Cluster-Heartbeat-1:[ctx-33571f8a]) (logid:9e1f1b1a) Peer scan is finished. profiler: Done. Duration: 1ms , profilerQueryActiveList: Done. Duration: 0ms, , profilerSyncClusterInfo: Done. Duration: 0ms, profilerInvalidatedNodeList: Done. Duration: 0ms, profilerRemovedList: Done. Duration: 0ms,, profilerNewList: Done. Duration: 0ms, profilerInactiveList: Done. Duration: 0ms

Normal user

2026-02-18 12:06:47,284 DEBUG [c.c.a.ApiServlet] (qtp698741991-22:[ctx-edc24db8]) (logid:3f0a9791) ===START===  10.0.3.251 -- POST  command=forgotPassword&response=json
2026-02-18 12:06:47,397 INFO  [c.c.c.ClusterManagerImpl] (Cluster-Heartbeat-1:[ctx-dc0dc52a]) (logid:065d5f24) No inactive management server node found
2026-02-18 12:06:47,397 DEBUG [c.c.c.ClusterManagerImpl] (Cluster-Heartbeat-1:[ctx-dc0dc52a]) (logid:065d5f24) Peer scan is finished. profiler: Done. Duration: 4ms , profilerQueryActiveList: Done. Duration: 0ms, , profilerSyncClusterInfo: Done. Duration: 2ms, profilerInvalidatedNodeList: Done. Duration: 0ms, profilerRemovedList: Done. Duration: 0ms,, profilerNewList: Done. Duration: 0ms, profilerInactiveList: Done. Duration: 1ms
2026-02-18 12:06:47,400 DEBUG [o.a.c.h.H.HAManagerBgPollTask] (BackgroundTaskPollManager-3:[ctx-6359616b]) (logid:10e168a2) HA health check task is running...
2026-02-18 12:06:47,447 DEBUG [o.a.c.u.UserPasswordResetManagerImpl] (qtp698741991-22:[ctx-edc24db8]) (logid:3f0a9791) User password reset email for user UserAccount {"accountName":"kirannormal","id":6,"username":"kirannormal"}. account id: 6 domain id: 1 sent to test@example.com with token expiry at 2026-02-18T12:36:47.290+0000
2026-02-18 12:06:47,450 DEBUG [c.c.a.a.DefaultForgotPasswordAPIAuthenticatorCmd] (qtp698741991-22:[ctx-edc24db8]) (logid:3f0a9791) Forgot password request for user kirannormal in domain / is successful: true

@DaanHoogland DaanHoogland modified the milestones: 4.20.4, 4.20.3 Feb 18, 2026
@DaanHoogland DaanHoogland merged commit 8c12a13 into apache:4.20 Feb 18, 2026
29 of 32 checks passed
@DaanHoogland DaanHoogland deleted the reset-pswd-npe-fix branch February 18, 2026 19:11
@weizhouapache
Copy link
Member

it looks like simulator CI and smoke test failure are caused by this

image image

can you fix it @sureshanaparti ?

cc @DaanHoogland @abh1sar

@sureshanaparti
Copy link
Contributor Author

it looks like simulator CI and smoke test failure are caused by this

can you fix it @sureshanaparti ?

cc @DaanHoogland @abh1sar

@DaanHoogland @abh1sar fixed here - #12689

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Reset password for SAML and LDAP users results in a NPE

9 participants