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

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


The following commit(s) were added to refs/heads/4.19 by this push:
     new b93589b5bdf server: reset 2fa user configuration on incomplete setup 
(#10247)
b93589b5bdf is described below

commit b93589b5bdf52043127a9c898d6aa596b0245c4c
Author: Abhishek Kumar <[email protected]>
AuthorDate: Thu Jan 30 20:21:04 2025 +0530

    server: reset 2fa user configuration on incomplete setup (#10247)
    
    Signed-off-by: Abhishek Kumar <[email protected]>
---
 .../main/java/com/cloud/user/UserAccountVO.java    |  7 +++
 .../contrail/management/MockAccountManager.java    |  5 +++
 server/src/main/java/com/cloud/api/ApiServer.java  |  3 +-
 .../main/java/com/cloud/user/AccountManager.java   |  3 ++
 .../java/com/cloud/user/AccountManagerImpl.java    | 22 +++++++++
 .../com/cloud/user/AccountManagerImplTest.java     | 52 ++++++++++++++++++++++
 .../com/cloud/user/MockAccountManagerImpl.java     |  4 ++
 7 files changed, 95 insertions(+), 1 deletion(-)

diff --git a/engine/schema/src/main/java/com/cloud/user/UserAccountVO.java 
b/engine/schema/src/main/java/com/cloud/user/UserAccountVO.java
index c18ca53f7ab..a9d4ca9a2b9 100644
--- a/engine/schema/src/main/java/com/cloud/user/UserAccountVO.java
+++ b/engine/schema/src/main/java/com/cloud/user/UserAccountVO.java
@@ -35,6 +35,8 @@ import org.apache.cloudstack.api.InternalIdentity;
 
 import com.cloud.utils.db.Encrypt;
 import com.cloud.utils.db.GenericDao;
+
+import 
org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
 import org.apache.commons.lang3.StringUtils;
 
 @Entity
@@ -368,4 +370,9 @@ public class UserAccountVO implements UserAccount, 
InternalIdentity {
     public void setDetails(Map<String, String> details) {
         this.details = details;
     }
+
+    @Override
+    public String toString() {
+        return String.format("User %s", 
ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "name", 
"uuid"));
+    }
 }
diff --git 
a/plugins/network-elements/juniper-contrail/src/test/java/org/apache/cloudstack/network/contrail/management/MockAccountManager.java
 
b/plugins/network-elements/juniper-contrail/src/test/java/org/apache/cloudstack/network/contrail/management/MockAccountManager.java
index a3fac2c8be9..a63c5b68e57 100644
--- 
a/plugins/network-elements/juniper-contrail/src/test/java/org/apache/cloudstack/network/contrail/management/MockAccountManager.java
+++ 
b/plugins/network-elements/juniper-contrail/src/test/java/org/apache/cloudstack/network/contrail/management/MockAccountManager.java
@@ -516,4 +516,9 @@ public class MockAccountManager extends ManagerBase 
implements AccountManager {
     public ConfigKey<?>[] getConfigKeys() {
         return null;
     }
+
+    @Override
+    public UserAccount 
clearUserTwoFactorAuthenticationInSetupStateOnLogin(UserAccount user) {
+        return null;
+    }
 }
diff --git a/server/src/main/java/com/cloud/api/ApiServer.java 
b/server/src/main/java/com/cloud/api/ApiServer.java
index d68f42470d5..c78f8e68c2b 100644
--- a/server/src/main/java/com/cloud/api/ApiServer.java
+++ b/server/src/main/java/com/cloud/api/ApiServer.java
@@ -1159,7 +1159,7 @@ public class ApiServer extends ManagerBase implements 
HttpRequestHandler, ApiSer
             domainId = userDomain.getId();
         }
 
-        final UserAccount userAcct = accountMgr.authenticateUser(username, 
password, domainId, loginIpAddress, requestParameters);
+        UserAccount userAcct = accountMgr.authenticateUser(username, password, 
domainId, loginIpAddress, requestParameters);
         if (userAcct != null) {
             final String timezone = userAcct.getTimezone();
             float offsetInHrs = 0f;
@@ -1204,6 +1204,7 @@ public class ApiServer extends ManagerBase implements 
HttpRequestHandler, ApiSer
                 session.setAttribute("timezoneoffset", 
Float.valueOf(offsetInHrs).toString());
             }
 
+            userAcct = 
accountMgr.clearUserTwoFactorAuthenticationInSetupStateOnLogin(userAcct);
             boolean is2faEnabled = false;
             if (userAcct.isUser2faEnabled() || 
(Boolean.TRUE.equals(AccountManagerImpl.enableUserTwoFactorAuthentication.valueIn(userAcct.getDomainId()))
 && 
Boolean.TRUE.equals(AccountManagerImpl.mandateUserTwoFactorAuthentication.valueIn(userAcct.getDomainId()))))
 {
                 is2faEnabled = true;
diff --git a/server/src/main/java/com/cloud/user/AccountManager.java 
b/server/src/main/java/com/cloud/user/AccountManager.java
index c95047a6c42..1d7611d5b54 100644
--- a/server/src/main/java/com/cloud/user/AccountManager.java
+++ b/server/src/main/java/com/cloud/user/AccountManager.java
@@ -195,6 +195,9 @@ public interface AccountManager extends AccountService, 
Configurable {
     UserTwoFactorAuthenticator getUserTwoFactorAuthenticator(final Long 
domainId, final Long userAccountId);
 
     void verifyUsingTwoFactorAuthenticationCode(String code, Long domainId, 
Long userAccountId);
+
     UserTwoFactorAuthenticationSetupResponse 
setupUserTwoFactorAuthentication(SetupUserTwoFactorAuthenticationCmd cmd);
 
+    UserAccount 
clearUserTwoFactorAuthenticationInSetupStateOnLogin(UserAccount user);
+
 }
diff --git a/server/src/main/java/com/cloud/user/AccountManagerImpl.java 
b/server/src/main/java/com/cloud/user/AccountManagerImpl.java
index 1e727036d56..7d22a114331 100644
--- a/server/src/main/java/com/cloud/user/AccountManagerImpl.java
+++ b/server/src/main/java/com/cloud/user/AccountManagerImpl.java
@@ -3475,4 +3475,26 @@ public class AccountManagerImpl extends ManagerBase 
implements AccountManager, M
         return userTwoFactorAuthenticationProvidersMap.get(name.toLowerCase());
     }
 
+    @Override
+    public UserAccount 
clearUserTwoFactorAuthenticationInSetupStateOnLogin(UserAccount user) {
+        return Transaction.execute((TransactionCallback<UserAccount>) status 
-> {
+            if (!user.isUser2faEnabled() && 
StringUtils.isBlank(user.getUser2faProvider())) {
+                return user;
+            }
+            UserDetailVO userDetailVO = 
_userDetailsDao.findDetail(user.getId(), UserDetailVO.Setup2FADetail);
+            if (userDetailVO != null && 
UserAccountVO.Setup2FAstatus.VERIFIED.name().equals(userDetailVO.getValue())) {
+                return user;
+            }
+            s_logger.info(String.format("Clearing 2FA configurations for %s as 
it is still in setup on a new login request", user));
+            if (userDetailVO != null) {
+                _userDetailsDao.remove(userDetailVO.getId());
+            }
+            UserAccountVO userAccountVO = 
_userAccountDao.findById(user.getId());
+            userAccountVO.setUser2faEnabled(false);
+            userAccountVO.setUser2faProvider(null);
+            userAccountVO.setKeyFor2fa(null);
+            _userAccountDao.update(user.getId(), userAccountVO);
+            return userAccountVO;
+        });
+    }
 }
diff --git a/server/src/test/java/com/cloud/user/AccountManagerImplTest.java 
b/server/src/test/java/com/cloud/user/AccountManagerImplTest.java
index 0e8e1df0f3a..e68de194f01 100644
--- a/server/src/test/java/com/cloud/user/AccountManagerImplTest.java
+++ b/server/src/test/java/com/cloud/user/AccountManagerImplTest.java
@@ -39,10 +39,12 @@ import 
org.apache.cloudstack.auth.UserAuthenticator.ActionOnFailedAuthentication
 import org.apache.cloudstack.auth.UserTwoFactorAuthenticator;
 import org.apache.cloudstack.context.CallContext;
 import org.apache.cloudstack.framework.config.ConfigKey;
+import org.apache.cloudstack.resourcedetail.UserDetailVO;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.Mockito;
@@ -1218,4 +1220,54 @@ public class AccountManagerImplTest extends 
AccountManagetImplTestBase {
         
Mockito.when(_projectAccountDao.listAdministratedProjectIds(accountId)).thenReturn(managedProjectIds);
         accountManagerImpl.checkIfAccountManagesProjects(accountId);
     }
+
+    @Test
+    public void testClearUser2FA_When2FADisabled_NoChanges() {
+        UserAccount user = Mockito.mock(UserAccount.class);
+        Mockito.when(user.isUser2faEnabled()).thenReturn(false);
+        Mockito.when(user.getUser2faProvider()).thenReturn(null);
+        UserAccount result = 
accountManagerImpl.clearUserTwoFactorAuthenticationInSetupStateOnLogin(user);
+        Assert.assertSame(user, result);
+        Mockito.verifyNoInteractions(userDetailsDaoMock, userAccountDaoMock);
+    }
+
+    @Test
+    public void testClearUser2FA_When2FAInVerifiedState_NoChanges() {
+        UserAccount user = Mockito.mock(UserAccount.class);
+        Mockito.when(user.getId()).thenReturn(1L);
+        Mockito.when(user.isUser2faEnabled()).thenReturn(true);
+        UserDetailVO userDetail = new UserDetailVO();
+        userDetail.setValue(UserAccountVO.Setup2FAstatus.VERIFIED.name());
+        Mockito.when(userDetailsDaoMock.findDetail(1L, 
UserDetailVO.Setup2FADetail)).thenReturn(userDetail);
+        UserAccount result = 
accountManagerImpl.clearUserTwoFactorAuthenticationInSetupStateOnLogin(user);
+        Assert.assertSame(user, result);
+        Mockito.verify(userDetailsDaoMock).findDetail(1L, 
UserDetailVO.Setup2FADetail);
+        Mockito.verifyNoMoreInteractions(userDetailsDaoMock, 
userAccountDaoMock);
+    }
+
+    @Test
+    public void testClearUser2FA_When2FAInSetupState_Disable2FA() {
+        UserAccount user = Mockito.mock(UserAccount.class);
+        Mockito.when(user.getId()).thenReturn(1L);
+        Mockito.when(user.isUser2faEnabled()).thenReturn(true);
+        UserDetailVO userDetail = new UserDetailVO();
+        userDetail.setValue(UserAccountVO.Setup2FAstatus.ENABLED.name());
+        UserAccountVO userAccountVO = new UserAccountVO();
+        userAccountVO.setId(1L);
+        Mockito.when(userDetailsDaoMock.findDetail(1L, 
UserDetailVO.Setup2FADetail)).thenReturn(userDetail);
+        
Mockito.when(userAccountDaoMock.findById(1L)).thenReturn(userAccountVO);
+        UserAccount result = 
accountManagerImpl.clearUserTwoFactorAuthenticationInSetupStateOnLogin(user);
+        Assert.assertNotNull(result);
+        Assert.assertFalse(result.isUser2faEnabled());
+        Assert.assertNull(result.getUser2faProvider());
+        Mockito.verify(userDetailsDaoMock).findDetail(1L, 
UserDetailVO.Setup2FADetail);
+        Mockito.verify(userDetailsDaoMock).remove(Mockito.anyLong());
+        Mockito.verify(userAccountDaoMock).findById(1L);
+        ArgumentCaptor<UserAccountVO> captor = 
ArgumentCaptor.forClass(UserAccountVO.class);
+        Mockito.verify(userAccountDaoMock).update(Mockito.eq(1L), 
captor.capture());
+        UserAccountVO updatedUser = captor.getValue();
+        Assert.assertFalse(updatedUser.isUser2faEnabled());
+        Assert.assertNull(updatedUser.getUser2faProvider());
+        Assert.assertNull(updatedUser.getKeyFor2fa());
+    }
 }
diff --git a/server/src/test/java/com/cloud/user/MockAccountManagerImpl.java 
b/server/src/test/java/com/cloud/user/MockAccountManagerImpl.java
index f8558992440..96b73cc63dd 100644
--- a/server/src/test/java/com/cloud/user/MockAccountManagerImpl.java
+++ b/server/src/test/java/com/cloud/user/MockAccountManagerImpl.java
@@ -484,4 +484,8 @@ public class MockAccountManagerImpl extends ManagerBase 
implements Manager, Acco
         return null;
     }
 
+    @Override
+    public UserAccount 
clearUserTwoFactorAuthenticationInSetupStateOnLogin(UserAccount user) {
+        return null;
+    }
 }

Reply via email to