apache / apache/cloudstack

NPE in ApiServlet.skip2FAcheckForUser on SAML login when 2FA is disabled (4.22.0.0)

Offen Anfängerfreundlich
#13,172 4 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen
component:saml type:bug
Vorherrschende Sprache
Java
Sterne
3.1k
Forks
1.4k
Ø Merge
6 T. 19 Std.
Gemergte PRs (30 T.)
32

Beschreibung

### problem

##### COMPONENT NAME
API, SAML

##### SUMMARY
Every API request issued after a successful `samlSso` callback throws a `NullPointerException` in `ApiServlet.skip2FAcheckForUser`, because `session.getAttribute("2FAuthenticated")` returns `null` and is unboxed directly to `boolean`. The SAML login flow does not set this session attribute, and the 2FA-disabled global settings are not consulted before the unboxing.

Local username/password login is unaffected — only the SAML path triggers the NPE.

##### STEPS TO REPRODUCE
1. Enable SAML2 plugin and configure an IdP (Keycloak in our case).
2. Ensure `enable.user.2fa=false` and `mandate.user.2fa=false`.
3. Authorize a CloudStack user for SAML via `authorizeSamlSso`.
4. Click "Login with SSO" → authenticate at IdP → redirected back to `/client/api?command=samlSso`.
5. Browser immediately fires follow-up API call (e.g. `listIdps`, `login`, etc.).

##### EXPECTED RESULTS
The follow-up API request completes; user lands on the dashboard. A `null` value for the `2FAuthenticated` session attribute should be treated as "2FA not required / not completed" rather than dereferenced as a `boolean`.

##### ACTUAL RESULTS
Use cannot login via SAML:

```java
2026-05-17 10:22:39,650 ERROR [c.c.a.ApiServlet] (qtp1047478056-364:[ctx-50e47dca]) (logid:59e6408b) unknown exception writing api response java.lang.NullPointerException: Cannot invoke "java.lang.Boolean.booleanValue()" because the return value of "javax.servlet.http.HttpSession.getAttribute(String)" is null
at com.cloud.api.ApiServlet.skip2FAcheckForUser(ApiServlet.java:512)
at com.cloud.api.ApiServlet.processRequestInContext(ApiServlet.java:361)
at com.cloud.api.ApiServlet$1.run(ApiServlet.java:193)
at org.apache.cloudstack.managed.context.impl.DefaultManagedContext$1.call(DefaultManagedContext.java:56)
at org.apache.cloudstack.managed.context.impl.DefaultManagedContext.callWithContext(DefaultManagedContext.java:103)
at org.apache.cloudstack.managed.context.impl.DefaultManagedContext.runWithContext(DefaultManagedContext.java:53)
at com.cloud.api.ApiServlet.processRequest(ApiServlet.java:190)
at com.cloud.api.ApiServlet.doPost(ApiServlet.java:149)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:665)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:750)
... (Jetty frames truncated)
```

Client receives the auth failure / error response, lands back on the login screen.

This is my first ticket here, so be gentle please!

### versions

4.22.0 on Debian 13.4 via shapeblue

i cloudstack-common 4.22.0.0-shapeblue0 all A common package which contains files which are shared by several CloudStack packages
ii cloudstack-management 4.22.0.0-shapeblue0 all CloudStack server library
ii cloudstack-usage 4.22.0.0-shapeblue0 all CloudStack usage monitor

And just auto installed java 21 (I know the docs says 17, but I haven't been able to downgrade).

ii default-jdk-headless 2:1.21-76 amd64 Standard Java or Java compatible Development Kit (headless)
ii openjdk-21-jdk-headless:amd64 21.0.11+10-1~deb13u2 amd64 OpenJDK Development Kit (JDK) (headless)
ii openjdk-21-jre-headless:amd64 21.0.11+10-1~deb13u2 amd64 OpenJDK Java runtime, using Hotspot JIT (headless)

##### CONFIGURATION

- SAML2 plugin enabled (`saml2.enabled=true`)
- IdP: Keycloak 26.x, realm `lluw`, SAML client with dedicated `uid` mapper (User Property: `email`)
- 2FA disabled globally: `enable.user.2fa=false`, `mandate.user.2fa=false`
- Single management server, MySQL backend

### The steps to reproduce the bug

1. Enable SAML2 plugin and configure an IdP (Keycloak in our case).
2. Ensure `enable.user.2fa=false` and `mandate.user.2fa=false`.
3. Authorize a CloudStack user for SAML via `authorizeSamlSso`.
4. Click "Login with SSO" → authenticate at IdP → redirected back to `/client/api?command=samlSso`.

5 browser response:
```xml

531

Your authenticated user is not authorized for SAML Single Sign-On, please contact your administrator

```

##### LOG response

```java
2026-05-17 10:22:39,650 ERROR [c.c.a.ApiServlet] (qtp1047478056-364:[ctx-50e47dca]) (logid:59e6408b) unknown exception writing api response java.lang.NullPointerException: Cannot invoke "java.lang.Boolean.booleanValue()" because the return value of "javax.servlet.http.HttpSession.getAttribute(String)" is null
at com.cloud.api.ApiServlet.skip2FAcheckForUser(ApiServlet.java:512)
at com.cloud.api.ApiServlet.processRequestInContext(ApiServlet.java:361)
at com.cloud.api.ApiServlet$1.run(ApiServlet.java:193)
at org.apache.cloudstack.managed.context.impl.DefaultManagedContext$1.call(DefaultManagedContext.java:56)
at org.apache.cloudstack.managed.context.impl.DefaultManagedContext.callWithContext(DefaultManagedContext.java:103)
at org.apache.cloudstack.managed.context.impl.DefaultManagedContext.runWithContext(DefaultManagedContext.java:53)
at com.cloud.api.ApiServlet.processRequest(ApiServlet.java:190)
at com.cloud.api.ApiServlet.doPost(ApiServlet.java:149)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:665)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:750)
... (Jetty frames truncated)
```

### What to do about it?

### Expected result

The follow-up API request completes; user lands on the dashboard. A `null` value for the `2FAuthenticated` session attribute should be treated as "2FA not required / not completed" rather than dereferenced as a `boolean`.

- `ApiServlet.java:512` reads the `2FAuthenticated` attribute and unboxes without a null check.
- The SAML auth command (`SAML2LoginAPIAuthenticatorCmd`) creates the session but does not set `2FAuthenticated`.
- Suggested fix: replace direct unboxing with `Boolean.TRUE.equals(session.getAttribute("2FAuthenticated"))`, or short-circuit when both `enable.user.2fa` and `mandate.user.2fa` are false.

Beitragsleitfaden

Beitragsleitfaden öffnen

Rechercherichtung

Beginne in ApiServlet.java bei Zeile 512 und untersuche, wie skip2FAcheckForUser das 2FAuthenticated-Sitzungsattribut liest. Verfolge anschließend SAML2LoginAPIAuthenticatorCmd, um die Sitzungseinrichtung mit dem lokalen Anmeldeablauf zu vergleichen. Reproduziere das Problem mit aktiviertem SAML2 und deaktivierten beiden 2FA-Einstellungen; fertig ist die Änderung, wenn die nachfolgende API-Anfrage ohne die NullPointerException erfolgreich ist und der Benutzer das Dashboard erreicht.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
java
Bereich
api, authentication, backend
Issue-Typ
Bug
Schwierigkeit
2/5
Geschätzter Aufwand
1-3 Stunden
Aktivitätsstatus
Aktiv
Klarheit
Klar beschrieben
Anfängerfreundlichkeit
78/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.