From 4ec20e3af0f5999a2dd82f461e36af37a62adec0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=84=89=E1=85=B3=E1=86=BC=E1=84=8B?= =?UTF-8?q?=E1=85=A7=E1=86=AB?= Date: Wed, 13 Aug 2025 22:36:56 +0900 Subject: [PATCH 1/4] fix: resolve conflicts (merge develop into feature-query-integration) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 이승연 --- .../workflows/dispatch_storybook_release.yaml | 2 +- apps/web/package.json | 2 +- .../composables/use-user-profile-api.ts | 17 +- .../schema/api-verbs/disable-mfa.ts | 4 + .../identity/user-profile/schema/constant.ts | 5 + .../identity/user-profile/schema/type.ts | 3 +- .../identity/user/schema/api-verbs/create.ts | 3 + .../identity/user/schema/api-verbs/update.ts | 3 + .../api-clients/identity/user/schema/model.ts | 10 +- .../workspace-user/schema/api-verbs/create.ts | 3 + .../components/info-tooltip/InfoTooltip.vue | 4 +- .../mfa/components/EmailFoldingInfo.vue} | 48 +- .../components/mfa/components/EmailInfo.vue} | 15 +- .../components/mfa/components/OTPForm.vue | 40 ++ .../components/mfa/components/OTPQRInfo.vue} | 77 +-- .../mfa/components/VerificationCodeForm.vue | 42 ++ .../mfa/composables/use-user-mfa-qr-info.ts | 25 + .../use-user-profile-confirm-mfa-mutation.ts | 32 ++ .../use-user-profile-enable-mfa-query.ts | 26 + .../components/modals/ModalController.vue | 46 ++ .../global-config/api-client-manager.ts | 23 - apps/web/src/router/helpers/route-helper.ts | 1 + .../authenticator/local/template/ID_PW.vue | 20 +- .../mfa/MultifactorAuthConfigured.vue | 44 ++ .../auth/pages/MultiFactorAuthSetUpPage.vue | 241 +++++++++ .../services/auth/routes/route-constant.ts | 1 + apps/web/src/services/auth/routes/routes.ts | 14 + .../iam/components/UserManagementAddModal.vue | 26 +- .../components/UserManagementAddPassword.vue | 55 +- .../components/UserManagementFormInfoForm.vue | 21 +- .../components/UserManagementFormModal.vue | 149 +++--- ...serManagementFormNotificationEmailForm.vue | 17 +- .../UserManagementFormPasswordForm.vue | 31 +- .../iam/components/UserManagementTab.vue | 13 +- .../components/UserManagementTabDetail.vue | 19 +- .../iam/components/UserManagementTable.vue | 2 +- .../components/UserManagementTableToolbox.vue | 29 +- .../mfa/UserBulkMFASettingModal.vue | 167 ++++++ .../mfa/UserMFASecretKeyDeleteModal.vue | 133 +++++ .../mfa/UserMFASettingDisableButton.vue | 40 ++ .../mfa/UserMFASettingEnforceForm.vue | 104 ++++ .../mfa/UserMFASettingFormLayout.vue | 136 +++++ .../use-user-mfa-disable-mutation.ts | 60 +++ .../mutations/use-user-update-mutation.ts | 54 ++ .../services/iam/constants/modal.constant.ts | 5 + .../services/iam/constants/user-constant.ts | 2 + .../src/services/iam/store/user-page-store.ts | 39 +- apps/web/src/services/iam/types/modal.type.ts | 3 + apps/web/src/services/iam/types/user-type.ts | 4 +- .../components/UserAccountMultiFactorAuth.vue | 64 ++- .../UserAccountMultiFactorAuthFormModal.vue | 261 ---------- .../UserAccountMultiFactorAuthItems.vue | 162 +++--- ...ccountMultiFactorAuthEmailDisableModal.vue | 180 +++++++ ...AccountMultiFactorAuthEmailEnableModal.vue | 183 +++++++ ...rAccountMultiFactorAuthOTPDisableModal.vue | 176 +++++++ ...erAccountMultiFactorAuthOTPEnableModal.vue | 180 +++++++ .../my-page/pages/UserAccountPage.vue | 22 +- .../my-page/stores/multi-factor-auth-store.ts | 49 ++ apps/web/src/store/user/constant.ts | 6 + apps/web/src/store/user/type.ts | 6 +- package-lock.json | 6 +- package.json | 2 +- .../core-lib/src/space-connector/index.ts | 17 +- .../src/space-connector/service-api.ts | 19 - .../core-lib/src/space-connector/token-api.ts | 5 + .../console-translation-2.8.babel | 488 ++++++++++++++++++ packages/language-pack/en.json | 26 + packages/language-pack/ja.json | 26 + packages/language-pack/ko.json | 26 + .../src/controls/select-card/PSelectCard.vue | 13 +- 70 files changed, 3128 insertions(+), 649 deletions(-) create mode 100644 apps/web/src/api-clients/identity/user-profile/schema/api-verbs/disable-mfa.ts rename apps/web/src/{services/my-page/components/UserAccountMultiFactorAuthModalFolding.vue => common/components/mfa/components/EmailFoldingInfo.vue} (68%) rename apps/web/src/{services/my-page/components/UserAccountMultiFactorAuthModalEmailInfo.vue => common/components/mfa/components/EmailInfo.vue} (92%) create mode 100644 apps/web/src/common/components/mfa/components/OTPForm.vue rename apps/web/src/{services/my-page/components/UserAccountMultiFactorAuthModalMSInfo.vue => common/components/mfa/components/OTPQRInfo.vue} (61%) create mode 100644 apps/web/src/common/components/mfa/components/VerificationCodeForm.vue create mode 100644 apps/web/src/common/components/mfa/composables/use-user-mfa-qr-info.ts create mode 100644 apps/web/src/common/components/mfa/composables/use-user-profile-confirm-mfa-mutation.ts create mode 100644 apps/web/src/common/components/mfa/composables/use-user-profile-enable-mfa-query.ts create mode 100644 apps/web/src/common/components/modals/ModalController.vue create mode 100644 apps/web/src/services/auth/components/mfa/MultifactorAuthConfigured.vue create mode 100644 apps/web/src/services/auth/pages/MultiFactorAuthSetUpPage.vue create mode 100644 apps/web/src/services/iam/components/mfa/UserBulkMFASettingModal.vue create mode 100644 apps/web/src/services/iam/components/mfa/UserMFASecretKeyDeleteModal.vue create mode 100644 apps/web/src/services/iam/components/mfa/UserMFASettingDisableButton.vue create mode 100644 apps/web/src/services/iam/components/mfa/UserMFASettingEnforceForm.vue create mode 100644 apps/web/src/services/iam/components/mfa/UserMFASettingFormLayout.vue create mode 100644 apps/web/src/services/iam/composables/mutations/use-user-mfa-disable-mutation.ts create mode 100644 apps/web/src/services/iam/composables/mutations/use-user-update-mutation.ts create mode 100644 apps/web/src/services/iam/constants/modal.constant.ts create mode 100644 apps/web/src/services/iam/types/modal.type.ts delete mode 100644 apps/web/src/services/my-page/components/UserAccountMultiFactorAuthFormModal.vue create mode 100644 apps/web/src/services/my-page/components/mfa/UserAccountMultiFactorAuthEmailDisableModal.vue create mode 100644 apps/web/src/services/my-page/components/mfa/UserAccountMultiFactorAuthEmailEnableModal.vue create mode 100644 apps/web/src/services/my-page/components/mfa/UserAccountMultiFactorAuthOTPDisableModal.vue create mode 100644 apps/web/src/services/my-page/components/mfa/UserAccountMultiFactorAuthOTPEnableModal.vue diff --git a/.github/workflows/dispatch_storybook_release.yaml b/.github/workflows/dispatch_storybook_release.yaml index 78dc4a3b2e..7b37ae36a1 100644 --- a/.github/workflows/dispatch_storybook_release.yaml +++ b/.github/workflows/dispatch_storybook_release.yaml @@ -48,7 +48,7 @@ jobs: aws-region: ap-northeast-2 - name: Deploy to s3 - run: aws s3 sync apps/storybook/storybook-static/ s3://${{ secrets.STORYBOOK_S3_BUCKET }} --delete + run: aws s3 sync apps/mirinae-storybook/storybook-static/ s3://${{ secrets.STORYBOOK_S3_BUCKET }} --delete - name: Invalidate CloudFront Cache run: aws cloudfront create-invalidation --distribution-id ${{ secrets.CDN_DISTRIBUTION_ID }} --paths "/*" diff --git a/apps/web/package.json b/apps/web/package.json index bed1108cff..0940cfe084 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "web", - "version": "2.0.0-dev377", + "version": "2.0.0-dev378", "private": true, "description": "Cloudforet Console Web Application", "author": "Cloudforet", diff --git a/apps/web/src/api-clients/identity/user-profile/composables/use-user-profile-api.ts b/apps/web/src/api-clients/identity/user-profile/composables/use-user-profile-api.ts index b155920e69..c08aa42a31 100644 --- a/apps/web/src/api-clients/identity/user-profile/composables/use-user-profile-api.ts +++ b/apps/web/src/api-clients/identity/user-profile/composables/use-user-profile-api.ts @@ -1,16 +1,16 @@ import { SpaceConnector } from '@cloudforet/core-lib/space-connector'; -import type { ListResponse } from '@/api-clients/_common/schema/api-verbs/list'; import type { UserProfileConfirmEmailParameters } from '@/api-clients/identity/user-profile/schema/api-verbs/confirm-email'; import type { UserProfileConfirmMfaParameters } from '@/api-clients/identity/user-profile/schema/api-verbs/confirm-mfa'; +import type { UserProfileDisableMfaParameters } from '@/api-clients/identity/user-profile/schema/api-verbs/disable-mfa'; import type { UserProfileEnableMfaParameters } from '@/api-clients/identity/user-profile/schema/api-verbs/enable-mfa'; import type { UserProfileGetWorkspacesParameters } from '@/api-clients/identity/user-profile/schema/api-verbs/get-workspaces'; import type { UserProfileResetPasswordParameters } from '@/api-clients/identity/user-profile/schema/api-verbs/reset-password'; import type { UserProfileUpdateParameters } from '@/api-clients/identity/user-profile/schema/api-verbs/update'; import type { UserProfileUpdatePasswordParameters } from '@/api-clients/identity/user-profile/schema/api-verbs/update-password'; import type { UserProfileVerifyEmailParameters } from '@/api-clients/identity/user-profile/schema/api-verbs/verify-email'; -import type { WorkspaceGroupModel } from '@/api-clients/identity/workspace-group/schema/model'; -import type { WorkspaceModel } from '@/api-clients/identity/workspace/schema/model'; +import type { UserModel } from '@/api-clients/identity/user/schema/model'; + export const useUserProfileApi = () => { @@ -19,12 +19,11 @@ export const useUserProfileApi = () => { updatePassword: SpaceConnector.clientV2.identity.userProfile.updatePassword, resetPassword: SpaceConnector.clientV2.identity.userProfile.resetPassword, verifyEmail: SpaceConnector.clientV2.identity.userProfile.verifyEmail, - confirmEmail: SpaceConnector.clientV2.identity.userProfile.confirmEmail, - enableMfa: SpaceConnector.clientV2.identity.userProfile.enableMfa, - confirmMfa: SpaceConnector.clientV2.identity.userProfile.confirmMfa, - getWorkspaces: SpaceConnector.clientV2.identity.userProfile.getWorkspaces>, - getWorkspaceGroups: SpaceConnector.clientV2.identity.userProfile.getWorkspaceGroups>, - + confirmEmail: SpaceConnector.clientV2.identity.userProfile.confirmEmail, + disableMfa: SpaceConnector.clientV2.identity.userProfile.disableMfa, + enableMfa: SpaceConnector.clientV2.identity.userProfile.enableMfa, + confirmMfa: SpaceConnector.clientV2.identity.userProfile.confirmMfa, + getWorkspaces: SpaceConnector.clientV2.identity.userProfile.getWorkspaces, }; return { diff --git a/apps/web/src/api-clients/identity/user-profile/schema/api-verbs/disable-mfa.ts b/apps/web/src/api-clients/identity/user-profile/schema/api-verbs/disable-mfa.ts new file mode 100644 index 0000000000..619b9e7e9f --- /dev/null +++ b/apps/web/src/api-clients/identity/user-profile/schema/api-verbs/disable-mfa.ts @@ -0,0 +1,4 @@ +/* eslint-disable @typescript-eslint/no-empty-interface */ +export interface UserProfileDisableMfaParameters { + // No parameters +} diff --git a/apps/web/src/api-clients/identity/user-profile/schema/constant.ts b/apps/web/src/api-clients/identity/user-profile/schema/constant.ts index 2eb11920bf..be88fccd66 100644 --- a/apps/web/src/api-clients/identity/user-profile/schema/constant.ts +++ b/apps/web/src/api-clients/identity/user-profile/schema/constant.ts @@ -2,3 +2,8 @@ export const MULTI_FACTOR_AUTH_TYPE = { OTP: 'OTP', EMAIL: 'EMAIL', } as const; + +export const MFA_STATE = { + ENABLED: 'ENABLED', + DISABLED: 'DISABLED', +} as const; diff --git a/apps/web/src/api-clients/identity/user-profile/schema/type.ts b/apps/web/src/api-clients/identity/user-profile/schema/type.ts index 1c6a813feb..d1d614f889 100644 --- a/apps/web/src/api-clients/identity/user-profile/schema/type.ts +++ b/apps/web/src/api-clients/identity/user-profile/schema/type.ts @@ -1,3 +1,4 @@ -import type { MULTI_FACTOR_AUTH_TYPE } from '@/api-clients/identity/user-profile/schema/constant'; +import type { MFA_STATE, MULTI_FACTOR_AUTH_TYPE } from '@/api-clients/identity/user-profile/schema/constant'; export type MultiFactorAuthType = typeof MULTI_FACTOR_AUTH_TYPE[keyof typeof MULTI_FACTOR_AUTH_TYPE]; +export type MfaState = typeof MFA_STATE[keyof typeof MFA_STATE]; diff --git a/apps/web/src/api-clients/identity/user/schema/api-verbs/create.ts b/apps/web/src/api-clients/identity/user/schema/api-verbs/create.ts index 7ad83faf97..e55cbc6693 100644 --- a/apps/web/src/api-clients/identity/user/schema/api-verbs/create.ts +++ b/apps/web/src/api-clients/identity/user/schema/api-verbs/create.ts @@ -1,4 +1,5 @@ import type { Tags } from '@/api-clients/_common/schema/model'; +import type { MfaState, MultiFactorAuthType } from '@/api-clients/identity/user-profile/schema/type'; import type { AuthType } from '@/api-clients/identity/user/schema/type'; @@ -12,4 +13,6 @@ export interface UserCreateParameters { timezone?: string; tags?: Tags; reset_password?: boolean; + enforce_mfa_state?: MfaState; + enforce_mfa_type?: MultiFactorAuthType; // only when enforce_mfa_state is ENABLED, this field is required } diff --git a/apps/web/src/api-clients/identity/user/schema/api-verbs/update.ts b/apps/web/src/api-clients/identity/user/schema/api-verbs/update.ts index c7278cc482..dd46464b3c 100644 --- a/apps/web/src/api-clients/identity/user/schema/api-verbs/update.ts +++ b/apps/web/src/api-clients/identity/user/schema/api-verbs/update.ts @@ -1,4 +1,5 @@ import type { Tags } from '@/api-clients/_common/schema/model'; +import type { MfaState, MultiFactorAuthType } from '@/api-clients/identity/user-profile/schema/type'; export interface UserUpdateParameters { @@ -10,4 +11,6 @@ export interface UserUpdateParameters { timezone?: string; tags?: Tags; reset_password?: boolean; + enforce_mfa_state?: MfaState; + enforce_mfa_type?: MultiFactorAuthType; // only when enforce_mfa_state is ENABLED, this field is required } diff --git a/apps/web/src/api-clients/identity/user/schema/model.ts b/apps/web/src/api-clients/identity/user/schema/model.ts index 7edf82ba1d..3cfb3c6e10 100644 --- a/apps/web/src/api-clients/identity/user/schema/model.ts +++ b/apps/web/src/api-clients/identity/user/schema/model.ts @@ -14,7 +14,7 @@ export interface UserModel { auth_type: AuthType; // backend role_type: RoleType; role_id?: string; - mfa: UserMfa; + mfa?: UserMfa; language: string; timezone: string; api_key_count: number; @@ -26,11 +26,13 @@ export interface UserModel { } export interface UserMfa { - state: UserMfaState, - mfa_type: MultiFactorAuthType, - options: { + state?: UserMfaState, + mfa_type?: MultiFactorAuthType, + options?: { + enforce?: boolean, // if true, mfa_type is required email?: string, user_secret_id?: string, + otp_qrcode_uri?: string, // response from 'enable-mfa' verb only } } diff --git a/apps/web/src/api-clients/identity/workspace-user/schema/api-verbs/create.ts b/apps/web/src/api-clients/identity/workspace-user/schema/api-verbs/create.ts index ed17d6383a..5a36caaa45 100644 --- a/apps/web/src/api-clients/identity/workspace-user/schema/api-verbs/create.ts +++ b/apps/web/src/api-clients/identity/workspace-user/schema/api-verbs/create.ts @@ -1,4 +1,5 @@ import type { Tags } from '@/api-clients/_common/schema/model'; +import type { MfaState, MultiFactorAuthType } from '@/api-clients/identity/user-profile/schema/type'; import type { AuthType } from '@/api-clients/identity/user/schema/type'; export interface WorkspaceUserCreateParameters { @@ -11,5 +12,7 @@ export interface WorkspaceUserCreateParameters { timezone?: string; tags?: Tags; reset_password?: boolean; + enforce_mfa_state?: MfaState; + enforce_mfa_type?: MultiFactorAuthType; role_id: string; } diff --git a/apps/web/src/common/components/info-tooltip/InfoTooltip.vue b/apps/web/src/common/components/info-tooltip/InfoTooltip.vue index 5274de29c2..03550df8f2 100644 --- a/apps/web/src/common/components/info-tooltip/InfoTooltip.vue +++ b/apps/web/src/common/components/info-tooltip/InfoTooltip.vue @@ -1,9 +1,11 @@ @@ -62,7 +82,7 @@ const handleClickSendEmailButton = async () => { {{ $t('COMMON.MFA_MODAL.COLLAPSE_DESC') }} {{ $t('COMMON.MFA_MODAL.SEND_NEW_CODE') }} diff --git a/apps/web/src/services/my-page/components/UserAccountMultiFactorAuthModalEmailInfo.vue b/apps/web/src/common/components/mfa/components/EmailInfo.vue similarity index 92% rename from apps/web/src/services/my-page/components/UserAccountMultiFactorAuthModalEmailInfo.vue rename to apps/web/src/common/components/mfa/components/EmailInfo.vue index 18d1d2024b..0330e6f4d7 100644 --- a/apps/web/src/services/my-page/components/UserAccountMultiFactorAuthModalEmailInfo.vue +++ b/apps/web/src/common/components/mfa/components/EmailInfo.vue @@ -16,25 +16,22 @@ import { emailValidator } from '@/lib/helper/user-validation-helper'; import { useFormValidator } from '@/common/composables/form-validator'; import { useProxyValue } from '@/common/composables/proxy-state'; -import { useMultiFactorAuthStore } from '@/services/my-page/stores/multi-factor-auth-store'; - interface Props { - isSentCode?: boolean + isSentCode?: boolean; + isForm?: boolean; } const props = withDefaults(defineProps(), { isSentCode: false, + isForm: false, }); -const multiFactorAuthStore = useMultiFactorAuthStore(); -const multiFactorAuthState = multiFactorAuthStore.state; const userStore = useUserStore(); const emit = defineEmits<{(e: 'update:is-sent-code'): void }>(); const storeState = reactive({ email: computed(() => userStore.state.mfa?.options?.email), - isFormModal: computed(() => multiFactorAuthState.modalType === 'FORM'), }); const state = reactive({ loading: false, @@ -58,7 +55,7 @@ const { const handleClickSendCodeButton = async () => { state.loading = true; try { - if (storeState.isFormModal) { + if (props.isForm) { await postEnableMfa({ mfa_type: MULTI_FACTOR_AUTH_TYPE.EMAIL, options: { @@ -82,9 +79,9 @@ const handleClickSendCodeButton = async () => {