diff --git a/es/aspects/token.js b/es/aspects/token.js index 9ccc91f25..13e01d139 100644 --- a/es/aspects/token.js +++ b/es/aspects/token.js @@ -536,23 +536,9 @@ export async function loginByMobile(params, context) { } export async function verifyPassword(params, context) { const { password } = params; - const systemId = context.getSystemId(); - const [pwdPassport] = await context.select('passport', { - data: { - id: 1, - systemId: 1, - config: 1, - type: 1, - enabled: 1, - }, - filter: { - systemId, - enabled: true, - type: 'password', - } - }, { forUpdate: true }); - // assert(pwdPassport); - const pwdMode = pwdPassport?.config?.mode ?? 'all'; + const { system } = context.getApplication(); + const pwdConfig = system?.config.Password; + const pwdMode = pwdConfig?.mode ?? 'all'; let pwdFilter = {}; if (pwdMode === 'all') { pwdFilter = { @@ -664,13 +650,15 @@ export async function loginByAccount(params, context) { id: 1, type: 1, systemId: 1, - } + }, + allowPwd: 1, }, filter: { passport: { systemId, }, applicationId, + allowPwd: true, } }, { dontCollect: true, @@ -761,13 +749,15 @@ export async function loginByAccount(params, context) { id: 1, type: 1, systemId: 1, - } + }, + allowPwd: 1, }, filter: { passport: { systemId, }, applicationId, + allowPwd: true, } }, { dontCollect: true, @@ -875,29 +865,9 @@ export async function loginByAccount(params, context) { } }; const closeRootMode = context.openRootMode(); - const application = context.getApplication(); - const [applicationPassport] = await context.select('applicationPassport', { - data: { - id: 1, - passportId: 1, - passport: { - id: 1, - config: 1, - type: 1, - }, - applicationId: 1, - }, - filter: { - applicationId: application?.id, - passport: { - type: 'password', - }, - } - }, { - dontCollect: true, - }); - // assert(applicationPassport?.passport); - const pwdMode = applicationPassport?.passport?.config?.mode ?? 'all'; + const { system } = context.getApplication(); + const pwdConfig = system?.config.Password; + const pwdMode = pwdConfig?.mode ?? 'all'; let pwdFilter = {}, updateData = {}; if (pwdMode === 'all') { pwdFilter = { @@ -2546,8 +2516,8 @@ export async function refreshToken(params, context) { // 只有server模式去刷新token // 'development' | 'production' | 'staging' const intervals = { - development: 7200 * 1000, - staging: 600 * 1000, + development: 7200 * 1000, // 2小时 + staging: 600 * 1000, // 十分钟 production: 600 * 1000, // 十分钟 }; let applicationId = token.applicationId; diff --git a/es/components/changePassword/byMobile/index.d.ts b/es/components/changePassword/byMobile/index.d.ts index 9e6eb4aca..9b24ef2db 100644 --- a/es/components/changePassword/byMobile/index.d.ts +++ b/es/components/changePassword/byMobile/index.d.ts @@ -1,3 +1,2 @@ -/// declare const _default: (props: import("oak-frontend-base").ReactComponentProps) => React.ReactElement; export default _default; diff --git a/es/components/changePassword/byMobile/index.js b/es/components/changePassword/byMobile/index.js index 0a7382f5d..6c8ba5328 100644 --- a/es/components/changePassword/byMobile/index.js +++ b/es/components/changePassword/byMobile/index.js @@ -54,9 +54,8 @@ export default OakComponent({ lifetimes: { async ready() { const lastSendAt = await this.load(SEND_KEY); - const application = this.features.application.getApplication(); - const { result: applicationPassports } = await this.features.cache.exec('getApplicationPassports', { applicationId: application.id }); - const passwordConfig = applicationPassports.find((ele) => ele.passport.type === 'password')?.passport.config; + const system = this.features.application.getApplication().system; + const passwordConfig = system?.config.Password; const mode = passwordConfig?.mode ?? 'all'; const pwdMin = passwordConfig?.min ?? 8; const pwdMax = passwordConfig?.max ?? 24; diff --git a/es/components/changePassword/byPassword/index.d.ts b/es/components/changePassword/byPassword/index.d.ts index 9e6eb4aca..9b24ef2db 100644 --- a/es/components/changePassword/byPassword/index.d.ts +++ b/es/components/changePassword/byPassword/index.d.ts @@ -1,3 +1,2 @@ -/// declare const _default: (props: import("oak-frontend-base").ReactComponentProps) => React.ReactElement; export default _default; diff --git a/es/components/changePassword/byPassword/index.js b/es/components/changePassword/byPassword/index.js index 435ebdf71..023a58537 100644 --- a/es/components/changePassword/byPassword/index.js +++ b/es/components/changePassword/byPassword/index.js @@ -23,9 +23,8 @@ export default OakComponent({ }, lifetimes: { async ready() { - const application = this.features.application.getApplication(); - const { result: applicationPassports } = await this.features.cache.exec('getApplicationPassports', { applicationId: application.id }); - const passwordConfig = applicationPassports.find((ele) => ele.passport.type === 'password')?.passport.config; + const system = this.features.application.getApplication().system; + const passwordConfig = system?.config.Password; const mode = passwordConfig?.mode ?? 'all'; const pwdMin = passwordConfig?.min ?? 8; const pwdMax = passwordConfig?.max ?? 24; diff --git a/es/components/config/style/platform/index.d.ts b/es/components/config/style/platform/index.d.ts index d8ca7e5a9..9224d2cdc 100644 --- a/es/components/config/style/platform/index.d.ts +++ b/es/components/config/style/platform/index.d.ts @@ -1,7 +1,7 @@ import { Style } from '../../../../types/Style'; declare const _default: (props: import("oak-frontend-base").ReactComponentProps) => React.ReactElement; diff --git a/es/components/config/upsert/index.d.ts b/es/components/config/upsert/index.d.ts index 9bf014f88..a4b61c5c2 100644 --- a/es/components/config/upsert/index.d.ts +++ b/es/components/config/upsert/index.d.ts @@ -1,7 +1,7 @@ import { Config } from '../../../types/Config'; declare const _default: (props: import("oak-frontend-base").ReactComponentProps) => React.ReactElement; diff --git a/es/components/config/upsert/password/index.d.ts b/es/components/config/upsert/password/index.d.ts new file mode 100644 index 000000000..424f55446 --- /dev/null +++ b/es/components/config/upsert/password/index.d.ts @@ -0,0 +1,7 @@ +import React from 'react'; +import { Config } from '../../../../types/Config'; +export default function Security(props: { + password: Required['Password']; + setValue: (path: string, value: any) => void; + setValues: (value: Record) => void; +}): React.JSX.Element; diff --git a/es/components/config/upsert/password/index.js b/es/components/config/upsert/password/index.js new file mode 100644 index 000000000..529d0d9cf --- /dev/null +++ b/es/components/config/upsert/password/index.js @@ -0,0 +1,78 @@ +import React, { useEffect, useState } from 'react'; +import { Col, Divider, Input, Form, Space, Radio, InputNumber, Switch, } from 'antd'; +import Styles from './web.module.less'; +import EditorRegexs from '../../../passport/password/editorRegexs'; +export default function Security(props) { + const { password, setValue, setValues } = props; + const { mode, min, max, verify, regexs, tip } = password || {}; + const [newTip, setNewTip] = useState(''); + useEffect(() => { + const { password } = props; + if (!password.mode) { + setValues({ + mode: 'all', + min: 8, + max: 24, + }); + } + }, [password]); + useEffect(() => { + if (tip && !newTip) { + setNewTip(tip); + } + }, [tip]); + return ( + + + 密码设置 + +
+ + { + const { value } = target; + setValue('mode', value); + }} value={mode}> + 明文与SHA1加密 + 仅明文 + 仅SHA1加密 + + + + + { + setValue('min', value); + }}/> +
~
+ { + setValue('max', value); + }}/> +
+
+ + + { + setValue('verigy', checked); + }}/> + + + <> + {!!verify ? (<> + { + setValue('regexs', regexs); + }}/> + ) : (
暂未启用正则校验,无需设置
)} + +
+ + { + setNewTip(e.target.value); + }} onBlur={() => { + if (newTip && newTip !== tip) { + setValue('tip', newTip); + } + }}/> + +
+ +
); +} diff --git a/es/components/config/upsert/password/web.module.less b/es/components/config/upsert/password/web.module.less new file mode 100644 index 000000000..7fa576eed --- /dev/null +++ b/es/components/config/upsert/password/web.module.less @@ -0,0 +1,16 @@ + +.label { + color: var(--oak-text-color-primary); + font-size: 28px; + line-height: 36px; +} + +.tips { + color: var(--oak-text-color-placeholder); + font-size: 12px; +} + +.title { + margin-bottom: 0px; + margin-top:36px; +} \ No newline at end of file diff --git a/es/components/config/upsert/web.pc.js b/es/components/config/upsert/web.pc.js index 3167e78a3..5a0e68990 100644 --- a/es/components/config/upsert/web.pc.js +++ b/es/components/config/upsert/web.pc.js @@ -9,10 +9,11 @@ import Sms from './sms/index'; import Email from './email/index'; import Basic from './basic/index'; import Security from './security/index'; +import Password from './password/index'; export default function Render(props) { const { entity, name, currentConfig, dirty } = props.data; const { resetConfig, updateConfig, setValue, setValues, removeItem, cleanKey, t } = props.methods; - const { Account: account, Cos: cos, Map: map, Live: live, Sms: sms, App: app, Emails: emails, Security: security, } = currentConfig || {}; + const { Account: account, Cos: cos, Map: map, Live: live, Sms: sms, App: app, Emails: emails, Security: security, Password: password, } = currentConfig || {}; return (<> @@ -84,6 +85,15 @@ export default function Render(props) { }); }}/>), }, + { + key: '密码设置', + label: '密码设置', + children: ( setValue(`Password.${path}`, value)} setValues={(value) => { + setValues({ + Password: value + }); + }}/>), + }, ]}> ); diff --git a/es/components/user/password/update/index.js b/es/components/user/password/update/index.js index f532eb4fb..0cec4926e 100644 --- a/es/components/user/password/update/index.js +++ b/es/components/user/password/update/index.js @@ -34,9 +34,8 @@ export default OakComponent({ }, lifetimes: { async ready() { - const application = this.features.application.getApplication(); - const { result: applicationPassports } = await this.features.cache.exec('getApplicationPassports', { applicationId: application.id }); - const passwordConfig = applicationPassports.find((ele) => ele.passport.type === 'password')?.passport.config; + const system = this.features.application.getApplication().system; + const passwordConfig = system?.config.Password; const mode = passwordConfig?.mode ?? 'all'; const pwdMin = passwordConfig?.min ?? 8; const pwdMax = passwordConfig?.max ?? 24; diff --git a/es/components/user/password/verify/index.d.ts b/es/components/user/password/verify/index.d.ts index c5fb2e667..721288d6b 100644 --- a/es/components/user/password/verify/index.d.ts +++ b/es/components/user/password/verify/index.d.ts @@ -1,4 +1,4 @@ declare const _default: (props: import("oak-frontend-base").ReactComponentProps void) | undefined; + onVerified: undefined | (() => void); }>) => React.ReactElement; export default _default; diff --git a/es/components/user/password/verify/index.js b/es/components/user/password/verify/index.js index 1c7bd268e..752bed879 100644 --- a/es/components/user/password/verify/index.js +++ b/es/components/user/password/verify/index.js @@ -11,9 +11,8 @@ export default OakComponent({ lifetimes: { async ready() { this.features.token.getToken(); - const application = this.features.application.getApplication(); - const { result: applicationPassports } = await this.features.cache.exec('getApplicationPassports', { applicationId: application.id }); - const passwordConfig = applicationPassports.find((ele) => ele.passport.type === 'password')?.passport.config; + const system = this.features.application.getApplication().system; + const passwordConfig = system?.config.Password; const mode = passwordConfig?.mode ?? 'all'; this.setState({ mode, diff --git a/es/components/user/password/verify/web.d.ts b/es/components/user/password/verify/web.d.ts index cb271e15f..8c7e08cdd 100644 --- a/es/components/user/password/verify/web.d.ts +++ b/es/components/user/password/verify/web.d.ts @@ -1,4 +1,3 @@ -/// import { WebComponentProps } from "oak-frontend-base"; import { EntityDict } from "../../../../oak-app-domain"; export default function Render(props: WebComponentProps import { WebComponentProps } from "oak-frontend-base"; import { EntityDict } from "../../../../oak-app-domain"; export default function Render(props: WebComponentProps(props: ReactComponentProps) => React.ReactElement; export default _default; diff --git a/es/components/userRelation/upsert/byUser/index.d.ts b/es/components/userRelation/upsert/byUser/index.d.ts index 57ff8d988..4940119bf 100644 --- a/es/components/userRelation/upsert/byUser/index.d.ts +++ b/es/components/userRelation/upsert/byUser/index.d.ts @@ -4,7 +4,7 @@ import { EntityDict as BaseEntityDict } from 'oak-domain/lib/types/Entity'; declare const _default: (props: ReactComponentProps) => React.ReactElement; export default _default; diff --git a/es/components/userRelation/upsert/byUserEntityGrant/index.d.ts b/es/components/userRelation/upsert/byUserEntityGrant/index.d.ts index 5c6e1bdab..f72c8f3fc 100644 --- a/es/components/userRelation/upsert/byUserEntityGrant/index.d.ts +++ b/es/components/userRelation/upsert/byUserEntityGrant/index.d.ts @@ -7,12 +7,12 @@ declare const _default: void) | undefined; + rule: EntityDict["userEntityGrant"]["Schema"]["rule"]; + ruleOnRow: EntityDict["userEntityGrant"]["OpSchema"]["ruleOnRow"]; + onUserEntityGrantCreated?: (id: string) => void; }>) => React.ReactElement; export default _default; diff --git a/es/components/userRelation/upsert/index.d.ts b/es/components/userRelation/upsert/index.d.ts index 924306f4c..bfce90975 100644 --- a/es/components/userRelation/upsert/index.d.ts +++ b/es/components/userRelation/upsert/index.d.ts @@ -7,8 +7,8 @@ declare const _default: ; - mode: 'byMobile' | 'byUserEntityGrant' | 'byEmail'; + passwordRequired?: boolean; + disabledMethods: Array<"email" | "mobile" | "userEntityGrant">; + mode: "byMobile" | "byUserEntityGrant" | "byEmail"; }>) => React.ReactElement; export default _default; diff --git a/es/components/userRelation/upsert/onUser/index.d.ts b/es/components/userRelation/upsert/onUser/index.d.ts index cafe99e04..1f0ec1ff8 100644 --- a/es/components/userRelation/upsert/onUser/index.d.ts +++ b/es/components/userRelation/upsert/onUser/index.d.ts @@ -2,7 +2,7 @@ import { EntityDict } from '../../../../oak-app-domain'; declare const _default: (props: import("oak-frontend-base").ReactComponentProps void; passwordRequired: boolean; diff --git a/es/components/userRelation/upsert/onUser/index.js b/es/components/userRelation/upsert/onUser/index.js index 370179a1d..88bae726e 100644 --- a/es/components/userRelation/upsert/onUser/index.js +++ b/es/components/userRelation/upsert/onUser/index.js @@ -84,9 +84,8 @@ export default OakComponent({ }, lifetimes: { async ready() { - const application = this.features.application.getApplication(); - const { result: applicationPassports } = await this.features.cache.exec('getApplicationPassports', { applicationId: application.id }); - const passwordConfig = applicationPassports.find((ele) => ele.passport.type === 'password')?.passport.config; + const system = this.features.application.getApplication().system; + const passwordConfig = system?.config.Password; const mode = passwordConfig?.mode ?? 'all'; const pwdMin = passwordConfig?.min ?? 8; const pwdMax = passwordConfig?.max ?? 24; diff --git a/es/components/userRelation/upsert/onUser/userRelation/index.d.ts b/es/components/userRelation/upsert/onUser/userRelation/index.d.ts index 5db0a88be..3ce27eed3 100644 --- a/es/components/userRelation/upsert/onUser/userRelation/index.d.ts +++ b/es/components/userRelation/upsert/onUser/userRelation/index.d.ts @@ -2,6 +2,6 @@ import { EntityDict } from '../../../../../oak-app-domain'; declare const _default: (props: import("oak-frontend-base").ReactComponentProps) => React.ReactElement; export default _default; diff --git a/es/types/Config.d.ts b/es/types/Config.d.ts index 36e54fe4a..9a713bf07 100644 --- a/es/types/Config.d.ts +++ b/es/types/Config.d.ts @@ -151,6 +151,14 @@ export type EmailConfig = { name?: string; secure?: boolean; }; +export type PasswordConfig = { + mode?: 'all' | 'plain' | 'sha1'; + min?: number; + max?: number; + verify?: boolean; + regexs?: string[]; + tip?: string; +}; export type QrCodeType = 'wechatMpDomainUrl' | 'wechatMpWxaCode' | 'wechatPublic' | 'wechatPublicForMp' | 'webForWechatPublic'; export type Config = { Account?: { @@ -202,6 +210,7 @@ export type Config = { level?: 'weak' | 'medium' | 'strong'; passwordVerifyGap?: number; }; + Password?: PasswordConfig; }; export type AccountOrigin = 'ali' | 'tencent' | 'qiniu' | 'amap' | 'ctyun' | 'local' | 's3'; export type CosOrigin = 'qiniu' | 'wechat' | 'ctyun' | 'aliyun' | 'tencent' | 'local' | 'unknown' | 's3'; diff --git a/lib/aspects/token.js b/lib/aspects/token.js index 120cfba2b..6a82a9d86 100644 --- a/lib/aspects/token.js +++ b/lib/aspects/token.js @@ -1,6 +1,28 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.loginWebByMpToken = exports.refreshToken = exports.wakeupParasite = exports.logout = exports.getWechatMpUserPhoneNumber = exports.switchTo = exports.sendCaptchaByEmail = exports.sendCaptchaByMobile = exports.syncUserInfoWechatMp = exports.loginWechatMp = exports.loginWechat = exports.loginWechatNative = exports.loginByWechat = exports.refreshWechatPublicUserInfo = exports.setUserAvatarFromWechat = exports.bindByEmail = exports.bindByMobile = exports.loginByEmail = exports.loginByAccount = exports.verifyPassword = exports.loginByMobile = exports.loadTokenInfo = exports.setUpTokenAndUser = void 0; +exports.setUpTokenAndUser = setUpTokenAndUser; +exports.loadTokenInfo = loadTokenInfo; +exports.loginByMobile = loginByMobile; +exports.verifyPassword = verifyPassword; +exports.loginByAccount = loginByAccount; +exports.loginByEmail = loginByEmail; +exports.bindByMobile = bindByMobile; +exports.bindByEmail = bindByEmail; +exports.setUserAvatarFromWechat = setUserAvatarFromWechat; +exports.refreshWechatPublicUserInfo = refreshWechatPublicUserInfo; +exports.loginByWechat = loginByWechat; +exports.loginWechatNative = loginWechatNative; +exports.loginWechat = loginWechat; +exports.loginWechatMp = loginWechatMp; +exports.syncUserInfoWechatMp = syncUserInfoWechatMp; +exports.sendCaptchaByMobile = sendCaptchaByMobile; +exports.sendCaptchaByEmail = sendCaptchaByEmail; +exports.switchTo = switchTo; +exports.getWechatMpUserPhoneNumber = getWechatMpUserPhoneNumber; +exports.logout = logout; +exports.wakeupParasite = wakeupParasite; +exports.refreshToken = refreshToken; +exports.loginWebByMpToken = loginWebByMpToken; const tslib_1 = require("tslib"); const uuid_1 = require("oak-domain/lib/utils/uuid"); const WechatSDK_1 = tslib_1.__importDefault(require("oak-external-sdk/lib/WechatSDK")); @@ -380,7 +402,6 @@ createData, user) { } } } -exports.setUpTokenAndUser = setUpTokenAndUser; async function setupMobile(mobile, env, context) { const result2 = await context.select('mobile', { data: { @@ -444,7 +465,6 @@ async function loadTokenInfo(tokenValue, context) { }, }, {}); } -exports.loadTokenInfo = loadTokenInfo; async function loginByMobile(params, context) { const { mobile, captcha, env, disableRegister } = params; const loginLogic = async (isRoot) => { @@ -540,26 +560,11 @@ async function loginByMobile(params, context) { closeRootMode(); return tokenValue; } -exports.loginByMobile = loginByMobile; async function verifyPassword(params, context) { const { password } = params; - const systemId = context.getSystemId(); - const [pwdPassport] = await context.select('passport', { - data: { - id: 1, - systemId: 1, - config: 1, - type: 1, - enabled: 1, - }, - filter: { - systemId, - enabled: true, - type: 'password', - } - }, { forUpdate: true }); - // assert(pwdPassport); - const pwdMode = pwdPassport?.config?.mode ?? 'all'; + const { system } = context.getApplication(); + const pwdConfig = system?.config.Password; + const pwdMode = pwdConfig?.mode ?? 'all'; let pwdFilter = {}; if (pwdMode === 'all') { pwdFilter = { @@ -605,7 +610,6 @@ async function verifyPassword(params, context) { } }, {}); } -exports.verifyPassword = verifyPassword; async function loginByAccount(params, context) { const { account, password, env } = params; let needUpdatePassword = false; @@ -672,13 +676,15 @@ async function loginByAccount(params, context) { id: 1, type: 1, systemId: 1, - } + }, + allowPwd: 1, }, filter: { passport: { systemId, }, applicationId, + allowPwd: true, } }, { dontCollect: true, @@ -769,13 +775,15 @@ async function loginByAccount(params, context) { id: 1, type: 1, systemId: 1, - } + }, + allowPwd: 1, }, filter: { passport: { systemId, }, applicationId, + allowPwd: true, } }, { dontCollect: true, @@ -883,29 +891,9 @@ async function loginByAccount(params, context) { } }; const closeRootMode = context.openRootMode(); - const application = context.getApplication(); - const [applicationPassport] = await context.select('applicationPassport', { - data: { - id: 1, - passportId: 1, - passport: { - id: 1, - config: 1, - type: 1, - }, - applicationId: 1, - }, - filter: { - applicationId: application?.id, - passport: { - type: 'password', - }, - } - }, { - dontCollect: true, - }); - // assert(applicationPassport?.passport); - const pwdMode = applicationPassport?.passport?.config?.mode ?? 'all'; + const { system } = context.getApplication(); + const pwdConfig = system?.config.Password; + const pwdMode = pwdConfig?.mode ?? 'all'; let pwdFilter = {}, updateData = {}; if (pwdMode === 'all') { pwdFilter = { @@ -958,7 +946,6 @@ async function loginByAccount(params, context) { closeRootMode(); return tokenValue; } -exports.loginByAccount = loginByAccount; async function loginByEmail(params, context) { const { email, captcha, env, disableRegister } = params; const loginLogic = async () => { @@ -1029,7 +1016,6 @@ async function loginByEmail(params, context) { closeRootMode(); return tokenValue; } -exports.loginByEmail = loginByEmail; async function bindByMobile(params, context) { const { mobile, captcha, env, } = params; const userId = context.getCurrentUserId(); @@ -1141,7 +1127,6 @@ async function bindByMobile(params, context) { await bindLogic(); closeRootMode(); } -exports.bindByMobile = bindByMobile; async function bindByEmail(params, context) { const { email, captcha, env, } = params; const userId = context.getCurrentUserId(); @@ -1254,7 +1239,6 @@ async function bindByEmail(params, context) { await bindLogic(); closeRootMode(); } -exports.bindByEmail = bindByEmail; async function setupLoginName(name, env, context) { const result2 = await context.select('loginName', { data: { @@ -1495,7 +1479,6 @@ async function setUserAvatarFromWechat(params, context) { }, {}); } } -exports.setUserAvatarFromWechat = setUserAvatarFromWechat; async function tryRefreshWechatPublicUserInfo(wechatUserId, context) { const [wechatUser] = await context.select('wechatUser', { data: { @@ -1584,7 +1567,6 @@ async function refreshWechatPublicUserInfo({}, context) { (0, assert_1.assert)(token.entityId); return await tryRefreshWechatPublicUserInfo(token.entityId, context); } -exports.refreshWechatPublicUserInfo = refreshWechatPublicUserInfo; // 用户在微信端授权登录后,在web端触发该方法 async function loginByWechat(params, context) { const { wechatLoginId, env } = params; @@ -1614,7 +1596,6 @@ async function loginByWechat(params, context) { closeRootMode(); return tokenValue; } -exports.loginByWechat = loginByWechat; async function loginFromWechatEnv(code, env, context, wechatLoginId) { const application = context.getApplication(); const { type, config, systemId } = application; @@ -1958,7 +1939,6 @@ async function loginWechatNative({ code, env, }, context) { closeRootMode(); return tokenValue; } -exports.loginWechatNative = loginWechatNative; /** * 公众号授权登录 * @param param0 @@ -1989,7 +1969,6 @@ async function loginWechat({ code, env, wechatLoginId, }, context) { closeRootMode(); return tokenValue; } -exports.loginWechat = loginWechat; /** * 小程序授权登录 * @param param0 @@ -2003,7 +1982,6 @@ async function loginWechatMp({ code, env, }, context) { closeRootMode(); return tokenValue; } -exports.loginWechatMp = loginWechatMp; /** * 同步从wx.getUserProfile拿到的用户信息 * @param param0 @@ -2057,7 +2035,6 @@ async function syncUserInfoWechatMp({ nickname, avatarUrl, encryptedData, iv, si // 实测发现解密出来的和userInfo完全一致…… await setUserInfoFromWechat(user, { nickname, avatar: avatarUrl }, context); } -exports.syncUserInfoWechatMp = syncUserInfoWechatMp; async function sendCaptchaByMobile({ mobile, env, type: captchaType, }, context) { const { type } = env; let visitorId = mobile; @@ -2222,7 +2199,6 @@ async function sendCaptchaByMobile({ mobile, env, type: captchaType, }, context) return '验证码发送失败'; } } -exports.sendCaptchaByMobile = sendCaptchaByMobile; async function sendCaptchaByEmail({ email, env, type: captchaType, }, context) { const { type } = env; let visitorId = email; @@ -2370,7 +2346,6 @@ async function sendCaptchaByEmail({ email, env, type: captchaType, }, context) { return '验证码发送失败'; } } -exports.sendCaptchaByEmail = sendCaptchaByEmail; async function switchTo({ userId }, context) { const reallyRoot = context.isReallyRoot(); if (!reallyRoot) { @@ -2392,7 +2367,6 @@ async function switchTo({ userId }, context) { }, }, {}); } -exports.switchTo = switchTo; async function getWechatMpUserPhoneNumber({ code, env }, context) { const application = context.getApplication(); const { type, config, systemId } = application; @@ -2408,7 +2382,6 @@ async function getWechatMpUserPhoneNumber({ code, env }, context) { closeRootMode(); return reuslt; } -exports.getWechatMpUserPhoneNumber = getWechatMpUserPhoneNumber; async function logout(params, context) { const { tokenValue } = params; if (tokenValue) { @@ -2431,7 +2404,6 @@ async function logout(params, context) { closeRootMode(); } } -exports.logout = logout; /** * 创建一个当前parasite上的token * @param params @@ -2498,7 +2470,6 @@ async function wakeupParasite(params, context) { closeRootMode(); return tokenValue; } -exports.wakeupParasite = wakeupParasite; /** * todo 检查登录环境一致性,同一个token不能跨越不同设备 * @param env1 @@ -2571,8 +2542,8 @@ async function refreshToken(params, context) { // 只有server模式去刷新token // 'development' | 'production' | 'staging' const intervals = { - development: 7200 * 1000, - staging: 600 * 1000, + development: 7200 * 1000, // 2小时 + staging: 600 * 1000, // 十分钟 production: 600 * 1000, // 十分钟 }; let applicationId = token.applicationId; @@ -2645,7 +2616,6 @@ async function refreshToken(params, context) { closeRootMode(); return tokenValue; } -exports.refreshToken = refreshToken; /** * 使用微信小程序中的token登录web * @param tokenValue @@ -2713,4 +2683,3 @@ async function loginWebByMpToken(params, context) { closeRootMode(); return tokenValue; } -exports.loginWebByMpToken = loginWebByMpToken; diff --git a/lib/types/Config.d.ts b/lib/types/Config.d.ts index 36e54fe4a..9a713bf07 100644 --- a/lib/types/Config.d.ts +++ b/lib/types/Config.d.ts @@ -151,6 +151,14 @@ export type EmailConfig = { name?: string; secure?: boolean; }; +export type PasswordConfig = { + mode?: 'all' | 'plain' | 'sha1'; + min?: number; + max?: number; + verify?: boolean; + regexs?: string[]; + tip?: string; +}; export type QrCodeType = 'wechatMpDomainUrl' | 'wechatMpWxaCode' | 'wechatPublic' | 'wechatPublicForMp' | 'webForWechatPublic'; export type Config = { Account?: { @@ -202,6 +210,7 @@ export type Config = { level?: 'weak' | 'medium' | 'strong'; passwordVerifyGap?: number; }; + Password?: PasswordConfig; }; export type AccountOrigin = 'ali' | 'tencent' | 'qiniu' | 'amap' | 'ctyun' | 'local' | 's3'; export type CosOrigin = 'qiniu' | 'wechat' | 'ctyun' | 'aliyun' | 'tencent' | 'local' | 'unknown' | 's3'; diff --git a/src/aspects/token.ts b/src/aspects/token.ts index 40c7a694b..8014e6804 100644 --- a/src/aspects/token.ts +++ b/src/aspects/token.ts @@ -40,7 +40,7 @@ import { sendSms } from '../utils/sms'; import { mergeUser } from './user'; import { cloneDeep, pick } from 'oak-domain/lib/utils/lodash'; import { BRC } from '../types/RuntimeCxt'; -import { PwdConfig, SmsConfig } from '../entities/Passport'; +import { SmsConfig } from '../entities/Passport'; import { sendEmail } from '../utils/email'; import { EmailConfig } from '../oak-app-domain/Passport/Schema'; import { isEmail, isMobile } from 'oak-domain/lib/utils/validator'; @@ -730,23 +730,9 @@ export async function verifyPassword( context: BRC ) { const { password } = params; - const systemId = context.getSystemId(); - const [pwdPassport] = await context.select('passport', { - data: { - id: 1, - systemId: 1, - config: 1, - type: 1, - enabled: 1, - }, - filter: { - systemId, - enabled: true, - type: 'password', - } - }, { forUpdate: true }); - // assert(pwdPassport); - const pwdMode = (pwdPassport?.config as PwdConfig)?.mode ?? 'all'; + const { system } = context.getApplication()!; + const pwdConfig = system?.config.Password; + const pwdMode = pwdConfig?.mode ?? 'all'; let pwdFilter = {}; if (pwdMode === 'all') { pwdFilter = { @@ -881,13 +867,15 @@ export async function loginByAccount( id: 1, type: 1, systemId: 1, - } + }, + allowPwd: 1, }, filter: { passport: { systemId, }, applicationId, + allowPwd: true, } }, { @@ -991,13 +979,15 @@ export async function loginByAccount( id: 1, type: 1, systemId: 1, - } + }, + allowPwd: 1, }, filter: { passport: { systemId, }, applicationId, + allowPwd: true, } }, { @@ -1119,32 +1109,9 @@ export async function loginByAccount( } }; const closeRootMode = context.openRootMode(); - const application = context.getApplication(); - const [applicationPassport] = await context.select('applicationPassport', - { - data: { - id: 1, - passportId: 1, - passport: { - id: 1, - config: 1, - type: 1, - }, - applicationId: 1, - }, - filter: { - applicationId: application?.id!, - passport: { - type: 'password', - }, - } - }, - { - dontCollect: true, - } - ); - // assert(applicationPassport?.passport); - const pwdMode = (applicationPassport?.passport?.config as PwdConfig)?.mode ?? 'all'; + const { system } = context.getApplication()!; + const pwdConfig = system?.config.Password; + const pwdMode = pwdConfig?.mode ?? 'all'; let pwdFilter = {}, updateData = {}; if (pwdMode === 'all') { pwdFilter = { @@ -3324,7 +3291,7 @@ export async function refreshToken( if (!token) { throw new OakUnloggedInException("Token令牌已失效,请重新登录"); } - + const now = Date.now(); if (!checkTokenEnvConsistency(env, token.env as WebEnv)) { console.log('####### refreshToken 环境改变 start #######\n'); diff --git a/src/components/changePassword/byMobile/index.ts b/src/components/changePassword/byMobile/index.ts index 12b5c323f..e86791f97 100644 --- a/src/components/changePassword/byMobile/index.ts +++ b/src/components/changePassword/byMobile/index.ts @@ -63,9 +63,8 @@ export default OakComponent({ lifetimes: { async ready() { const lastSendAt = await this.load(SEND_KEY); - const application = this.features.application.getApplication(); - const { result: applicationPassports } = await this.features.cache.exec('getApplicationPassports', { applicationId: application.id }); - const passwordConfig: PwdConfig = applicationPassports.find((ele: EntityDict['applicationPassport']['Schema']) => ele.passport.type === 'password')?.passport.config; + const system = this.features.application.getApplication().system; + const passwordConfig = system?.config.Password; const mode = passwordConfig?.mode ?? 'all'; const pwdMin = passwordConfig?.min ?? 8; const pwdMax = passwordConfig?.max ?? 24; diff --git a/src/components/changePassword/byPassword/index.ts b/src/components/changePassword/byPassword/index.ts index 3afe1f8a9..eace07d57 100644 --- a/src/components/changePassword/byPassword/index.ts +++ b/src/components/changePassword/byPassword/index.ts @@ -26,9 +26,8 @@ export default OakComponent({ }, lifetimes: { async ready() { - const application = this.features.application.getApplication(); - const { result: applicationPassports } = await this.features.cache.exec('getApplicationPassports', { applicationId: application.id }); - const passwordConfig: PwdConfig = applicationPassports.find((ele: EntityDict['applicationPassport']['Schema']) => ele.passport.type === 'password')?.passport.config; + const system = this.features.application.getApplication().system; + const passwordConfig = system?.config.Password; const mode = passwordConfig?.mode ?? 'all'; const pwdMin = passwordConfig?.min ?? 8; const pwdMax = passwordConfig?.max ?? 24; diff --git a/src/components/config/upsert/password/index.tsx b/src/components/config/upsert/password/index.tsx new file mode 100644 index 000000000..f559c7107 --- /dev/null +++ b/src/components/config/upsert/password/index.tsx @@ -0,0 +1,136 @@ +import React, { useEffect, useState } from 'react'; +import { + Tabs, + Row, + Col, + Card, + Divider, + Input, + Form, + Space, + Select, + Radio, + InputNumber, + Switch, +} from 'antd'; +import Styles from './web.module.less'; +import { Config } from '../../../../types/Config'; +import EditorRegexs from '../../../passport/password/editorRegexs'; + +export default function Security(props: { + password: Required['Password']; + setValue: (path: string, value: any) => void; + setValues: (value: Record) => void; +}) { + const { password, setValue, setValues } = props; + const { mode, min, max, verify, regexs, tip } = password || {}; + const [newTip, setNewTip] = useState(''); + + useEffect(() => { + const { password } = props; + if (!password.mode) { + setValues({ + mode: 'all', + min: 8, + max: 24, + }); + } + }, [password]); + + useEffect(() => { + if (tip && !newTip) { + setNewTip(tip) + } + }, [tip]); + + return ( + + + + 密码设置 + +
+ + { + const { value } = target; + setValue('mode', value); + }} + value={mode} + > + 明文与SHA1加密 + 仅明文 + 仅SHA1加密 + + + + + { + setValue('min', value); + }} /> +
~
+ { + setValue('max', value); + }} /> +
+
+ + + { + setValue('verigy', checked) + }} + /> + + + <> + {!!verify ? ( + <> + { + setValue('regexs', regexs); + }} /> + + ) : ( +
暂未启用正则校验,无需设置
+ )} + +
+ + { + setNewTip(e.target.value); + }} + onBlur={() => { + if (newTip && newTip !== tip) { + setValue('tip', newTip); + } + }} + /> + +
+ +
+ ); +} \ No newline at end of file diff --git a/src/components/config/upsert/password/web.module.less b/src/components/config/upsert/password/web.module.less new file mode 100644 index 000000000..7fa576eed --- /dev/null +++ b/src/components/config/upsert/password/web.module.less @@ -0,0 +1,16 @@ + +.label { + color: var(--oak-text-color-primary); + font-size: 28px; + line-height: 36px; +} + +.tips { + color: var(--oak-text-color-placeholder); + font-size: 12px; +} + +.title { + margin-bottom: 0px; + margin-top:36px; +} \ No newline at end of file diff --git a/src/components/config/upsert/web.pc.tsx b/src/components/config/upsert/web.pc.tsx index b42eec2b7..eb3e2cd63 100644 --- a/src/components/config/upsert/web.pc.tsx +++ b/src/components/config/upsert/web.pc.tsx @@ -9,6 +9,7 @@ import Sms from './sms/index'; import Email from './email/index'; import Basic from './basic/index'; import Security from './security/index'; +import Password from './password/index'; import { Config } from '../../../types/Config'; import { EntityDict } from '../../../oak-app-domain'; @@ -47,6 +48,7 @@ export default function Render( App: app, Emails: emails, Security: security, + Password: password, } = currentConfig || {}; return ( <> @@ -228,6 +230,23 @@ export default function Render( /> ), }, + { + key: '密码设置', + label: '密码设置', + children: ( + + setValue(`Password.${path}`, value) + } + setValues={(value) => { + setValues({ + Password: value + }); + }} + /> + ), + }, ]} > diff --git a/src/components/user/password/update/index.ts b/src/components/user/password/update/index.ts index cb263fa81..9f36ed797 100644 --- a/src/components/user/password/update/index.ts +++ b/src/components/user/password/update/index.ts @@ -38,9 +38,8 @@ export default OakComponent({ }, lifetimes: { async ready() { - const application = this.features.application.getApplication(); - const { result: applicationPassports } = await this.features.cache.exec('getApplicationPassports', { applicationId: application.id }); - const passwordConfig: PwdConfig = applicationPassports.find((ele: EntityDict['applicationPassport']['Schema']) => ele.passport.type === 'password')?.passport.config; + const system = this.features.application.getApplication().system; + const passwordConfig = system?.config.Password; const mode = passwordConfig?.mode ?? 'all'; const pwdMin = passwordConfig?.min ?? 8; const pwdMax = passwordConfig?.max ?? 24; diff --git a/src/components/user/password/verify/index.ts b/src/components/user/password/verify/index.ts index 0fee05c23..826700709 100644 --- a/src/components/user/password/verify/index.ts +++ b/src/components/user/password/verify/index.ts @@ -14,9 +14,8 @@ export default OakComponent({ lifetimes: { async ready() { this.features.token.getToken(); - const application = this.features.application.getApplication(); - const { result: applicationPassports } = await this.features.cache.exec('getApplicationPassports', { applicationId: application.id }); - const passwordConfig: PwdConfig = applicationPassports.find((ele: EntityDict['applicationPassport']['Schema']) => ele.passport.type === 'password')?.passport.config; + const system = this.features.application.getApplication().system; + const passwordConfig = system?.config.Password; const mode = passwordConfig?.mode ?? 'all'; this.setState({ mode, diff --git a/src/components/userRelation/upsert/onUser/index.ts b/src/components/userRelation/upsert/onUser/index.ts index 711d0e50c..011c62689 100644 --- a/src/components/userRelation/upsert/onUser/index.ts +++ b/src/components/userRelation/upsert/onUser/index.ts @@ -90,9 +90,8 @@ export default OakComponent({ }, lifetimes: { async ready() { - const application = this.features.application.getApplication(); - const { result: applicationPassports } = await this.features.cache.exec('getApplicationPassports', { applicationId: application.id }); - const passwordConfig: PwdConfig = applicationPassports.find((ele: EntityDict['applicationPassport']['Schema']) => ele.passport.type === 'password')?.passport.config; + const system = this.features.application.getApplication().system; + const passwordConfig = system?.config.Password; const mode = passwordConfig?.mode ?? 'all'; const pwdMin = passwordConfig?.min ?? 8; const pwdMax = passwordConfig?.max ?? 24; diff --git a/src/types/Config.ts b/src/types/Config.ts index 75eea59dc..cdc0a865a 100644 --- a/src/types/Config.ts +++ b/src/types/Config.ts @@ -81,7 +81,7 @@ export type LocalCosConfig = { }; // S3/Minio 的区域类型(可以根据实际需要扩展) -export type S3Zone = +export type S3Zone = | 'us-east-1' | 'us-west-1' | 'us-west-2' @@ -185,6 +185,15 @@ export type EmailConfig = { secure?: boolean; //是否ssl }; +export type PasswordConfig = { + mode?: 'all' | 'plain' | 'sha1' //密码存储模式,默认为all + min?: number; //位数最小值,默认为8 + max?: number; //位数最大值,默认为24 + verify?: boolean; //开启正则校验,默认不开启 + regexs?: string[]; + tip?: string; //登录提示语 +}; + export type QrCodeType = 'wechatMpDomainUrl' | 'wechatMpWxaCode' | 'wechatPublic' | 'wechatPublicForMp' | 'webForWechatPublic'; export type Config = { @@ -236,7 +245,8 @@ export type Config = { type?: 'password', // 采用密码作为第一安全元素 level?: 'weak' | 'medium' | 'strong'; // 强度 passwordVerifyGap?: number; // 在密码验证后的多长时间内认为是安全的,可以做敏感动作 - } + }; + Password?: PasswordConfig; }; export type AccountOrigin = 'ali' | 'tencent' | 'qiniu' | 'amap' | 'ctyun' | 'local' | 's3';