73 lines
2.6 KiB
TypeScript
73 lines
2.6 KiB
TypeScript
|
|
/**
|
|||
|
|
* 密碼政策(與後端 ValidatePasswordPolicy 完全一致):
|
|||
|
|
* - 至少 12 字元
|
|||
|
|
* - 含大寫、小寫、數字、符號
|
|||
|
|
*
|
|||
|
|
* 使用者可見說明(語言包,勿在 UI 另寫一句):
|
|||
|
|
* - zh: password.policy.hint / api.err.400003
|
|||
|
|
* 「密碼須至少 12 碼,且含大寫、小寫、數字與符號」
|
|||
|
|
* - en: 同 key
|
|||
|
|
* 「Password must be at least 12 characters with upper, lower, digit, and symbol」
|
|||
|
|
* 後端 API 400003 message 使用上述英文句;FE 依 code 轉語言包。
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
/** 與後端 domain.PasswordMinLen 一致 */
|
|||
|
|
export const PASSWORD_MIN_LEN = 12;
|
|||
|
|
|
|||
|
|
/** 與後端 domain.PasswordPolicyZH / FE password.policy.hint 一致 */
|
|||
|
|
export const PASSWORD_POLICY_ZH =
|
|||
|
|
"密碼須至少 12 碼,且含大寫、小寫、數字與符號";
|
|||
|
|
|
|||
|
|
/** 與後端 domain.PasswordPolicyEN / API 400003 / FE en password.policy.hint 一致 */
|
|||
|
|
export const PASSWORD_POLICY_EN =
|
|||
|
|
"Password must be at least 12 characters with upper, lower, digit, and symbol";
|
|||
|
|
|
|||
|
|
export type PasswordPolicyResult = {
|
|||
|
|
ok: boolean;
|
|||
|
|
/** i18n key under password.policy.* or empty if ok */
|
|||
|
|
errorKey?: string;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const HAS_UPPER = /[A-Z]/;
|
|||
|
|
const HAS_LOWER = /[a-z]/;
|
|||
|
|
const HAS_DIGIT = /[0-9]/;
|
|||
|
|
/** 符號:非字母數字(與後端 unicode.IsPunct/Symbol 大致對齊) */
|
|||
|
|
const HAS_SYMBOL = /[^A-Za-z0-9]/;
|
|||
|
|
|
|||
|
|
export function checkPasswordPolicy(password: string): PasswordPolicyResult {
|
|||
|
|
const p = password ?? "";
|
|||
|
|
// 任一條件不符都回同一句(與後端 400003 / password.policy.hint 一致,不拆多種文案)
|
|||
|
|
const ok =
|
|||
|
|
[...p].length >= PASSWORD_MIN_LEN &&
|
|||
|
|
HAS_UPPER.test(p) &&
|
|||
|
|
HAS_LOWER.test(p) &&
|
|||
|
|
HAS_DIGIT.test(p) &&
|
|||
|
|
HAS_SYMBOL.test(p);
|
|||
|
|
if (!ok) {
|
|||
|
|
return { ok: false, errorKey: "password.policy.hint" };
|
|||
|
|
}
|
|||
|
|
return { ok: true };
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** 是否符合政策(提交前 gate) */
|
|||
|
|
export function isPasswordPolicyOk(password: string): boolean {
|
|||
|
|
return checkPasswordPolicy(password).ok;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** 產生符合政策的臨時密碼(mock admin 用) */
|
|||
|
|
export function generateTempPassword(): string {
|
|||
|
|
const lower = "abcdefghijkmnopqrstuvwxyz";
|
|||
|
|
const upper = "ABCDEFGHJKLMNPQRSTUVWXYZ";
|
|||
|
|
const digits = "23456789";
|
|||
|
|
const symbols = "!@#$%^&*+-=";
|
|||
|
|
const all = lower + upper + digits + symbols;
|
|||
|
|
const pick = (s: string) => s[Math.floor(Math.random() * s.length)]!;
|
|||
|
|
const chars = [pick(lower), pick(upper), pick(digits), pick(symbols)];
|
|||
|
|
while (chars.length < 16) chars.push(pick(all));
|
|||
|
|
for (let i = chars.length - 1; i > 0; i--) {
|
|||
|
|
const j = Math.floor(Math.random() * (i + 1));
|
|||
|
|
[chars[i], chars[j]] = [chars[j]!, chars[i]!];
|
|||
|
|
}
|
|||
|
|
return chars.join("");
|
|||
|
|
}
|