You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ha...@apache.org on 2022/11/08 09:50:45 UTC

[cloudstack] branch 2FA updated (b8d1c99038f -> 025744d7b04)

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

harikrishna pushed a change to branch 2FA
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


    from b8d1c99038f Partial changes for UI
     new 4bc15cd6983 Added UI changes and permissions
     new 8681050b31c Added user context while setting 2fa
     new 025744d7b04 Added success messages for UI operations

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../cloudstack/api/response/UserResponse.java      | 12 ++++
 .../resources/META-INF/db/schema-41710to41800.sql  | 48 ++++++++++++-
 ...ListUserTwoFactorAuthenticatorProvidersCmd.java |  2 +
 .../auth/SetupUserTwoFactorAuthenticationCmd.java  | 17 ++++-
 ...ValidateUserTwoFactorAuthenticationCodeCmd.java |  2 +
 .../api/query/dao/UserAccountJoinDaoImpl.java      |  1 +
 .../com/cloud/api/query/vo/UserAccountJoinVO.java  |  7 ++
 .../java/com/cloud/user/AccountManagerImpl.java    | 49 ++++++++++---
 ui/public/locales/en.json                          |  6 +-
 ui/src/config/router.js                            |  2 +-
 ui/src/config/section/user.js                      | 29 +++++++-
 ui/src/permission.js                               |  8 +++
 ui/src/store/modules/user.js                       |  4 +-
 ui/src/views/auth/Login.vue                        |  4 +-
 ui/src/views/dashboard/TwoFa.vue                   | 13 ++--
 ui/src/views/iam/RegisterTwoFactorAuth.vue         | 82 ++++++++++++----------
 16 files changed, 223 insertions(+), 63 deletions(-)


[cloudstack] 02/03: Added user context while setting 2fa

Posted by ha...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

harikrishna pushed a commit to branch 2FA
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit 8681050b31cc9d88b719ec1973547c725becdf14
Author: Harikrishna Patnala <ha...@gmail.com>
AuthorDate: Tue Nov 8 13:15:14 2022 +0530

    Added user context while setting 2fa
---
 server/src/main/java/com/cloud/user/AccountManagerImpl.java | 3 ++-
 ui/src/config/router.js                                     | 2 +-
 ui/src/views/dashboard/TwoFa.vue                            | 1 -
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/server/src/main/java/com/cloud/user/AccountManagerImpl.java b/server/src/main/java/com/cloud/user/AccountManagerImpl.java
index 40cd5a8d7fb..980d77628e4 100644
--- a/server/src/main/java/com/cloud/user/AccountManagerImpl.java
+++ b/server/src/main/java/com/cloud/user/AccountManagerImpl.java
@@ -3233,7 +3233,8 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
             }
             owner = _accountService.getActiveAccountById(userId);
         } else {
-            userVO = _userDao.findById(caller.getId());
+            userId = CallContext.current().getCallingUserId();
+            userVO = _userDao.findById(userId);
         }
         checkAccess(caller, null, true, owner);
 
diff --git a/ui/src/config/router.js b/ui/src/config/router.js
index e720d9ccbe1..349e5da7d71 100644
--- a/ui/src/config/router.js
+++ b/ui/src/config/router.js
@@ -311,7 +311,7 @@ export const constantRouterMap = [
     path: '/2FA',
     name: 'TwoFa',
     meta: {
-      title: 'label.2fa',
+      title: 'label.two.factor.authentication',
       hidden: true
     },
     component: () => import('@/views/dashboard/TwoFa')
diff --git a/ui/src/views/dashboard/TwoFa.vue b/ui/src/views/dashboard/TwoFa.vue
index 8d9eb5cb6e1..9487ffc33da 100644
--- a/ui/src/views/dashboard/TwoFa.vue
+++ b/ui/src/views/dashboard/TwoFa.vue
@@ -16,7 +16,6 @@
 // under the License.
 
 <template>
-  <!-- <a-card class="center-align" style="width: 600px; height: 720px"> -->
   <a-form>
     <img
       v-if="$config.banner"


[cloudstack] 01/03: Added UI changes and permissions

Posted by ha...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

harikrishna pushed a commit to branch 2FA
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit 4bc15cd69833d6ba6fb3fae48621c89dc1817cd9
Author: Harikrishna Patnala <ha...@gmail.com>
AuthorDate: Mon Nov 7 15:12:43 2022 +0530

    Added UI changes and permissions
---
 .../cloudstack/api/response/UserResponse.java      | 12 ++++
 .../resources/META-INF/db/schema-41710to41800.sql  | 48 +++++++++++++-
 ...ListUserTwoFactorAuthenticatorProvidersCmd.java |  2 +
 .../auth/SetupUserTwoFactorAuthenticationCmd.java  | 17 ++++-
 ...ValidateUserTwoFactorAuthenticationCodeCmd.java |  2 +
 .../api/query/dao/UserAccountJoinDaoImpl.java      |  1 +
 .../com/cloud/api/query/vo/UserAccountJoinVO.java  |  7 ++
 .../java/com/cloud/user/AccountManagerImpl.java    | 48 +++++++++++---
 ui/public/locales/en.json                          |  5 +-
 ui/src/config/section/user.js                      | 26 +++++++-
 ui/src/permission.js                               |  8 +++
 ui/src/store/modules/user.js                       |  4 +-
 ui/src/views/auth/Login.vue                        |  4 +-
 ui/src/views/dashboard/TwoFa.vue                   |  6 +-
 ui/src/views/iam/RegisterTwoFactorAuth.vue         | 76 ++++++++++++----------
 15 files changed, 206 insertions(+), 60 deletions(-)

diff --git a/api/src/main/java/org/apache/cloudstack/api/response/UserResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/UserResponse.java
index 1c81027f0a8..d521ce3a07d 100644
--- a/api/src/main/java/org/apache/cloudstack/api/response/UserResponse.java
+++ b/api/src/main/java/org/apache/cloudstack/api/response/UserResponse.java
@@ -120,6 +120,10 @@ public class UserResponse extends BaseResponse implements SetResourceIconRespons
     @Param(description = "Base64 string representation of the resource icon", since = "4.16.0.0")
     ResourceIconResponse icon;
 
+    @SerializedName(ApiConstants.IS_2FA_ENABLED)
+    @Param(description = "true if user has two factor authentication enabled", since = "4.18.0.0")
+    private Boolean is2FAenabled;
+
     @Override
     public String getObjectId() {
         return this.getId();
@@ -285,4 +289,12 @@ public class UserResponse extends BaseResponse implements SetResourceIconRespons
     public void setResourceIconResponse(ResourceIconResponse icon) {
         this.icon = icon;
     }
+
+    public Boolean Is2FAenabled() {
+        return is2FAenabled;
+    }
+
+    public void set2FAenabled(Boolean is2FAenabled) {
+        this.is2FAenabled = is2FAenabled;
+    }
 }
diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41710to41800.sql b/engine/schema/src/main/resources/META-INF/db/schema-41710to41800.sql
index 4618e2ca03e..67c9729e591 100644
--- a/engine/schema/src/main/resources/META-INF/db/schema-41710to41800.sql
+++ b/engine/schema/src/main/resources/META-INF/db/schema-41710to41800.sql
@@ -52,4 +52,50 @@ ALTER TABLE `cloud`.`vpc`
 
 ALTER TABLE `cloud`.`user` ADD COLUMN `two_factor_authentication_enabled` tinyint NOT NULL DEFAULT 0;
 ALTER TABLE `cloud`.`user` ADD COLUMN `key_for_2fa` varchar(255) default NULL;
-ALTER TABLE `cloud`.`user` ADD COLUMN `user_2fa_provider` varchar(255) default NULL;
\ No newline at end of file
+ALTER TABLE `cloud`.`user` ADD COLUMN `user_2fa_provider` varchar(255) default NULL;
+
+DROP VIEW IF EXISTS `cloud`.`user_view`;
+CREATE VIEW `cloud`.`user_view` AS
+    select
+        user.id,
+        user.uuid,
+        user.username,
+        user.password,
+        user.firstname,
+        user.lastname,
+        user.email,
+        user.state,
+        user.api_key,
+        user.secret_key,
+        user.created,
+        user.removed,
+        user.timezone,
+        user.registration_token,
+        user.is_registered,
+        user.incorrect_login_attempts,
+        user.source,
+        user.default,
+        account.id account_id,
+        account.uuid account_uuid,
+        account.account_name account_name,
+        account.type account_type,
+        account.role_id account_role_id,
+        domain.id domain_id,
+        domain.uuid domain_uuid,
+        domain.name domain_name,
+        domain.path domain_path,
+        async_job.id job_id,
+        async_job.uuid job_uuid,
+        async_job.job_status job_status,
+        async_job.account_id job_account_id,
+        user.two_factor_authentication_enabled two_factor_authentication_enabled
+    from
+        `cloud`.`user`
+            inner join
+        `cloud`.`account` ON user.account_id = account.id
+            inner join
+        `cloud`.`domain` ON account.domain_id = domain.id
+            left join
+        `cloud`.`async_job` ON async_job.instance_id = user.id
+            and async_job.instance_type = 'User'
+            and async_job.job_status = 0;
\ No newline at end of file
diff --git a/server/src/main/java/com/cloud/api/auth/ListUserTwoFactorAuthenticatorProvidersCmd.java b/server/src/main/java/com/cloud/api/auth/ListUserTwoFactorAuthenticatorProvidersCmd.java
index aa82061ac16..f16ddf1ce5a 100644
--- a/server/src/main/java/com/cloud/api/auth/ListUserTwoFactorAuthenticatorProvidersCmd.java
+++ b/server/src/main/java/com/cloud/api/auth/ListUserTwoFactorAuthenticatorProvidersCmd.java
@@ -18,6 +18,7 @@ package com.cloud.api.auth;
 
 import com.cloud.user.Account;
 import com.cloud.user.AccountManager;
+import org.apache.cloudstack.acl.RoleType;
 import org.apache.cloudstack.api.APICommand;
 import org.apache.cloudstack.api.ApiConstants;
 import org.apache.cloudstack.api.BaseCmd;
@@ -32,6 +33,7 @@ import java.util.List;
 
 @APICommand(name = ListUserTwoFactorAuthenticatorProvidersCmd.APINAME,
         description = "Lists user two factor authenticator providers",
+        authorized = {RoleType.Admin, RoleType.DomainAdmin, RoleType.ResourceAdmin, RoleType.User},
         responseObject = UserTwoFactorAuthenticatorProviderResponse.class, since = "4.18.0")
 public class ListUserTwoFactorAuthenticatorProvidersCmd extends BaseCmd {
 
diff --git a/server/src/main/java/com/cloud/api/auth/SetupUserTwoFactorAuthenticationCmd.java b/server/src/main/java/com/cloud/api/auth/SetupUserTwoFactorAuthenticationCmd.java
index 5ef0341662c..055d84c816b 100644
--- a/server/src/main/java/com/cloud/api/auth/SetupUserTwoFactorAuthenticationCmd.java
+++ b/server/src/main/java/com/cloud/api/auth/SetupUserTwoFactorAuthenticationCmd.java
@@ -17,18 +17,21 @@
 package com.cloud.api.auth;
 
 import com.cloud.user.AccountManager;
+import org.apache.cloudstack.acl.RoleType;
 import org.apache.cloudstack.api.APICommand;
 import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiCommandResourceType;
 import org.apache.cloudstack.api.BaseCmd;
 import org.apache.cloudstack.api.Parameter;
 import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.UserResponse;
 import org.apache.cloudstack.api.response.UserTwoFactorAuthenticationSetupResponse;
 import org.apache.cloudstack.context.CallContext;
 import org.apache.log4j.Logger;
 
 import javax.inject.Inject;
 
-@APICommand(name = SetupUserTwoFactorAuthenticationCmd.APINAME, description = "Setup the 2fa for the user.", requestHasSensitiveInfo = false,
+@APICommand(name = SetupUserTwoFactorAuthenticationCmd.APINAME, description = "Setup the 2fa for the user.", authorized = {RoleType.Admin, RoleType.DomainAdmin, RoleType.ResourceAdmin, RoleType.User}, requestHasSensitiveInfo = false,
         responseObject = UserTwoFactorAuthenticationSetupResponse.class, entityType = {}, since = "4.18.0")
 public class SetupUserTwoFactorAuthenticationCmd extends BaseCmd {
 
@@ -48,6 +51,9 @@ public class SetupUserTwoFactorAuthenticationCmd extends BaseCmd {
     @Parameter(name = ApiConstants.ENABLE, type = CommandType.BOOLEAN, description = "Enabled by default, provide false to disable 2FA")
     private Boolean enable;
 
+    @Parameter(name = ApiConstants.USER_ID, type = CommandType.STRING, entityType = UserResponse.class, description = "optional: the id of the user for which 2FA has to be disabled")
+    private Long userId;
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -60,6 +66,10 @@ public class SetupUserTwoFactorAuthenticationCmd extends BaseCmd {
         return enable == null ? true : enable;
     }
 
+    public Long getUserId() {
+        return userId;
+    }
+
     @Override
     public void execute() throws ServerApiException {
         UserTwoFactorAuthenticationSetupResponse response = accountManager.setupUserTwoFactorAuthentication(this);
@@ -78,4 +88,9 @@ public class SetupUserTwoFactorAuthenticationCmd extends BaseCmd {
         return CallContext.current().getCallingAccount().getId();
     }
 
+    @Override
+    public ApiCommandResourceType getApiResourceType() {
+        return ApiCommandResourceType.User;
+    }
+
 }
diff --git a/server/src/main/java/com/cloud/api/auth/ValidateUserTwoFactorAuthenticationCodeCmd.java b/server/src/main/java/com/cloud/api/auth/ValidateUserTwoFactorAuthenticationCodeCmd.java
index a3405761eac..67d13370819 100644
--- a/server/src/main/java/com/cloud/api/auth/ValidateUserTwoFactorAuthenticationCodeCmd.java
+++ b/server/src/main/java/com/cloud/api/auth/ValidateUserTwoFactorAuthenticationCodeCmd.java
@@ -21,6 +21,7 @@ import com.cloud.api.response.ApiResponseSerializer;
 import com.cloud.exception.CloudAuthenticationException;
 import com.cloud.user.AccountManager;
 import com.cloud.user.UserAccount;
+import org.apache.cloudstack.acl.RoleType;
 import org.apache.cloudstack.api.APICommand;
 import org.apache.cloudstack.api.ApiConstants;
 import org.apache.cloudstack.api.ApiErrorCode;
@@ -44,6 +45,7 @@ import java.util.List;
 import java.util.Map;
 
 @APICommand(name = ValidateUserTwoFactorAuthenticationCodeCmd.APINAME, description = "Checks the 2fa code for the user.", requestHasSensitiveInfo = false,
+        authorized = {RoleType.Admin, RoleType.DomainAdmin, RoleType.ResourceAdmin, RoleType.User},
         responseObject = SuccessResponse.class, entityType = {}, since = "4.18.0")
 public class ValidateUserTwoFactorAuthenticationCodeCmd extends BaseCmd implements APIAuthenticator {
 
diff --git a/server/src/main/java/com/cloud/api/query/dao/UserAccountJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/UserAccountJoinDaoImpl.java
index 8d06bcd0a26..ee6e08d69db 100644
--- a/server/src/main/java/com/cloud/api/query/dao/UserAccountJoinDaoImpl.java
+++ b/server/src/main/java/com/cloud/api/query/dao/UserAccountJoinDaoImpl.java
@@ -72,6 +72,7 @@ public class UserAccountJoinDaoImpl extends GenericDaoBase<UserAccountJoinVO, Lo
         userResponse.setApiKey(usr.getApiKey());
         userResponse.setSecretKey(usr.getSecretKey());
         userResponse.setIsDefault(usr.isDefault());
+        userResponse.set2FAenabled(usr.isTwoFactorAuthenticationEnabled());
 
         // set async job
         if (usr.getJobId() != null) {
diff --git a/server/src/main/java/com/cloud/api/query/vo/UserAccountJoinVO.java b/server/src/main/java/com/cloud/api/query/vo/UserAccountJoinVO.java
index 2c6a5060076..4b73fa58ed6 100644
--- a/server/src/main/java/com/cloud/api/query/vo/UserAccountJoinVO.java
+++ b/server/src/main/java/com/cloud/api/query/vo/UserAccountJoinVO.java
@@ -130,6 +130,9 @@ public class UserAccountJoinVO extends BaseViewVO implements InternalIdentity, I
     @Enumerated(value = EnumType.STRING)
     private User.Source source;
 
+    @Column(name = "two_factor_authentication_enabled")
+    boolean twoFactorAuthenticationEnabled;
+
     public UserAccountJoinVO() {
     }
 
@@ -274,4 +277,8 @@ public class UserAccountJoinVO extends BaseViewVO implements InternalIdentity, I
     public User.Source getSource() {
         return source;
     }
+
+    public boolean isTwoFactorAuthenticationEnabled() {
+        return twoFactorAuthenticationEnabled;
+    }
 }
diff --git a/server/src/main/java/com/cloud/user/AccountManagerImpl.java b/server/src/main/java/com/cloud/user/AccountManagerImpl.java
index 3c2fbe50a2a..40cd5a8d7fb 100644
--- a/server/src/main/java/com/cloud/user/AccountManagerImpl.java
+++ b/server/src/main/java/com/cloud/user/AccountManagerImpl.java
@@ -3197,42 +3197,72 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
         Account caller = CallContext.current().getCallingAccount();
         Account owner = _accountService.getActiveAccountById(caller.getId());
 
-        checkAccess(caller, null, true, owner);
-
         UserTwoFactorAuthenticationSetupResponse response = new UserTwoFactorAuthenticationSetupResponse();
         if (cmd.getEnable()) {
+            checkAccess(caller, null, true, owner);
+            Long userId = CallContext.current().getCallingUserId();
+
             if (StringUtils.isEmpty(providerName)) {
                 throw new InvalidParameterValueException("Provider name is mandatory to setup 2FA");
             }
             UserTwoFactorAuthenticator provider = getUserTwoFactorAuthenticationProvider(providerName);
-            UserAccountVO userAccount = _userAccountDao.findById(owner.getId());
+            UserAccountVO userAccount = _userAccountDao.findById(userId);
+            UserVO userVO = _userDao.findById(userId);
             String code = provider.setup2FAKey(userAccount);
 
             UserVO user = _userDao.createForUpdate();
             user.setKeyFor2fa(code);
             user.setUser2faProvider(provider.getName());
             user.setTwoFactorAuthenticationEnabled(true);
-            _userDao.update(owner.getId(), user);
+            _userDao.update(userId, user);
 
-            response.setId(owner.getUuid());
-            response.setUsername(owner.getName());
+            response.setId(userVO.getUuid());
+            response.setUsername(userAccount.getUsername());
             response.setSecretCode(code);
 
             return response;
         }
 
+        // Admin can disable 2FA of the users
+        UserVO userVO = null;
+        Long userId = cmd.getUserId();
+        if (userId != null) {
+            userVO = validateUser(userId, caller.getDomainId());
+            if (userVO == null) {
+                throw new InvalidParameterValueException("Unable to find user= " + userVO.getUsername() + " in domain id = " + caller.getDomainId());
+            }
+            owner = _accountService.getActiveAccountById(userId);
+        } else {
+            userVO = _userDao.findById(caller.getId());
+        }
+        checkAccess(caller, null, true, owner);
+
         UserVO user = _userDao.createForUpdate();
         user.setKeyFor2fa(null);
         user.setUser2faProvider(null);
         user.setTwoFactorAuthenticationEnabled(false);
-        _userDao.update(owner.getId(), user);
+        _userDao.update(userVO.getId(), user);
 
-        response.setId(owner.getUuid());
-        response.setUsername(owner.getName());
+        response.setId(userVO.getUuid());
+        response.setUsername(userVO.getUsername());
 
         return response;
     }
 
+    private UserVO validateUser(Long userId, Long domainId) {
+        UserVO user = null;
+        if (userId != null) {
+            user = _userDao.findById(userId);
+            if (user == null) {
+                throw new InvalidParameterValueException("Invalid user ID provided");
+            }
+            if (_accountDao.findById(user.getAccountId()).getDomainId() != domainId) {
+                throw new InvalidParameterValueException("User doesn't belong to the specified account or domain");
+            }
+        }
+        return user;
+    }
+
     public UserTwoFactorAuthenticator getUserTwoFactorAuthenticator(final String name) {
         if (StringUtils.isEmpty(name)) {
             throw new CloudRuntimeException("Invalid UserTwoFactorAuthenticator name provided");
diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json
index 508c6f93098..aa2ac1c9336 100644
--- a/ui/public/locales/en.json
+++ b/ui/public/locales/en.json
@@ -136,7 +136,8 @@
 "label.action.reboot.systemvm": "Reboot system VM",
 "label.action.recover.volume": "Recover volume",
 "label.action.recurring.snapshot": "Recurring snapshots",
-"label.action.register.2FA.user.auth": "Register user for Two Factor Authentication",
+"label.action.register.2FA.user.auth": "Register user Two Factor Authentication",
+"label.action.disable.2FA.user.auth": "Disable user Two Factor Authentication",
 "label.action.register.iso": "Register ISO",
 "label.action.register.template": "Register template from URL",
 "label.action.release.ip": "Release IP",
@@ -895,6 +896,7 @@
 "label.iqn": "Target IQN",
 "label.is.in.progress": "is in progress",
 "label.is.shared": "Is shared",
+"label.is2faenabled": "Is 2FA enabled",
 "label.isadvanced": "Show advanced settings",
 "label.iscsi": "iSCSI",
 "label.iscustomized": "Custom disk size",
@@ -1944,6 +1946,7 @@
 "message.action.destroy.instance.with.backups": "Please confirm that you want to destroy the instance. There may be backups associated with the instance which will not be deleted.",
 "message.action.destroy.systemvm": "Please confirm that you want to destroy the System VM.",
 "message.action.destroy.volume": "Please confirm that you want to destroy the volume.",
+"message.action.disable.2FA.user.auth": "Please confirm that you want to disable user Two factor authentication.",
 "message.action.disable.cluster": "Please confirm that you want to disable this cluster.",
 "message.action.disable.physical.network": "Please confirm that you want to disable this physical network.",
 "message.action.disable.pod": "Please confirm that you want to disable this pod.",
diff --git a/ui/src/config/section/user.js b/ui/src/config/section/user.js
index 5a298134839..cb86d395396 100644
--- a/ui/src/config/section/user.js
+++ b/ui/src/config/section/user.js
@@ -26,7 +26,7 @@ export default {
   hidden: true,
   permission: ['listUsers'],
   columns: ['username', 'state', 'firstname', 'lastname', 'email', 'account'],
-  details: ['username', 'id', 'firstname', 'lastname', 'email', 'usersource', 'timezone', 'rolename', 'roletype', 'account', 'domain', 'created'],
+  details: ['username', 'id', 'firstname', 'lastname', 'email', 'usersource', 'timezone', 'rolename', 'roletype', 'is2faenabled', 'account', 'domain', 'created'],
   tabs: [
     {
       name: 'details',
@@ -107,14 +107,34 @@ export default {
       component: shallowRef(defineAsyncComponent(() => import('@/views/iam/ConfigureSamlSsoAuth.vue')))
     },
     {
-      // update API name
-      api: 'updateUser',
+      api: 'setupUserTwoFactorAuthentication',
       icon: 'scan-outlined',
       label: 'label.action.register.2FA.user.auth',
       dataView: true,
       popup: true,
+      show: (record, store) => {
+        return (record.is2faenabled === false && record.id === store.userInfo.id)
+      },
       component: shallowRef(defineAsyncComponent(() => import('@/views/iam/RegisterTwoFactorAuth.vue')))
     },
+    {
+      api: 'setupUserTwoFactorAuthentication',
+      icon: 'scan-outlined',
+      label: 'label.action.disable.2FA.user.auth',
+      message: 'message.action.disable.2FA.user.auth',
+      dataView: true,
+      groupAction: true,
+      popup: true,
+      args: ['enable'],
+      mapping: {
+        enable: {
+          value: (record) => { return false }
+        }
+      },
+      show: (record, store) => {
+        return (record.is2faenabled === true) && (record.id === store.userInfo.id || ['Admin', 'DomainAdmin'].includes(store.userInfo.roletype))
+      }
+    },
     {
       api: 'deleteUser',
       icon: 'delete-outlined',
diff --git a/ui/src/permission.js b/ui/src/permission.js
index b14b826487c..834d0e098be 100644
--- a/ui/src/permission.js
+++ b/ui/src/permission.js
@@ -60,6 +60,14 @@ router.beforeEach((to, from, next) => {
       console.log('hari3')
       next({ path: '/dashboard' })
       NProgress.done()
+    } else if (to.path === '/2FA') {
+      if (store.getters.twoFaEnabled && !store.getters.loginFlag) {
+        console.log('Do Two-factor authentication')
+        next()
+      } else {
+        next({ path: '/dashboard' })
+        NProgress.done()
+      }
     } else {
       console.log('hari4')
       if (Object.keys(store.getters.apis).length === 0) {
diff --git a/ui/src/store/modules/user.js b/ui/src/store/modules/user.js
index 5a83e9bb1da..42c7db72ca3 100644
--- a/ui/src/store/modules/user.js
+++ b/ui/src/store/modules/user.js
@@ -180,9 +180,7 @@ const user = {
           commit('SET_CLOUDIAN', {})
           commit('SET_DOMAIN_STORE', {})
           commit('SET_LOGOUT_FLAG', false)
-          // TODO: get value from session and set - currently hard-coding it
-          // commit('SET_2FA_ENABLED', (result.is2faenabled === 'true'))
-          commit('SET_2FA_ENABLED', true)
+          commit('SET_2FA_ENABLED', (result.is2faenabled === 'true'))
           commit('SET_LOGIN_FLAG', false)
           notification.destroy()
 
diff --git a/ui/src/views/auth/Login.vue b/ui/src/views/auth/Login.vue
index 3fd287b7383..4b1f773ddb0 100644
--- a/ui/src/views/auth/Login.vue
+++ b/ui/src/views/auth/Login.vue
@@ -298,12 +298,10 @@ export default {
     loginSuccess (res) {
       this.$notification.destroy()
       this.$store.commit('SET_COUNT_NOTIFY', 0)
-      this.$store.commit('SET_LOGIN_FLAG', true)
-      console.log(store.getters.twoFaEnabled)
       if (store.getters.twoFaEnabled === true) {
         this.$router.push({ path: '/2FA' }).catch(() => {})
       } else {
-        console.log('hari2')
+        this.$store.commit('SET_LOGIN_FLAG', true)
         this.$router.push({ path: '/dashboard' }).catch(() => {})
       }
     },
diff --git a/ui/src/views/dashboard/TwoFa.vue b/ui/src/views/dashboard/TwoFa.vue
index 0f9e6017642..8d9eb5cb6e1 100644
--- a/ui/src/views/dashboard/TwoFa.vue
+++ b/ui/src/views/dashboard/TwoFa.vue
@@ -86,6 +86,9 @@ export default {
         api('validateUserTwoFactorAuthenticationCode', { '2facode': values.secretkey }).then(response => {
           this.twoFAresponse = true
           if (this.twoFAresponse) {
+            this.$notification.destroy()
+            this.$store.commit('SET_COUNT_NOTIFY', 0)
+            this.$store.commit('SET_LOGIN_FLAG', true)
             this.$router.push({ path: '/dashboard' }).catch(() => {})
           }
           console.log(response)
@@ -96,9 +99,6 @@ export default {
           })
         })
       })
-
-      // Add logic to set loginFlag to true
-      this.$store.dispatch('SetLoginFlag', true)
     }
   }
 }
diff --git a/ui/src/views/iam/RegisterTwoFactorAuth.vue b/ui/src/views/iam/RegisterTwoFactorAuth.vue
index 1c1db3e28cf..67eab5bbefa 100644
--- a/ui/src/views/iam/RegisterTwoFactorAuth.vue
+++ b/ui/src/views/iam/RegisterTwoFactorAuth.vue
@@ -38,43 +38,45 @@
         </a-select-option>
       </a-select>
     </div>
-    <div v-if="selectedProvider === 'google'">
-      <br />
-      <div> {{ $t('message.two.fa.auth.register.account') }} </div>
-      <vue-qrious
-        class="center-align"
-        :value="googleUrl"
-        @change="onDataUrlChange"
-      />
-    </div>
-    <div v-else-if="selectedProvider === 'staticpin'">
-      <div> <a @click="setup2FAProvider"> {{ $t('message.two.fa.static.pin.part2') }}</a></div>
-    </div>
-    <div v-else-if="selectedProvider !== null && selectedProvider !== 'staticpin'">
-      <div> {{ $t('message.two.fa.static.pin.part1') }} <a @click="setup2FAProvider"> {{ $t('message.two.fa.static.pin.part2') }}</a></div>
-    </div>
-    <div v-if="selectedProvider">
-      <br />
-      <h3> {{ $t('label.enter.code') }} </h3>
-      <a-form @finish="submitPin" v-ctrl-enter="submitPin" class="container">
-        <a-input v-model:value="code" />
-        <div :span="24">
-          <a-button ref="submit" type="primary" @click="submitPin">{{ $t('label.ok') }}</a-button>
-        </div>
-      </a-form>
-    </div>
+    <div v-if="show2FAdetails">
+      <div v-if="selectedProvider === 'google'">
+        <br />
+        <div> {{ $t('message.two.fa.auth.register.account') }} </div>
+        <vue-qrious
+          class="center-align"
+          :value="googleUrl"
+          @change="onDataUrlChange"
+        />
+      </div>
+      <div v-else-if="selectedProvider === 'staticpin'">
+        <div> <a @click="setup2FAProvider"> {{ $t('message.two.fa.static.pin.part2') }}</a></div>
+      </div>
+      <div v-else-if="selectedProvider !== null && selectedProvider !== 'staticpin'">
+        <div> {{ $t('message.two.fa.static.pin.part1') }} <a @click="setup2FAProvider"> {{ $t('message.two.fa.static.pin.part2') }}</a></div>
+      </div>
+      <div v-if="selectedProvider">
+        <br />
+        <h3> {{ $t('label.enter.code') }} </h3>
+        <a-form @finish="submitPin" v-ctrl-enter="submitPin" class="container">
+          <a-input v-model:value="code" />
+          <div :span="24">
+            <a-button ref="submit" type="primary" @click="submitPin">{{ $t('label.ok') }}</a-button>
+          </div>
+        </a-form>
+      </div>
 
-    <a-modal
-      v-if="showPin"
-      :visible="showPin"
-      :title="$t('label.two.factor.secret')"
-      :closable="true"
-      :footer="null"
-      @cancel="onCloseModal"
-      centered
-      width="450px">
-      <div> {{ pin }} </div>
-    </a-modal>
+      <a-modal
+        v-if="showPin"
+        :visible="showPin"
+        :title="$t('label.two.factor.secret')"
+        :closable="true"
+        :footer="null"
+        @cancel="onCloseModal"
+        centered
+        width="450px">
+        <div> {{ pin }} </div>
+      </a-modal>
+    </div>
   </a-form>
   </div>
 </template>
@@ -100,6 +102,7 @@ export default {
       pin: '',
       code: '',
       showPin: false,
+      show2FAdetails: false,
       providers: [],
       selectedProvider: null
     }
@@ -124,6 +127,7 @@ export default {
           this.googleUrl = 'otpauth://totp/CloudStack:' + this.username + '?secret=' + this.pin + '&issuer=CloudStack'
         }
         this.showPin = true
+        this.show2FAdetails = true
       }).catch(error => {
         this.$notification.error({
           message: this.$t('message.request.failed'),


[cloudstack] 03/03: Added success messages for UI operations

Posted by ha...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

harikrishna pushed a commit to branch 2FA
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit 025744d7b045453f6e18cf68d155a66739e71e0f
Author: Harikrishna Patnala <ha...@gmail.com>
AuthorDate: Tue Nov 8 15:20:28 2022 +0530

    Added success messages for UI operations
---
 .../com/cloud/api/auth/SetupUserTwoFactorAuthenticationCmd.java     | 2 +-
 server/src/main/java/com/cloud/user/AccountManagerImpl.java         | 2 +-
 ui/public/locales/en.json                                           | 1 +
 ui/src/config/section/user.js                                       | 5 ++++-
 ui/src/views/dashboard/TwoFa.vue                                    | 6 ++++++
 ui/src/views/iam/RegisterTwoFactorAuth.vue                          | 6 +++++-
 6 files changed, 18 insertions(+), 4 deletions(-)

diff --git a/server/src/main/java/com/cloud/api/auth/SetupUserTwoFactorAuthenticationCmd.java b/server/src/main/java/com/cloud/api/auth/SetupUserTwoFactorAuthenticationCmd.java
index 055d84c816b..77b51bf8153 100644
--- a/server/src/main/java/com/cloud/api/auth/SetupUserTwoFactorAuthenticationCmd.java
+++ b/server/src/main/java/com/cloud/api/auth/SetupUserTwoFactorAuthenticationCmd.java
@@ -51,7 +51,7 @@ public class SetupUserTwoFactorAuthenticationCmd extends BaseCmd {
     @Parameter(name = ApiConstants.ENABLE, type = CommandType.BOOLEAN, description = "Enabled by default, provide false to disable 2FA")
     private Boolean enable;
 
-    @Parameter(name = ApiConstants.USER_ID, type = CommandType.STRING, entityType = UserResponse.class, description = "optional: the id of the user for which 2FA has to be disabled")
+    @Parameter(name = ApiConstants.USER_ID, type = CommandType.UUID, entityType = UserResponse.class, description = "optional: the id of the user for which 2FA has to be disabled")
     private Long userId;
 
     /////////////////////////////////////////////////////
diff --git a/server/src/main/java/com/cloud/user/AccountManagerImpl.java b/server/src/main/java/com/cloud/user/AccountManagerImpl.java
index 980d77628e4..36d7272d87d 100644
--- a/server/src/main/java/com/cloud/user/AccountManagerImpl.java
+++ b/server/src/main/java/com/cloud/user/AccountManagerImpl.java
@@ -3231,7 +3231,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
             if (userVO == null) {
                 throw new InvalidParameterValueException("Unable to find user= " + userVO.getUsername() + " in domain id = " + caller.getDomainId());
             }
-            owner = _accountService.getActiveAccountById(userId);
+            owner = _accountService.getActiveAccountById(userVO.getAccountId());
         } else {
             userId = CallContext.current().getCallingUserId();
             userVO = _userDao.findById(userId);
diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json
index aa2ac1c9336..3031569654e 100644
--- a/ui/public/locales/en.json
+++ b/ui/public/locales/en.json
@@ -104,6 +104,7 @@
 "label.action.edit.instance": "Edit instance",
 "label.action.edit.iso": "Edit ISO",
 "label.action.edit.zone": "Edit zone",
+"label.action.enable.two.factor.authentication": "Enable Two factor authentication",
 "label.action.enable.account": "Enable account",
 "label.action.enable.cluster": "Enable cluster",
 "label.action.enable.maintenance.mode": "Enable maintenance mode",
diff --git a/ui/src/config/section/user.js b/ui/src/config/section/user.js
index cb86d395396..c7fe2a7606d 100644
--- a/ui/src/config/section/user.js
+++ b/ui/src/config/section/user.js
@@ -125,10 +125,13 @@ export default {
       dataView: true,
       groupAction: true,
       popup: true,
-      args: ['enable'],
+      args: ['enable', 'userid'],
       mapping: {
         enable: {
           value: (record) => { return false }
+        },
+        userid: {
+          value: (record) => { return record.id }
         }
       },
       show: (record, store) => {
diff --git a/ui/src/views/dashboard/TwoFa.vue b/ui/src/views/dashboard/TwoFa.vue
index 9487ffc33da..416c915e2e3 100644
--- a/ui/src/views/dashboard/TwoFa.vue
+++ b/ui/src/views/dashboard/TwoFa.vue
@@ -89,6 +89,12 @@ export default {
             this.$store.commit('SET_COUNT_NOTIFY', 0)
             this.$store.commit('SET_LOGIN_FLAG', true)
             this.$router.push({ path: '/dashboard' }).catch(() => {})
+
+            this.$message.success({
+              content: `${this.$t('label.action.enable.two.factor.authentication')}`,
+              duration: 2
+            })
+            this.$emit('refresh-data')
           }
           console.log(response)
         }).catch(error => {
diff --git a/ui/src/views/iam/RegisterTwoFactorAuth.vue b/ui/src/views/iam/RegisterTwoFactorAuth.vue
index 67eab5bbefa..388e7fa3173 100644
--- a/ui/src/views/iam/RegisterTwoFactorAuth.vue
+++ b/ui/src/views/iam/RegisterTwoFactorAuth.vue
@@ -142,7 +142,11 @@ export default {
     },
     submitPin () {
       api('validateUserTwoFactorAuthenticationCode', { '2facode': this.code }).then(response => {
-        console.log(response)
+        this.$message.success({
+          content: `${this.$t('label.action.enable.two.factor.authentication')}`,
+          duration: 2
+        })
+        this.$emit('refresh-data')
       }).catch(error => {
         this.$notification.error({
           message: this.$t('message.request.failed'),