From 1f2b0a358781d119b1456d4dba377fb633bac950 Mon Sep 17 00:00:00 2001 From: DacongDA Date: Wed, 2 Jul 2025 23:05:07 +0800 Subject: [PATCH] feat: add user's MFA items (#3921) --- object/organization.go | 8 +++++++- object/user.go | 3 ++- web/src/Setting.js | 15 ++++++++++++--- web/src/UserEditPage.js | 14 ++++++++++++++ web/src/table/AccountTable.js | 1 + 5 files changed, 36 insertions(+), 5 deletions(-) diff --git a/object/organization.go b/object/organization.go index 18e08f8e..eff0c89f 100644 --- a/object/organization.go +++ b/object/organization.go @@ -536,7 +536,13 @@ func IsNeedPromptMfa(org *Organization, user *User) bool { if org == nil || user == nil { return false } - for _, item := range org.MfaItems { + + mfaItems := org.MfaItems + + if len(user.MfaItems) > 0 { + mfaItems = user.MfaItems + } + for _, item := range mfaItems { if item.Rule == "Required" { if item.Name == EmailType && !user.MfaEmailEnabled { return true diff --git a/object/user.go b/object/user.go index 5a908dba..91465c62 100644 --- a/object/user.go +++ b/object/user.go @@ -212,6 +212,7 @@ type User struct { ManagedAccounts []ManagedAccount `xorm:"managedAccounts blob" json:"managedAccounts"` MfaAccounts []MfaAccount `xorm:"mfaAccounts blob" json:"mfaAccounts"` + MfaItems []*MfaItem `xorm:"varchar(300)" json:"mfaItems"` NeedUpdatePassword bool `json:"needUpdatePassword"` IpWhitelist string `xorm:"varchar(200)" json:"ipWhitelist"` } @@ -795,7 +796,7 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) (bool, er } } if isAdmin { - columns = append(columns, "name", "id", "email", "phone", "country_code", "type", "balance") + columns = append(columns, "name", "id", "email", "phone", "country_code", "type", "balance", "mfa_items") } columns = append(columns, "updated_time") diff --git a/web/src/Setting.js b/web/src/Setting.js index dcfeb46d..3f397eb1 100644 --- a/web/src/Setting.js +++ b/web/src/Setting.js @@ -696,18 +696,27 @@ export const MfaRulePrompted = "Prompted"; export const MfaRuleOptional = "Optional"; export function isRequiredEnableMfa(user, organization) { - if (!user || !organization || !organization.mfaItems) { + if (!user || !organization || (!organization.mfaItems && !user.mfaItems)) { return false; } return getMfaItemsByRules(user, organization, [MfaRuleRequired]).length > 0; } export function getMfaItemsByRules(user, organization, mfaRules = []) { - if (!user || !organization || !organization.mfaItems) { + if (!user || !organization || (!organization.mfaItems && !user.mfaItems)) { return []; } - return organization.mfaItems.filter((mfaItem) => mfaRules.includes(mfaItem.rule)) + let mfaItems = organization.mfaItems; + if (user.mfaItems && user.mfaItems.length !== 0) { + mfaItems = user.mfaItems; + } + + if (mfaItems === null) { + return []; + } + + return mfaItems.filter((mfaItem) => mfaRules.includes(mfaItem.rule)) .filter((mfaItem) => user.multiFactorAuths.some((mfa) => mfa.mfaType === mfaItem.name && !mfa.enabled)); } diff --git a/web/src/UserEditPage.js b/web/src/UserEditPage.js index 3ab930a2..3c449f7b 100644 --- a/web/src/UserEditPage.js +++ b/web/src/UserEditPage.js @@ -42,6 +42,7 @@ import * as MfaBackend from "./backend/MfaBackend"; import AccountAvatar from "./account/AccountAvatar"; import FaceIdTable from "./table/FaceIdTable"; import MfaAccountTable from "./table/MfaAccountTable"; +import MfaTable from "./table/MfaTable"; const {Option} = Select; @@ -926,6 +927,19 @@ class UserEditPage extends React.Component { ); + } else if (accountItem.name === "MFA items") { + return ( + + {Setting.getLabel(i18next.t("general:MFA items"), i18next.t("general:MFA items - Tooltip"))} : + + + {this.updateUserField("mfaItems", value);}} + /> + + ); } else if (accountItem.name === "Multi-factor authentication") { return ( !this.isSelfOrAdmin() ? null : ( diff --git a/web/src/table/AccountTable.js b/web/src/table/AccountTable.js index 3966f0f5..702a7694 100644 --- a/web/src/table/AccountTable.js +++ b/web/src/table/AccountTable.js @@ -110,6 +110,7 @@ class AccountTable extends React.Component { {name: "Managed accounts", label: i18next.t("user:Managed accounts")}, {name: "Face ID", label: i18next.t("user:Face ID")}, {name: "MFA accounts", label: i18next.t("user:MFA accounts")}, + {name: "MFA items", label: i18next.t("general:MFA items")}, ]; };