feat: 增加判断oauth登录时是否开启相应applicationPassport
This commit is contained in:
parent
d7d302f329
commit
3f690e6725
|
|
@ -16,6 +16,7 @@ export async function loginByOauth(params, context) {
|
||||||
// 验证 state 并获取 OAuth 配置
|
// 验证 state 并获取 OAuth 配置
|
||||||
const [state] = await context.select("oauthState", {
|
const [state] = await context.select("oauthState", {
|
||||||
data: {
|
data: {
|
||||||
|
providerId: 1,
|
||||||
provider: {
|
provider: {
|
||||||
type: 1,
|
type: 1,
|
||||||
clientId: 1,
|
clientId: 1,
|
||||||
|
|
@ -32,6 +33,31 @@ export async function loginByOauth(params, context) {
|
||||||
state: stateCode,
|
state: stateCode,
|
||||||
},
|
},
|
||||||
}, { dontCollect: true });
|
}, { dontCollect: true });
|
||||||
|
const systemId = context.getSystemId();
|
||||||
|
const [applicationPassport] = await context.select('applicationPassport', {
|
||||||
|
data: {
|
||||||
|
id: 1,
|
||||||
|
applicationId: 1,
|
||||||
|
passportId: 1,
|
||||||
|
passport: {
|
||||||
|
id: 1,
|
||||||
|
type: 1,
|
||||||
|
systemId: 1,
|
||||||
|
config: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
filter: {
|
||||||
|
passport: {
|
||||||
|
systemId,
|
||||||
|
type: 'oauth',
|
||||||
|
},
|
||||||
|
applicationId,
|
||||||
|
}
|
||||||
|
}, { dontCollect: true });
|
||||||
|
const allowOauth = !!(state.providerId && applicationPassport?.passport?.config?.oauthIds && applicationPassport?.passport?.config)?.oauthIds.includes(state.providerId);
|
||||||
|
if (!allowOauth) {
|
||||||
|
throw new OakUserException('error::user.loginWayDisabled');
|
||||||
|
}
|
||||||
assert(state, '无效的 state 参数');
|
assert(state, '无效的 state 参数');
|
||||||
assert(state.provider?.ableState && state.provider?.ableState === 'enabled', '该 OAuth 提供商已被禁用');
|
assert(state.provider?.ableState && state.provider?.ableState === 'enabled', '该 OAuth 提供商已被禁用');
|
||||||
// 如果已经使用
|
// 如果已经使用
|
||||||
|
|
@ -329,7 +355,7 @@ export async function authorize(params, context) {
|
||||||
applicationId: context.getApplicationId(),
|
applicationId: context.getApplicationId(),
|
||||||
userId: context.getCurrentUserId(),
|
userId: context.getCurrentUserId(),
|
||||||
scope: scope === undefined ? [] : [scope],
|
scope: scope === undefined ? [] : [scope],
|
||||||
expiresAt: Date.now() + 10 * 60 * 1000,
|
expiresAt: Date.now() + 10 * 60 * 1000, // 10分钟后过期
|
||||||
// PKCE 支持
|
// PKCE 支持
|
||||||
codeChallenge: code_challenge,
|
codeChallenge: code_challenge,
|
||||||
codeChallengeMethod: code_challenge_method || 'plain',
|
codeChallengeMethod: code_challenge_method || 'plain',
|
||||||
|
|
|
||||||
|
|
@ -21,13 +21,43 @@ export default OakComponent({
|
||||||
state: '',
|
state: '',
|
||||||
},
|
},
|
||||||
lifetimes: {
|
lifetimes: {
|
||||||
ready() {
|
async ready() {
|
||||||
const searchParams = new URLSearchParams(window.location.search);
|
const searchParams = new URLSearchParams(window.location.search);
|
||||||
const clientId = searchParams.get('client_id') || '';
|
const clientId = searchParams.get('client_id') || '';
|
||||||
const responseType = searchParams.get('response_type') || '';
|
const responseType = searchParams.get('response_type') || '';
|
||||||
const redirectUri = searchParams.get('redirect_uri') || '';
|
const redirectUri = searchParams.get('redirect_uri') || '';
|
||||||
const scope = searchParams.get('scope') || '';
|
const scope = searchParams.get('scope') || '';
|
||||||
const state = searchParams.get('state') || '';
|
const state = searchParams.get('state') || '';
|
||||||
|
//判断是否允许oauth登录
|
||||||
|
const application = this.features.application.getApplication();
|
||||||
|
const { result: applicationPassports } = await this.features.cache.exec('getApplicationPassports', { applicationId: application.id });
|
||||||
|
const oauthPassport = applicationPassports?.find((ele) => ele.passport?.type === 'oauth');
|
||||||
|
const oauthIds = oauthPassport?.config?.oauthIds;
|
||||||
|
let allowOauth = false;
|
||||||
|
if (clientId) {
|
||||||
|
const { data: [oauthProvider] } = await this.features.cache.refresh('oauthProvider', {
|
||||||
|
data: {
|
||||||
|
id: 1,
|
||||||
|
clientId: 1,
|
||||||
|
systemId: 1,
|
||||||
|
},
|
||||||
|
filter: {
|
||||||
|
clientId,
|
||||||
|
systemId: application.systemId,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (oauthProvider?.id && oauthIds?.length > 0 && oauthIds.includes(oauthProvider?.id)) {
|
||||||
|
allowOauth = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!allowOauth) {
|
||||||
|
this.setState({
|
||||||
|
hasError: true,
|
||||||
|
errorMsg: 'oauth.login',
|
||||||
|
});
|
||||||
|
this.setState({ loading: false });
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
client_id: clientId,
|
client_id: clientId,
|
||||||
response_type: responseType,
|
response_type: responseType,
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
"missing_client_id": "缺少 client_id 参数",
|
"missing_client_id": "缺少 client_id 参数",
|
||||||
"unknown": "未知错误,请稍后重试"
|
"unknown": "未知错误,请稍后重试"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"login": "当前暂未支持该第三方应用授权登录"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { RowWithActions, WebComponentProps } from 'oak-frontend-base';
|
import { RowWithActions, WebComponentProps } from 'oak-frontend-base';
|
||||||
import { EntityDict } from '../../../../../oak-app-domain';
|
import { EntityDict } from '../../../../../oak-app-domain';
|
||||||
declare const Upsert: (props: WebComponentProps<EntityDict, 'oauthProvider', false, {
|
declare const Upsert: (props: WebComponentProps<EntityDict, "oauthProvider", false, {
|
||||||
item: RowWithActions<EntityDict, 'oauthProvider'>;
|
item: RowWithActions<EntityDict, "oauthProvider">;
|
||||||
}>) => React.JSX.Element;
|
}>) => React.JSX.Element;
|
||||||
export default Upsert;
|
export default Upsert;
|
||||||
|
|
|
||||||
|
|
@ -10,13 +10,13 @@ const Upsert = (props) => {
|
||||||
}
|
}
|
||||||
return (<div className={Styles.id}>
|
return (<div className={Styles.id}>
|
||||||
<Form layout="vertical" autoComplete="off">
|
<Form layout="vertical" autoComplete="off">
|
||||||
<Form.Item label={t('name')} rules={[{ required: true, message: t('nameRequired') }]}>
|
<Form.Item label={t('name')} required={true} rules={[{ required: true, message: t('nameRequired') }]}>
|
||||||
<Input placeholder={t('namePlaceholder')} value={item.name || ""} onChange={(v) => {
|
<Input placeholder={t('namePlaceholder')} value={item.name || ""} onChange={(v) => {
|
||||||
update({ name: v.target.value });
|
update({ name: v.target.value });
|
||||||
}}/>
|
}}/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item label={t('type')} rules={[{ required: true, message: t('typeRequired') }]} extra={item.type && item.type !== 'oak' && item.type !== 'gitea' ? (<Text type="warning">
|
<Form.Item label={t('type')} required={true} rules={[{ required: true, message: t('typeRequired') }]} extra={item.type && item.type !== 'oak' && item.type !== 'gitea' ? (<Text type="warning">
|
||||||
「{item.type}」不是预设类型,请自行注入 handler。
|
「{item.type}」不是预设类型,请自行注入 handler。
|
||||||
</Text>) : undefined}>
|
</Text>) : undefined}>
|
||||||
<Select mode="tags" placeholder={t('typePlaceholder')} value={item.type ? [item.type] : []} // 保持数组形式
|
<Select mode="tags" placeholder={t('typePlaceholder')} value={item.type ? [item.type] : []} // 保持数组形式
|
||||||
|
|
@ -37,13 +37,13 @@ const Upsert = (props) => {
|
||||||
}}/>
|
}}/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item label={t('authorizationEndpoint')} rules={[{ required: true, message: t('authorizationEndpointRequired') }]}>
|
<Form.Item label={t('authorizationEndpoint')} required={true} rules={[{ required: true, message: t('authorizationEndpointRequired') }]}>
|
||||||
<Input placeholder={t('authorizationEndpointPlaceholder')} value={item.authorizationEndpoint || ""} onChange={(v) => {
|
<Input placeholder={t('authorizationEndpointPlaceholder')} value={item.authorizationEndpoint || ""} onChange={(v) => {
|
||||||
update({ authorizationEndpoint: v.target.value });
|
update({ authorizationEndpoint: v.target.value });
|
||||||
}}/>
|
}}/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item label={t('tokenEndpoint')} rules={[{ required: true, message: t('tokenEndpointRequired') }]}>
|
<Form.Item label={t('tokenEndpoint')} required={true} rules={[{ required: true, message: t('tokenEndpointRequired') }]}>
|
||||||
<Input placeholder={t('tokenEndpointPlaceholder')} value={item.tokenEndpoint || ""} onChange={(v) => {
|
<Input placeholder={t('tokenEndpointPlaceholder')} value={item.tokenEndpoint || ""} onChange={(v) => {
|
||||||
update({ tokenEndpoint: v.target.value });
|
update({ tokenEndpoint: v.target.value });
|
||||||
}}/>
|
}}/>
|
||||||
|
|
@ -67,13 +67,13 @@ const Upsert = (props) => {
|
||||||
}}/>
|
}}/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item label={t('clientId')} rules={[{ required: true, message: t('clientIdRequired') }]}>
|
<Form.Item label={t('clientId')} required={true} rules={[{ required: true, message: t('clientIdRequired') }]}>
|
||||||
<Input placeholder={t('clientIdPlaceholder')} value={item.clientId || ""} onChange={(v) => {
|
<Input placeholder={t('clientIdPlaceholder')} value={item.clientId || ""} onChange={(v) => {
|
||||||
update({ clientId: v.target.value });
|
update({ clientId: v.target.value });
|
||||||
}}/>
|
}}/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item label={t('clientSecret')} rules={[{ required: true, message: t('clientSecretRequired') }]}>
|
<Form.Item label={t('clientSecret')} required={true} rules={[{ required: true, message: t('clientSecretRequired') }]}>
|
||||||
<Input.Password placeholder={t('clientSecretPlaceholder')} value={item.clientSecret || ""} onChange={(v) => {
|
<Input.Password placeholder={t('clientSecretPlaceholder')} value={item.clientSecret || ""} onChange={(v) => {
|
||||||
update({ clientSecret: v.target.value });
|
update({ clientSecret: v.target.value });
|
||||||
}}/>
|
}}/>
|
||||||
|
|
@ -85,7 +85,7 @@ const Upsert = (props) => {
|
||||||
}} tokenSeparators={[',']} open={false}/>
|
}} tokenSeparators={[',']} open={false}/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item label={t('redirectUri')} rules={[{ required: true, message: t('redirectUriRequired') }]}>
|
<Form.Item label={t('redirectUri')} required={true} rules={[{ required: true, message: t('redirectUriRequired') }]}>
|
||||||
<Input placeholder={t('redirectUriPlaceholder')} value={item.redirectUri || ""} onChange={(v) => {
|
<Input placeholder={t('redirectUriPlaceholder')} value={item.redirectUri || ""} onChange={(v) => {
|
||||||
update({ redirectUri: v.target.value });
|
update({ redirectUri: v.target.value });
|
||||||
}}/>
|
}}/>
|
||||||
|
|
@ -96,7 +96,7 @@ const Upsert = (props) => {
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item label={t('ableState')} valuePropName="checked">
|
<Form.Item label={t('ableState')} valuePropName="checked">
|
||||||
<Switch checked={!!item.ableState} onChange={(checked) => update({ ableState: checked ? "enabled" : "disabled" })}/>
|
<Switch checked={item.ableState === 'enabled'} onChange={(checked) => update({ ableState: checked ? "enabled" : "disabled" })}/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
{/* <Form.Item>
|
{/* <Form.Item>
|
||||||
|
|
|
||||||
|
|
@ -64,9 +64,11 @@ export default OakComponent({
|
||||||
id: 1,
|
id: 1,
|
||||||
name: 1,
|
name: 1,
|
||||||
systemId: 1,
|
systemId: 1,
|
||||||
|
ableState: 1,
|
||||||
},
|
},
|
||||||
filter: {
|
filter: {
|
||||||
systemId,
|
systemId,
|
||||||
|
ableState: 'enabled'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (oauthProviders && oauthProviders?.length > 0) {
|
if (oauthProviders && oauthProviders?.length > 0) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Switch, Form, Select, Tag, } from 'antd';
|
import { Switch, Form, Select, Tag, Tooltip, } from 'antd';
|
||||||
import Styles from './web.module.less';
|
import Styles from './web.module.less';
|
||||||
export default function Oauth(props) {
|
export default function Oauth(props) {
|
||||||
const { passport, t, changeEnabled, updateConfig, oauthOptions } = props;
|
const { passport, t, changeEnabled, updateConfig, oauthOptions } = props;
|
||||||
|
|
@ -12,15 +12,17 @@ export default function Oauth(props) {
|
||||||
return (<div className={Styles.item}>
|
return (<div className={Styles.item}>
|
||||||
<div className={Styles.title}>
|
<div className={Styles.title}>
|
||||||
<Tag color={stateColor}>{t(`passport:v.type.${type}`)}</Tag>
|
<Tag color={stateColor}>{t(`passport:v.type.${type}`)}</Tag>
|
||||||
|
<Tooltip title={(oauthOptions && oauthOptions?.length > 0) ? '' : '请先启用oauth供应商'}>
|
||||||
<Switch checkedChildren="开启" unCheckedChildren="关闭" checked={enabled} onChange={(checked) => {
|
<Switch checkedChildren="开启" unCheckedChildren="关闭" checked={enabled} onChange={(checked) => {
|
||||||
changeEnabled(checked);
|
changeEnabled(checked);
|
||||||
}}/>
|
}} disabled={!(oauthOptions && oauthOptions?.length > 0)}/>
|
||||||
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
{enabled &&
|
{enabled &&
|
||||||
<div>
|
<div>
|
||||||
<Form labelCol={{ span: 4 }} wrapperCol={{ span: 20 }} style={{ maxWidth: 900, marginTop: 16 }}>
|
<Form labelCol={{ span: 4 }} wrapperCol={{ span: 20 }} style={{ maxWidth: 900, marginTop: 16 }}>
|
||||||
<Form.Item label='oauth提供商'>
|
<Form.Item label='oauth供应商'>
|
||||||
<Select mode="multiple" style={{ width: '100%' }} placeholder="请选择oauth提供商" value={oauthIds} onChange={(value) => {
|
<Select mode="multiple" style={{ width: '100%' }} placeholder="请选择oauth供应商" value={oauthIds} onChange={(value) => {
|
||||||
updateConfig(id, config, 'oauthIds', value, 'oauth');
|
updateConfig(id, config, 'oauthIds', value, 'oauth');
|
||||||
}} options={oauthOptions}/>
|
}} options={oauthOptions}/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,7 @@ export default function render(props) {
|
||||||
<div>* 如需启用邮箱登录,请先前往配置管理邮箱设置,创建系统邮箱,并完成相关配置</div>
|
<div>* 如需启用邮箱登录,请先前往配置管理邮箱设置,创建系统邮箱,并完成相关配置</div>
|
||||||
<div>* 如需启用小程序授权登录,请先前往应用管理,创建小程序application,并完成基础配置</div>
|
<div>* 如需启用小程序授权登录,请先前往应用管理,创建小程序application,并完成基础配置</div>
|
||||||
<div>* 如需启用公众号授权登录,请先前往应用管理,创建是服务号的公众号application,并完成基础配置</div>
|
<div>* 如需启用公众号授权登录,请先前往应用管理,创建是服务号的公众号application,并完成基础配置</div>
|
||||||
|
<div>* 如需启用OAuth授权登录,请先前往OAuth管理,创建OAuth供应商,并启用</div>
|
||||||
</div>
|
</div>
|
||||||
</Row>
|
</Row>
|
||||||
{passports && passports.map((passport) => {
|
{passports && passports.map((passport) => {
|
||||||
|
|
|
||||||
|
|
@ -266,7 +266,8 @@ const i18ns = [
|
||||||
"missing_client_id": "缺少 client_id 参数",
|
"missing_client_id": "缺少 client_id 参数",
|
||||||
"unknown": "未知错误,请稍后重试"
|
"unknown": "未知错误,请稍后重试"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"login": "当前暂未支持该第三方应用授权登录"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -140,5 +140,68 @@ const triggers = [
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: '当provider禁用时,更新passport',
|
||||||
|
entity: 'oauthProvider',
|
||||||
|
action: 'update',
|
||||||
|
when: 'after',
|
||||||
|
check: (operation) => {
|
||||||
|
const { data } = operation;
|
||||||
|
return data.hasOwnProperty('ableState') && data.ableState === 'disabled';
|
||||||
|
},
|
||||||
|
fn: async ({ operation }, context, option) => {
|
||||||
|
const { filter } = operation;
|
||||||
|
const [oauthProvider] = await context.select('oauthProvider', {
|
||||||
|
data: {
|
||||||
|
id: 1,
|
||||||
|
systemId: 1,
|
||||||
|
ableState: 1,
|
||||||
|
},
|
||||||
|
filter: filter,
|
||||||
|
}, { forUpdate: true });
|
||||||
|
assert(oauthProvider, '禁用oauthProvider的filter请勿包含abledState');
|
||||||
|
let count = 0;
|
||||||
|
const { id, systemId } = oauthProvider;
|
||||||
|
const [passport] = await context.select('passport', {
|
||||||
|
data: {
|
||||||
|
id: 1,
|
||||||
|
type: 1,
|
||||||
|
config: 1,
|
||||||
|
systemId: 1,
|
||||||
|
enabled: 1,
|
||||||
|
},
|
||||||
|
filter: {
|
||||||
|
type: 'oauth',
|
||||||
|
systemId,
|
||||||
|
config: {
|
||||||
|
oauthIds: {
|
||||||
|
$contains: [id],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, { forUpdate: true });
|
||||||
|
if (passport && passport.enabled) {
|
||||||
|
const { id: passportId, config } = passport;
|
||||||
|
let newConfig = cloneDeep(config);
|
||||||
|
pull(newConfig?.oauthIds, id);
|
||||||
|
if (newConfig?.oauthIds?.length <= 0) {
|
||||||
|
//无可支持的oauthProvider,将启用了的passport关闭
|
||||||
|
await context.operate('passport', {
|
||||||
|
id: await generateNewIdAsync(),
|
||||||
|
action: 'update',
|
||||||
|
data: {
|
||||||
|
enabled: false,
|
||||||
|
config: newConfig,
|
||||||
|
},
|
||||||
|
filter: {
|
||||||
|
id: passport.id,
|
||||||
|
}
|
||||||
|
}, option);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
||||||
];
|
];
|
||||||
export default triggers;
|
export default triggers;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.authorize = exports.createOAuthState = exports.getOAuthClientInfo = exports.loginByOauth = void 0;
|
exports.loginByOauth = loginByOauth;
|
||||||
|
exports.getOAuthClientInfo = getOAuthClientInfo;
|
||||||
|
exports.createOAuthState = createOAuthState;
|
||||||
|
exports.authorize = authorize;
|
||||||
const tslib_1 = require("tslib");
|
const tslib_1 = require("tslib");
|
||||||
const assert_1 = tslib_1.__importDefault(require("assert"));
|
const assert_1 = tslib_1.__importDefault(require("assert"));
|
||||||
const types_1 = require("oak-domain/lib/types");
|
const types_1 = require("oak-domain/lib/types");
|
||||||
|
|
@ -20,6 +23,7 @@ async function loginByOauth(params, context) {
|
||||||
// 验证 state 并获取 OAuth 配置
|
// 验证 state 并获取 OAuth 配置
|
||||||
const [state] = await context.select("oauthState", {
|
const [state] = await context.select("oauthState", {
|
||||||
data: {
|
data: {
|
||||||
|
providerId: 1,
|
||||||
provider: {
|
provider: {
|
||||||
type: 1,
|
type: 1,
|
||||||
clientId: 1,
|
clientId: 1,
|
||||||
|
|
@ -36,6 +40,31 @@ async function loginByOauth(params, context) {
|
||||||
state: stateCode,
|
state: stateCode,
|
||||||
},
|
},
|
||||||
}, { dontCollect: true });
|
}, { dontCollect: true });
|
||||||
|
const systemId = context.getSystemId();
|
||||||
|
const [applicationPassport] = await context.select('applicationPassport', {
|
||||||
|
data: {
|
||||||
|
id: 1,
|
||||||
|
applicationId: 1,
|
||||||
|
passportId: 1,
|
||||||
|
passport: {
|
||||||
|
id: 1,
|
||||||
|
type: 1,
|
||||||
|
systemId: 1,
|
||||||
|
config: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
filter: {
|
||||||
|
passport: {
|
||||||
|
systemId,
|
||||||
|
type: 'oauth',
|
||||||
|
},
|
||||||
|
applicationId,
|
||||||
|
}
|
||||||
|
}, { dontCollect: true });
|
||||||
|
const allowOauth = !!(state.providerId && applicationPassport?.passport?.config?.oauthIds && applicationPassport?.passport?.config)?.oauthIds.includes(state.providerId);
|
||||||
|
if (!allowOauth) {
|
||||||
|
throw new types_1.OakUserException('error::user.loginWayDisabled');
|
||||||
|
}
|
||||||
(0, assert_1.default)(state, '无效的 state 参数');
|
(0, assert_1.default)(state, '无效的 state 参数');
|
||||||
(0, assert_1.default)(state.provider?.ableState && state.provider?.ableState === 'enabled', '该 OAuth 提供商已被禁用');
|
(0, assert_1.default)(state.provider?.ableState && state.provider?.ableState === 'enabled', '该 OAuth 提供商已被禁用');
|
||||||
// 如果已经使用
|
// 如果已经使用
|
||||||
|
|
@ -184,7 +213,6 @@ async function loginByOauth(params, context) {
|
||||||
return tokenValue;
|
return tokenValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.loginByOauth = loginByOauth;
|
|
||||||
async function getOAuthClientInfo(params, context) {
|
async function getOAuthClientInfo(params, context) {
|
||||||
const { client_id, currentUserId } = params;
|
const { client_id, currentUserId } = params;
|
||||||
const closeRootMode = context.openRootMode();
|
const closeRootMode = context.openRootMode();
|
||||||
|
|
@ -247,7 +275,6 @@ async function getOAuthClientInfo(params, context) {
|
||||||
alreadyAuth: !!hasAuth,
|
alreadyAuth: !!hasAuth,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
exports.getOAuthClientInfo = getOAuthClientInfo;
|
|
||||||
async function createOAuthState(params, context) {
|
async function createOAuthState(params, context) {
|
||||||
const { providerId, userId, type } = params;
|
const { providerId, userId, type } = params;
|
||||||
const closeRootMode = context.openRootMode();
|
const closeRootMode = context.openRootMode();
|
||||||
|
|
@ -269,7 +296,6 @@ async function createOAuthState(params, context) {
|
||||||
closeRootMode();
|
closeRootMode();
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
exports.createOAuthState = createOAuthState;
|
|
||||||
async function authorize(params, context) {
|
async function authorize(params, context) {
|
||||||
const { response_type, client_id, redirect_uri, scope, state, action, code_challenge, code_challenge_method } = params;
|
const { response_type, client_id, redirect_uri, scope, state, action, code_challenge, code_challenge_method } = params;
|
||||||
if (response_type !== 'code') {
|
if (response_type !== 'code') {
|
||||||
|
|
@ -336,7 +362,7 @@ async function authorize(params, context) {
|
||||||
applicationId: context.getApplicationId(),
|
applicationId: context.getApplicationId(),
|
||||||
userId: context.getCurrentUserId(),
|
userId: context.getCurrentUserId(),
|
||||||
scope: scope === undefined ? [] : [scope],
|
scope: scope === undefined ? [] : [scope],
|
||||||
expiresAt: Date.now() + 10 * 60 * 1000,
|
expiresAt: Date.now() + 10 * 60 * 1000, // 10分钟后过期
|
||||||
// PKCE 支持
|
// PKCE 支持
|
||||||
codeChallenge: code_challenge,
|
codeChallenge: code_challenge,
|
||||||
codeChallengeMethod: code_challenge_method || 'plain',
|
codeChallengeMethod: code_challenge_method || 'plain',
|
||||||
|
|
@ -366,7 +392,6 @@ async function authorize(params, context) {
|
||||||
closeRootMode();
|
closeRootMode();
|
||||||
throw new Error('unknown action');
|
throw new Error('unknown action');
|
||||||
}
|
}
|
||||||
exports.authorize = authorize;
|
|
||||||
const fetchOAuthUserInfo = async (code, providerConfig) => {
|
const fetchOAuthUserInfo = async (code, providerConfig) => {
|
||||||
// 1. 使用 code 换取 access_token
|
// 1. 使用 code 换取 access_token
|
||||||
const tokenResponse = await fetch(providerConfig.tokenEndpoint, {
|
const tokenResponse = await fetch(providerConfig.tokenEndpoint, {
|
||||||
|
|
|
||||||
|
|
@ -268,7 +268,8 @@ const i18ns = [
|
||||||
"missing_client_id": "缺少 client_id 参数",
|
"missing_client_id": "缺少 client_id 参数",
|
||||||
"unknown": "未知错误,请稍后重试"
|
"unknown": "未知错误,请稍后重试"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"login": "当前暂未支持该第三方应用授权登录"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -143,5 +143,68 @@ const triggers = [
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: '当provider禁用时,更新passport',
|
||||||
|
entity: 'oauthProvider',
|
||||||
|
action: 'update',
|
||||||
|
when: 'after',
|
||||||
|
check: (operation) => {
|
||||||
|
const { data } = operation;
|
||||||
|
return data.hasOwnProperty('ableState') && data.ableState === 'disabled';
|
||||||
|
},
|
||||||
|
fn: async ({ operation }, context, option) => {
|
||||||
|
const { filter } = operation;
|
||||||
|
const [oauthProvider] = await context.select('oauthProvider', {
|
||||||
|
data: {
|
||||||
|
id: 1,
|
||||||
|
systemId: 1,
|
||||||
|
ableState: 1,
|
||||||
|
},
|
||||||
|
filter: filter,
|
||||||
|
}, { forUpdate: true });
|
||||||
|
(0, assert_1.default)(oauthProvider, '禁用oauthProvider的filter请勿包含abledState');
|
||||||
|
let count = 0;
|
||||||
|
const { id, systemId } = oauthProvider;
|
||||||
|
const [passport] = await context.select('passport', {
|
||||||
|
data: {
|
||||||
|
id: 1,
|
||||||
|
type: 1,
|
||||||
|
config: 1,
|
||||||
|
systemId: 1,
|
||||||
|
enabled: 1,
|
||||||
|
},
|
||||||
|
filter: {
|
||||||
|
type: 'oauth',
|
||||||
|
systemId,
|
||||||
|
config: {
|
||||||
|
oauthIds: {
|
||||||
|
$contains: [id],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, { forUpdate: true });
|
||||||
|
if (passport && passport.enabled) {
|
||||||
|
const { id: passportId, config } = passport;
|
||||||
|
let newConfig = (0, lodash_1.cloneDeep)(config);
|
||||||
|
(0, lodash_1.pull)(newConfig?.oauthIds, id);
|
||||||
|
if (newConfig?.oauthIds?.length <= 0) {
|
||||||
|
//无可支持的oauthProvider,将启用了的passport关闭
|
||||||
|
await context.operate('passport', {
|
||||||
|
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||||
|
action: 'update',
|
||||||
|
data: {
|
||||||
|
enabled: false,
|
||||||
|
config: newConfig,
|
||||||
|
},
|
||||||
|
filter: {
|
||||||
|
id: passport.id,
|
||||||
|
}
|
||||||
|
}, option);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
||||||
];
|
];
|
||||||
exports.default = triggers;
|
exports.default = triggers;
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import { generateNewIdAsync } from "oak-domain/lib/utils/uuid";
|
||||||
import { loadTokenInfo, setUpTokenAndUser } from "./token";
|
import { loadTokenInfo, setUpTokenAndUser } from "./token";
|
||||||
import { randomUUID } from "crypto";
|
import { randomUUID } from "crypto";
|
||||||
import { processUserInfo } from "../utils/oauth";
|
import { processUserInfo } from "../utils/oauth";
|
||||||
|
import { OAuthConfig } from "../entities/Passport";
|
||||||
|
|
||||||
export async function loginByOauth<ED extends EntityDict>(params: {
|
export async function loginByOauth<ED extends EntityDict>(params: {
|
||||||
code: string;
|
code: string;
|
||||||
|
|
@ -26,6 +27,7 @@ export async function loginByOauth<ED extends EntityDict>(params: {
|
||||||
// 验证 state 并获取 OAuth 配置
|
// 验证 state 并获取 OAuth 配置
|
||||||
const [state] = await context.select("oauthState", {
|
const [state] = await context.select("oauthState", {
|
||||||
data: {
|
data: {
|
||||||
|
providerId: 1,
|
||||||
provider: {
|
provider: {
|
||||||
type: 1,
|
type: 1,
|
||||||
clientId: 1,
|
clientId: 1,
|
||||||
|
|
@ -41,7 +43,33 @@ export async function loginByOauth<ED extends EntityDict>(params: {
|
||||||
filter: {
|
filter: {
|
||||||
state: stateCode,
|
state: stateCode,
|
||||||
},
|
},
|
||||||
}, { dontCollect: true })
|
}, { dontCollect: true });
|
||||||
|
|
||||||
|
const systemId = context.getSystemId();
|
||||||
|
const [applicationPassport] = await context.select('applicationPassport', {
|
||||||
|
data: {
|
||||||
|
id: 1,
|
||||||
|
applicationId: 1,
|
||||||
|
passportId: 1,
|
||||||
|
passport: {
|
||||||
|
id: 1,
|
||||||
|
type: 1,
|
||||||
|
systemId: 1,
|
||||||
|
config: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
filter: {
|
||||||
|
passport: {
|
||||||
|
systemId,
|
||||||
|
type: 'oauth',
|
||||||
|
},
|
||||||
|
applicationId,
|
||||||
|
}
|
||||||
|
}, { dontCollect: true });
|
||||||
|
const allowOauth = !!(state.providerId && (applicationPassport?.passport?.config as OAuthConfig)?.oauthIds && applicationPassport?.passport?.config as OAuthConfig)?.oauthIds.includes(state.providerId);
|
||||||
|
if (!allowOauth) {
|
||||||
|
throw new OakUserException('error::user.loginWayDisabled');
|
||||||
|
}
|
||||||
|
|
||||||
assert(state, '无效的 state 参数');
|
assert(state, '无效的 state 参数');
|
||||||
assert(state.provider?.ableState && state.provider?.ableState === 'enabled', '该 OAuth 提供商已被禁用');
|
assert(state.provider?.ableState && state.provider?.ableState === 'enabled', '该 OAuth 提供商已被禁用');
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ export default OakComponent({
|
||||||
state: '',
|
state: '',
|
||||||
},
|
},
|
||||||
lifetimes: {
|
lifetimes: {
|
||||||
ready() {
|
async ready() {
|
||||||
const searchParams = new URLSearchParams(window.location.search);
|
const searchParams = new URLSearchParams(window.location.search);
|
||||||
const clientId = searchParams.get('client_id') || '';
|
const clientId = searchParams.get('client_id') || '';
|
||||||
const responseType = searchParams.get('response_type') || '';
|
const responseType = searchParams.get('response_type') || '';
|
||||||
|
|
@ -32,6 +32,38 @@ export default OakComponent({
|
||||||
const scope = searchParams.get('scope') || '';
|
const scope = searchParams.get('scope') || '';
|
||||||
const state = searchParams.get('state') || '';
|
const state = searchParams.get('state') || '';
|
||||||
|
|
||||||
|
//判断是否允许oauth登录
|
||||||
|
const application = this.features.application.getApplication();
|
||||||
|
const { result: applicationPassports } = await this.features.cache.exec('getApplicationPassports', { applicationId: application.id });
|
||||||
|
const oauthPassport = applicationPassports?.find((ele: EntityDict['applicationPassport']['Schema']) => ele.passport?.type === 'oauth');
|
||||||
|
const oauthIds = oauthPassport?.config?.oauthIds;
|
||||||
|
let allowOauth = false;
|
||||||
|
if (clientId) {
|
||||||
|
const { data: [oauthProvider] } = await this.features.cache.refresh('oauthProvider', {
|
||||||
|
data: {
|
||||||
|
id: 1,
|
||||||
|
clientId: 1,
|
||||||
|
systemId: 1,
|
||||||
|
},
|
||||||
|
filter: {
|
||||||
|
clientId,
|
||||||
|
systemId: application.systemId,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (oauthProvider?.id && oauthIds?.length > 0 && oauthIds.includes(oauthProvider?.id)) {
|
||||||
|
allowOauth = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!allowOauth) {
|
||||||
|
this.setState({
|
||||||
|
hasError: true,
|
||||||
|
errorMsg: 'oauth.login',
|
||||||
|
});
|
||||||
|
|
||||||
|
this.setState({ loading: false });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
client_id: clientId,
|
client_id: clientId,
|
||||||
response_type: responseType,
|
response_type: responseType,
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
"missing_client_id": "缺少 client_id 参数",
|
"missing_client_id": "缺少 client_id 参数",
|
||||||
"unknown": "未知错误,请稍后重试"
|
"unknown": "未知错误,请稍后重试"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"login": "当前暂未支持该第三方应用授权登录"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -31,6 +31,7 @@ const Upsert = (
|
||||||
>
|
>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t('name')}
|
label={t('name')}
|
||||||
|
required={true}
|
||||||
rules={[{ required: true, message: t('nameRequired') }]}
|
rules={[{ required: true, message: t('nameRequired') }]}
|
||||||
>
|
>
|
||||||
<Input placeholder={t('namePlaceholder')} value={item.name || ""}
|
<Input placeholder={t('namePlaceholder')} value={item.name || ""}
|
||||||
|
|
@ -42,6 +43,7 @@ const Upsert = (
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t('type')}
|
label={t('type')}
|
||||||
|
required={true}
|
||||||
rules={[{ required: true, message: t('typeRequired') }]}
|
rules={[{ required: true, message: t('typeRequired') }]}
|
||||||
extra={
|
extra={
|
||||||
item.type && item.type !== 'oak' && item.type !== 'gitea' ? (
|
item.type && item.type !== 'oak' && item.type !== 'gitea' ? (
|
||||||
|
|
@ -87,6 +89,7 @@ const Upsert = (
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t('authorizationEndpoint')}
|
label={t('authorizationEndpoint')}
|
||||||
|
required={true}
|
||||||
rules={[{ required: true, message: t('authorizationEndpointRequired') }]}
|
rules={[{ required: true, message: t('authorizationEndpointRequired') }]}
|
||||||
>
|
>
|
||||||
<Input placeholder={t('authorizationEndpointPlaceholder')} value={item.authorizationEndpoint || ""}
|
<Input placeholder={t('authorizationEndpointPlaceholder')} value={item.authorizationEndpoint || ""}
|
||||||
|
|
@ -98,6 +101,7 @@ const Upsert = (
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t('tokenEndpoint')}
|
label={t('tokenEndpoint')}
|
||||||
|
required={true}
|
||||||
rules={[{ required: true, message: t('tokenEndpointRequired') }]}
|
rules={[{ required: true, message: t('tokenEndpointRequired') }]}
|
||||||
>
|
>
|
||||||
<Input placeholder={t('tokenEndpointPlaceholder')} value={item.tokenEndpoint || ""}
|
<Input placeholder={t('tokenEndpointPlaceholder')} value={item.tokenEndpoint || ""}
|
||||||
|
|
@ -139,6 +143,7 @@ const Upsert = (
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t('clientId')}
|
label={t('clientId')}
|
||||||
|
required={true}
|
||||||
rules={[{ required: true, message: t('clientIdRequired') }]}
|
rules={[{ required: true, message: t('clientIdRequired') }]}
|
||||||
>
|
>
|
||||||
<Input placeholder={t('clientIdPlaceholder')} value={item.clientId || ""}
|
<Input placeholder={t('clientIdPlaceholder')} value={item.clientId || ""}
|
||||||
|
|
@ -150,6 +155,7 @@ const Upsert = (
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t('clientSecret')}
|
label={t('clientSecret')}
|
||||||
|
required={true}
|
||||||
rules={[{ required: true, message: t('clientSecretRequired') }]}
|
rules={[{ required: true, message: t('clientSecretRequired') }]}
|
||||||
>
|
>
|
||||||
<Input.Password placeholder={t('clientSecretPlaceholder')} value={item.clientSecret || ""}
|
<Input.Password placeholder={t('clientSecretPlaceholder')} value={item.clientSecret || ""}
|
||||||
|
|
@ -176,6 +182,7 @@ const Upsert = (
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t('redirectUri')}
|
label={t('redirectUri')}
|
||||||
|
required={true}
|
||||||
rules={[{ required: true, message: t('redirectUriRequired') }]}
|
rules={[{ required: true, message: t('redirectUriRequired') }]}
|
||||||
>
|
>
|
||||||
<Input placeholder={t('redirectUriPlaceholder')} value={item.redirectUri || ""}
|
<Input placeholder={t('redirectUriPlaceholder')} value={item.redirectUri || ""}
|
||||||
|
|
@ -196,7 +203,7 @@ const Upsert = (
|
||||||
label={t('ableState')}
|
label={t('ableState')}
|
||||||
valuePropName="checked"
|
valuePropName="checked"
|
||||||
>
|
>
|
||||||
<Switch checked={!!item.ableState} onChange={(checked) => update({ ableState: checked ? "enabled" : "disabled" })} />
|
<Switch checked={item.ableState === 'enabled'} onChange={(checked) => update({ ableState: checked ? "enabled" : "disabled" })} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
{/* <Form.Item>
|
{/* <Form.Item>
|
||||||
|
|
|
||||||
|
|
@ -69,9 +69,11 @@ export default OakComponent({
|
||||||
id: 1,
|
id: 1,
|
||||||
name: 1,
|
name: 1,
|
||||||
systemId: 1,
|
systemId: 1,
|
||||||
|
ableState: 1,
|
||||||
},
|
},
|
||||||
filter: {
|
filter: {
|
||||||
systemId,
|
systemId,
|
||||||
|
ableState: 'enabled'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (oauthProviders && oauthProviders?.length > 0) {
|
if (oauthProviders && oauthProviders?.length > 0) {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { EmailConfig, MfwConfig, PfwConfig, SmsConfig, PwdConfig, NameConfig, OAuthConfig } from "../../../entities/Passport";
|
import { EmailConfig, MfwConfig, PfwConfig, SmsConfig, PwdConfig, NameConfig, OAuthConfig } from "../../../entities/Passport";
|
||||||
import { EntityDict } from "../../../oak-app-domain";
|
import { EntityDict } from "../../../oak-app-domain";
|
||||||
import { Switch, Form, Input, Select, Space, Tag, InputNumber, Radio, } from 'antd';
|
import { Switch, Form, Select, Tag, Tooltip, } from 'antd';
|
||||||
import Styles from './web.module.less';
|
import Styles from './web.module.less';
|
||||||
|
|
||||||
export default function Oauth(props: {
|
export default function Oauth(props: {
|
||||||
|
|
@ -24,6 +24,7 @@ export default function Oauth(props: {
|
||||||
<div className={Styles.item}>
|
<div className={Styles.item}>
|
||||||
<div className={Styles.title}>
|
<div className={Styles.title}>
|
||||||
<Tag color={stateColor}>{t(`passport:v.type.${type}`)}</Tag>
|
<Tag color={stateColor}>{t(`passport:v.type.${type}`)}</Tag>
|
||||||
|
<Tooltip title={(oauthOptions && oauthOptions?.length > 0) ? '' : '请先启用oauth供应商'}>
|
||||||
<Switch
|
<Switch
|
||||||
checkedChildren="开启"
|
checkedChildren="开启"
|
||||||
unCheckedChildren="关闭"
|
unCheckedChildren="关闭"
|
||||||
|
|
@ -31,7 +32,9 @@ export default function Oauth(props: {
|
||||||
onChange={(checked) => {
|
onChange={(checked) => {
|
||||||
changeEnabled(checked)
|
changeEnabled(checked)
|
||||||
}}
|
}}
|
||||||
|
disabled={!(oauthOptions && oauthOptions?.length > 0)}
|
||||||
/>
|
/>
|
||||||
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
{enabled &&
|
{enabled &&
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -41,12 +44,12 @@ export default function Oauth(props: {
|
||||||
style={{ maxWidth: 900, marginTop: 16 }}
|
style={{ maxWidth: 900, marginTop: 16 }}
|
||||||
>
|
>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label='oauth提供商'
|
label='oauth供应商'
|
||||||
>
|
>
|
||||||
<Select
|
<Select
|
||||||
mode="multiple"
|
mode="multiple"
|
||||||
style={{ width: '100%' }}
|
style={{ width: '100%' }}
|
||||||
placeholder="请选择oauth提供商"
|
placeholder="请选择oauth供应商"
|
||||||
value={oauthIds}
|
value={oauthIds}
|
||||||
onChange={(value: string[]) => {
|
onChange={(value: string[]) => {
|
||||||
updateConfig(id, config!, 'oauthIds', value, 'oauth');
|
updateConfig(id, config!, 'oauthIds', value, 'oauth');
|
||||||
|
|
|
||||||
|
|
@ -159,6 +159,7 @@ export default function render(props: WebComponentProps<
|
||||||
<div>* 如需启用邮箱登录,请先前往配置管理邮箱设置,创建系统邮箱,并完成相关配置</div>
|
<div>* 如需启用邮箱登录,请先前往配置管理邮箱设置,创建系统邮箱,并完成相关配置</div>
|
||||||
<div>* 如需启用小程序授权登录,请先前往应用管理,创建小程序application,并完成基础配置</div>
|
<div>* 如需启用小程序授权登录,请先前往应用管理,创建小程序application,并完成基础配置</div>
|
||||||
<div>* 如需启用公众号授权登录,请先前往应用管理,创建是服务号的公众号application,并完成基础配置</div>
|
<div>* 如需启用公众号授权登录,请先前往应用管理,创建是服务号的公众号application,并完成基础配置</div>
|
||||||
|
<div>* 如需启用OAuth授权登录,请先前往OAuth管理,创建OAuth供应商,并启用</div>
|
||||||
</div>
|
</div>
|
||||||
</Row>
|
</Row>
|
||||||
{passports && passports.map((passport) => {
|
{passports && passports.map((passport) => {
|
||||||
|
|
|
||||||
|
|
@ -268,7 +268,8 @@ const i18ns: I18n[] = [
|
||||||
"missing_client_id": "缺少 client_id 参数",
|
"missing_client_id": "缺少 client_id 参数",
|
||||||
"unknown": "未知错误,请稍后重试"
|
"unknown": "未知错误,请稍后重试"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"login": "当前暂未支持该第三方应用授权登录"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -147,6 +147,69 @@ const triggers: Trigger<EntityDict, "oauthProvider", BRC<EntityDict>>[] = [
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
} as RemoveTrigger<EntityDict, "oauthProvider", BRC<EntityDict>>,
|
} as RemoveTrigger<EntityDict, "oauthProvider", BRC<EntityDict>>,
|
||||||
|
{
|
||||||
|
name: '当provider禁用时,更新passport',
|
||||||
|
entity: 'oauthProvider',
|
||||||
|
action: 'update',
|
||||||
|
when: 'after',
|
||||||
|
check: (operation) => {
|
||||||
|
const { data } = operation as EntityDict['oauthProvider']['Update'];
|
||||||
|
return data.hasOwnProperty('ableState') && data.ableState === 'disabled';
|
||||||
|
},
|
||||||
|
fn: async ({ operation }, context, option) => {
|
||||||
|
const { filter } = operation;
|
||||||
|
const [oauthProvider] = await context.select('oauthProvider', {
|
||||||
|
data: {
|
||||||
|
id: 1,
|
||||||
|
systemId: 1,
|
||||||
|
ableState: 1,
|
||||||
|
},
|
||||||
|
filter: filter,
|
||||||
|
}, { forUpdate: true });
|
||||||
|
assert(oauthProvider, '禁用oauthProvider的filter请勿包含abledState');
|
||||||
|
let count = 0;
|
||||||
|
const { id, systemId } = oauthProvider;
|
||||||
|
const [passport] = await context.select('passport', {
|
||||||
|
data: {
|
||||||
|
id: 1,
|
||||||
|
type: 1,
|
||||||
|
config: 1,
|
||||||
|
systemId: 1,
|
||||||
|
enabled: 1,
|
||||||
|
},
|
||||||
|
filter: {
|
||||||
|
type: 'oauth',
|
||||||
|
systemId,
|
||||||
|
config: {
|
||||||
|
oauthIds: {
|
||||||
|
$contains: [id!],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, { forUpdate: true })
|
||||||
|
if (passport && passport.enabled) {
|
||||||
|
const { id: passportId, config } = passport;
|
||||||
|
let newConfig = cloneDeep(config) as OAuthConfig;
|
||||||
|
pull(newConfig?.oauthIds, id);
|
||||||
|
if (newConfig?.oauthIds?.length <= 0) {
|
||||||
|
//无可支持的oauthProvider,将启用了的passport关闭
|
||||||
|
await context.operate('passport', {
|
||||||
|
id: await generateNewIdAsync(),
|
||||||
|
action: 'update',
|
||||||
|
data: {
|
||||||
|
enabled: false,
|
||||||
|
config: newConfig,
|
||||||
|
},
|
||||||
|
filter: {
|
||||||
|
id: passport.id,
|
||||||
|
}
|
||||||
|
}, option);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
export default triggers;
|
export default triggers;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue