This is an automated email from the ASF dual-hosted git repository.

RosiKyu pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/main by this push:
     new 6982fac34a6 Fix NPE on first LDAP login (#14098)
6982fac34a6 is described below

commit 6982fac34a6ebcf7219455de9ae01fa0f4bd5d27
Author: XuJian <[email protected]>
AuthorDate: Thu Nov 12 16:59:38 2026 +0800

    Fix NPE on first LDAP login (#14098)
    
    Signed-off-by: xujiantop-crypto 
<[email protected]>
---
 .../api/auth/DefaultLoginAPIAuthenticatorCmd.java  |   4 +-
 .../auth/DefaultLoginAPIAuthenticatorCmdTest.java  | 105 +++++++++++++++++++++
 2 files changed, 108 insertions(+), 1 deletion(-)

diff --git 
a/server/src/main/java/com/cloud/api/auth/DefaultLoginAPIAuthenticatorCmd.java 
b/server/src/main/java/com/cloud/api/auth/DefaultLoginAPIAuthenticatorCmd.java
index 0d1e1d96674..25d9a7f35be 100644
--- 
a/server/src/main/java/com/cloud/api/auth/DefaultLoginAPIAuthenticatorCmd.java
+++ 
b/server/src/main/java/com/cloud/api/auth/DefaultLoginAPIAuthenticatorCmd.java
@@ -155,7 +155,9 @@ public class DefaultLoginAPIAuthenticatorCmd extends 
BaseCmd implements APIAuthe
                 }
                 serializedResponse = 
ApiResponseSerializer.toSerializedString(_apiServer.loginUser(session, 
username[0], pwd, domainId, domain, remoteAddress, params),
                         responseType);
-                userDetailsDao.removeDetail(userAccount.getId(), 
UserDetailVO.OauthLogin);
+                if (userAccount != null) {
+                    userDetailsDao.removeDetail(userAccount.getId(), 
UserDetailVO.OauthLogin);
+                }
                 return serializedResponse;
             } catch (final CloudAuthenticationException ex) {
                 ApiServlet.invalidateHttpSession(session, "fall through to API 
key,");
diff --git 
a/server/src/test/java/com/cloud/api/auth/DefaultLoginAPIAuthenticatorCmdTest.java
 
b/server/src/test/java/com/cloud/api/auth/DefaultLoginAPIAuthenticatorCmdTest.java
new file mode 100644
index 00000000000..59af702b22a
--- /dev/null
+++ 
b/server/src/test/java/com/cloud/api/auth/DefaultLoginAPIAuthenticatorCmdTest.java
@@ -0,0 +1,105 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.api.auth;
+
+import com.cloud.domain.Domain;
+import com.cloud.user.AccountService;
+import com.cloud.user.DomainService;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiServerService;
+import org.apache.cloudstack.api.response.LoginCmdResponse;
+import org.apache.cloudstack.resourcedetail.dao.UserDetailsDao;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.net.InetAddress;
+import java.util.HashMap;
+import java.util.Map;
+
+@RunWith(MockitoJUnitRunner.class)
+public class DefaultLoginAPIAuthenticatorCmdTest {
+
+    private static final String USERNAME = "ldap-user";
+    private static final long DOMAIN_ID = 2L;
+
+    @Mock
+    private ApiServerService apiServer;
+
+    @Mock
+    private AccountService accountService;
+
+    @Mock
+    private DomainService domainService;
+
+    @Mock
+    private UserDetailsDao userDetailsDao;
+
+    @Mock
+    private HttpSession session;
+
+    @Mock
+    private HttpServletRequest request;
+
+    @Mock
+    private HttpServletResponse response;
+
+    @Mock
+    private Domain domain;
+
+    private DefaultLoginAPIAuthenticatorCmd command;
+
+    @Before
+    public void setUp() {
+        command = new DefaultLoginAPIAuthenticatorCmd();
+        command._apiServer = apiServer;
+        command._accountService = accountService;
+        command._domainService = domainService;
+        command.userDetailsDao = userDetailsDao;
+    }
+
+    @Test
+    public void testAuthenticateFirstLdapLoginWithoutExistingUser() throws 
Exception {
+        Map<String, Object[]> params = new HashMap<>();
+        params.put(ApiConstants.USERNAME, new String[] {USERNAME});
+        params.put(ApiConstants.PASSWORD, new String[] {"password"});
+        LoginCmdResponse loginResponse = new LoginCmdResponse();
+        loginResponse.setResponseName("loginresponse");
+
+        Mockito.when(request.getMethod()).thenReturn("POST");
+        Mockito.when(apiServer.getDomainId(params)).thenReturn(null);
+        Mockito.when(domainService.findDomainByIdOrPath(null, 
null)).thenReturn(domain);
+        Mockito.when(domain.getId()).thenReturn(DOMAIN_ID);
+        Mockito.when(accountService.getActiveUserAccount(USERNAME, 
DOMAIN_ID)).thenReturn(null);
+        Mockito.when(apiServer.loginUser(Mockito.eq(session), 
Mockito.eq(USERNAME), Mockito.eq("password"),
+                Mockito.eq(DOMAIN_ID), Mockito.isNull(), 
Mockito.any(InetAddress.class), Mockito.eq(params)))
+                .thenReturn(loginResponse);
+
+        String result = command.authenticate("login", params, session, 
InetAddress.getLoopbackAddress(),
+                "json", new StringBuilder(), request, response);
+
+        Assert.assertEquals("{\"loginresponse\":{}}", result);
+        Mockito.verify(userDetailsDao, 
Mockito.never()).removeDetail(Mockito.anyLong(), Mockito.anyString());
+    }
+}

Reply via email to