feat: 增加判断oauth登录时是否开启相应applicationPassport

This commit is contained in:
lxy 2025-11-14 10:28:01 +08:00
parent d7d302f329
commit 3f690e6725
22 changed files with 396 additions and 43 deletions

View File

@ -16,6 +16,7 @@ export async function loginByOauth(params, context) {
// 验证 state 并获取 OAuth 配置
const [state] = await context.select("oauthState", {
data: {
providerId: 1,
provider: {
type: 1,
clientId: 1,
@ -32,6 +33,31 @@ export async function loginByOauth(params, context) {
state: stateCode,
},
}, { 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.provider?.ableState && state.provider?.ableState === 'enabled', '该 OAuth 提供商已被禁用');
// 如果已经使用
@ -329,7 +355,7 @@ export async function authorize(params, context) {
applicationId: context.getApplicationId(),
userId: context.getCurrentUserId(),
scope: scope === undefined ? [] : [scope],
expiresAt: Date.now() + 10 * 60 * 1000,
expiresAt: Date.now() + 10 * 60 * 1000, // 10分钟后过期
// PKCE 支持
codeChallenge: code_challenge,
codeChallengeMethod: code_challenge_method || 'plain',

View File

@ -21,13 +21,43 @@ export default OakComponent({
state: '',
},
lifetimes: {
ready() {
async ready() {
const searchParams = new URLSearchParams(window.location.search);
const clientId = searchParams.get('client_id') || '';
const responseType = searchParams.get('response_type') || '';
const redirectUri = searchParams.get('redirect_uri') || '';
const scope = searchParams.get('scope') || '';
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({
client_id: clientId,
response_type: responseType,

View File

@ -16,6 +16,7 @@
"missing_client_id": "缺少 client_id 参数",
"unknown": "未知错误,请稍后重试"
}
}
},
"login": "当前暂未支持该第三方应用授权登录"
}
}

View File

@ -1,7 +1,7 @@
import React from 'react';
import { RowWithActions, WebComponentProps } from 'oak-frontend-base';
import { EntityDict } from '../../../../../oak-app-domain';
declare const Upsert: (props: WebComponentProps<EntityDict, 'oauthProvider', false, {
item: RowWithActions<EntityDict, 'oauthProvider'>;
declare const Upsert: (props: WebComponentProps<EntityDict, "oauthProvider", false, {
item: RowWithActions<EntityDict, "oauthProvider">;
}>) => React.JSX.Element;
export default Upsert;

View File

@ -10,13 +10,13 @@ const Upsert = (props) => {
}
return (<div className={Styles.id}>
<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) => {
update({ name: v.target.value });
}}/>
</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
</Text>) : undefined}>
<Select mode="tags" placeholder={t('typePlaceholder')} value={item.type ? [item.type] : []} // 保持数组形式
@ -37,13 +37,13 @@ const Upsert = (props) => {
}}/>
</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) => {
update({ authorizationEndpoint: v.target.value });
}}/>
</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) => {
update({ tokenEndpoint: v.target.value });
}}/>
@ -67,13 +67,13 @@ const Upsert = (props) => {
}}/>
</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) => {
update({ clientId: v.target.value });
}}/>
</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) => {
update({ clientSecret: v.target.value });
}}/>
@ -85,7 +85,7 @@ const Upsert = (props) => {
}} tokenSeparators={[',']} open={false}/>
</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) => {
update({ redirectUri: v.target.value });
}}/>
@ -96,7 +96,7 @@ const Upsert = (props) => {
</Form.Item>
<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>

View File

@ -64,9 +64,11 @@ export default OakComponent({
id: 1,
name: 1,
systemId: 1,
ableState: 1,
},
filter: {
systemId,
ableState: 'enabled'
}
});
if (oauthProviders && oauthProviders?.length > 0) {

View File

@ -1,5 +1,5 @@
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';
export default function Oauth(props) {
const { passport, t, changeEnabled, updateConfig, oauthOptions } = props;
@ -12,15 +12,17 @@ export default function Oauth(props) {
return (<div className={Styles.item}>
<div className={Styles.title}>
<Tag color={stateColor}>{t(`passport:v.type.${type}`)}</Tag>
<Tooltip title={(oauthOptions && oauthOptions?.length > 0) ? '' : '请先启用oauth供应商'}>
<Switch checkedChildren="开启" unCheckedChildren="关闭" checked={enabled} onChange={(checked) => {
changeEnabled(checked);
}}/>
}} disabled={!(oauthOptions && oauthOptions?.length > 0)}/>
</Tooltip>
</div>
{enabled &&
<div>
<Form labelCol={{ span: 4 }} wrapperCol={{ span: 20 }} style={{ maxWidth: 900, marginTop: 16 }}>
<Form.Item label='oauth供商'>
<Select mode="multiple" style={{ width: '100%' }} placeholder="请选择oauth供商" value={oauthIds} onChange={(value) => {
<Form.Item label='oauth商'>
<Select mode="multiple" style={{ width: '100%' }} placeholder="请选择oauth商" value={oauthIds} onChange={(value) => {
updateConfig(id, config, 'oauthIds', value, 'oauth');
}} options={oauthOptions}/>
</Form.Item>

View File

@ -95,6 +95,7 @@ export default function render(props) {
<div>* 如需启用邮箱登录请先前往配置管理邮箱设置创建系统邮箱,并完成相关配置</div>
<div>* 如需启用小程序授权登录请先前往应用管理创建小程序application,并完成基础配置</div>
<div>* 如需启用公众号授权登录请先前往应用管理创建是服务号的公众号application,并完成基础配置</div>
<div>* 如需启用OAuth授权登录请先前往OAuth管理创建OAuth供应商,并启用</div>
</div>
</Row>
{passports && passports.map((passport) => {

View File

@ -266,7 +266,8 @@ const i18ns = [
"missing_client_id": "缺少 client_id 参数",
"unknown": "未知错误,请稍后重试"
}
}
},
"login": "当前暂未支持该第三方应用授权登录"
}
}
},

View File

@ -140,5 +140,68 @@ const triggers = [
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;

View File

@ -1,6 +1,9 @@
"use strict";
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 assert_1 = tslib_1.__importDefault(require("assert"));
const types_1 = require("oak-domain/lib/types");
@ -20,6 +23,7 @@ async function loginByOauth(params, context) {
// 验证 state 并获取 OAuth 配置
const [state] = await context.select("oauthState", {
data: {
providerId: 1,
provider: {
type: 1,
clientId: 1,
@ -36,6 +40,31 @@ async function loginByOauth(params, context) {
state: stateCode,
},
}, { 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.provider?.ableState && state.provider?.ableState === 'enabled', '该 OAuth 提供商已被禁用');
// 如果已经使用
@ -184,7 +213,6 @@ async function loginByOauth(params, context) {
return tokenValue;
}
}
exports.loginByOauth = loginByOauth;
async function getOAuthClientInfo(params, context) {
const { client_id, currentUserId } = params;
const closeRootMode = context.openRootMode();
@ -247,7 +275,6 @@ async function getOAuthClientInfo(params, context) {
alreadyAuth: !!hasAuth,
};
}
exports.getOAuthClientInfo = getOAuthClientInfo;
async function createOAuthState(params, context) {
const { providerId, userId, type } = params;
const closeRootMode = context.openRootMode();
@ -269,7 +296,6 @@ async function createOAuthState(params, context) {
closeRootMode();
return state;
}
exports.createOAuthState = createOAuthState;
async function authorize(params, context) {
const { response_type, client_id, redirect_uri, scope, state, action, code_challenge, code_challenge_method } = params;
if (response_type !== 'code') {
@ -336,7 +362,7 @@ async function authorize(params, context) {
applicationId: context.getApplicationId(),
userId: context.getCurrentUserId(),
scope: scope === undefined ? [] : [scope],
expiresAt: Date.now() + 10 * 60 * 1000,
expiresAt: Date.now() + 10 * 60 * 1000, // 10分钟后过期
// PKCE 支持
codeChallenge: code_challenge,
codeChallengeMethod: code_challenge_method || 'plain',
@ -366,7 +392,6 @@ async function authorize(params, context) {
closeRootMode();
throw new Error('unknown action');
}
exports.authorize = authorize;
const fetchOAuthUserInfo = async (code, providerConfig) => {
// 1. 使用 code 换取 access_token
const tokenResponse = await fetch(providerConfig.tokenEndpoint, {

View File

@ -268,7 +268,8 @@ const i18ns = [
"missing_client_id": "缺少 client_id 参数",
"unknown": "未知错误,请稍后重试"
}
}
},
"login": "当前暂未支持该第三方应用授权登录"
}
}
},

View File

@ -143,5 +143,68 @@ const triggers = [
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;

View File

@ -6,6 +6,7 @@ import { generateNewIdAsync } from "oak-domain/lib/utils/uuid";
import { loadTokenInfo, setUpTokenAndUser } from "./token";
import { randomUUID } from "crypto";
import { processUserInfo } from "../utils/oauth";
import { OAuthConfig } from "../entities/Passport";
export async function loginByOauth<ED extends EntityDict>(params: {
code: string;
@ -26,6 +27,7 @@ export async function loginByOauth<ED extends EntityDict>(params: {
// 验证 state 并获取 OAuth 配置
const [state] = await context.select("oauthState", {
data: {
providerId: 1,
provider: {
type: 1,
clientId: 1,
@ -41,7 +43,33 @@ export async function loginByOauth<ED extends EntityDict>(params: {
filter: {
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.provider?.ableState && state.provider?.ableState === 'enabled', '该 OAuth 提供商已被禁用');

View File

@ -24,7 +24,7 @@ export default OakComponent({
state: '',
},
lifetimes: {
ready() {
async ready() {
const searchParams = new URLSearchParams(window.location.search);
const clientId = searchParams.get('client_id') || '';
const responseType = searchParams.get('response_type') || '';
@ -32,6 +32,38 @@ export default OakComponent({
const scope = searchParams.get('scope') || '';
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({
client_id: clientId,
response_type: responseType,

View File

@ -16,6 +16,7 @@
"missing_client_id": "缺少 client_id 参数",
"unknown": "未知错误,请稍后重试"
}
}
},
"login": "当前暂未支持该第三方应用授权登录"
}
}

View File

@ -31,6 +31,7 @@ const Upsert = (
>
<Form.Item
label={t('name')}
required={true}
rules={[{ required: true, message: t('nameRequired') }]}
>
<Input placeholder={t('namePlaceholder')} value={item.name || ""}
@ -42,6 +43,7 @@ const Upsert = (
<Form.Item
label={t('type')}
required={true}
rules={[{ required: true, message: t('typeRequired') }]}
extra={
item.type && item.type !== 'oak' && item.type !== 'gitea' ? (
@ -87,6 +89,7 @@ const Upsert = (
<Form.Item
label={t('authorizationEndpoint')}
required={true}
rules={[{ required: true, message: t('authorizationEndpointRequired') }]}
>
<Input placeholder={t('authorizationEndpointPlaceholder')} value={item.authorizationEndpoint || ""}
@ -98,6 +101,7 @@ const Upsert = (
<Form.Item
label={t('tokenEndpoint')}
required={true}
rules={[{ required: true, message: t('tokenEndpointRequired') }]}
>
<Input placeholder={t('tokenEndpointPlaceholder')} value={item.tokenEndpoint || ""}
@ -139,6 +143,7 @@ const Upsert = (
<Form.Item
label={t('clientId')}
required={true}
rules={[{ required: true, message: t('clientIdRequired') }]}
>
<Input placeholder={t('clientIdPlaceholder')} value={item.clientId || ""}
@ -150,6 +155,7 @@ const Upsert = (
<Form.Item
label={t('clientSecret')}
required={true}
rules={[{ required: true, message: t('clientSecretRequired') }]}
>
<Input.Password placeholder={t('clientSecretPlaceholder')} value={item.clientSecret || ""}
@ -176,6 +182,7 @@ const Upsert = (
<Form.Item
label={t('redirectUri')}
required={true}
rules={[{ required: true, message: t('redirectUriRequired') }]}
>
<Input placeholder={t('redirectUriPlaceholder')} value={item.redirectUri || ""}
@ -196,7 +203,7 @@ const Upsert = (
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>

View File

@ -69,9 +69,11 @@ export default OakComponent({
id: 1,
name: 1,
systemId: 1,
ableState: 1,
},
filter: {
systemId,
ableState: 'enabled'
}
});
if (oauthProviders && oauthProviders?.length > 0) {

View File

@ -1,7 +1,7 @@
import React, { useEffect, useState } from "react";
import { EmailConfig, MfwConfig, PfwConfig, SmsConfig, PwdConfig, NameConfig, OAuthConfig } from "../../../entities/Passport";
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';
export default function Oauth(props: {
@ -24,6 +24,7 @@ export default function Oauth(props: {
<div className={Styles.item}>
<div className={Styles.title}>
<Tag color={stateColor}>{t(`passport:v.type.${type}`)}</Tag>
<Tooltip title={(oauthOptions && oauthOptions?.length > 0) ? '' : '请先启用oauth供应商'}>
<Switch
checkedChildren="开启"
unCheckedChildren="关闭"
@ -31,7 +32,9 @@ export default function Oauth(props: {
onChange={(checked) => {
changeEnabled(checked)
}}
disabled={!(oauthOptions && oauthOptions?.length > 0)}
/>
</Tooltip>
</div>
{enabled &&
<div>
@ -41,12 +44,12 @@ export default function Oauth(props: {
style={{ maxWidth: 900, marginTop: 16 }}
>
<Form.Item
label='oauth供商'
label='oauth商'
>
<Select
mode="multiple"
style={{ width: '100%' }}
placeholder="请选择oauth供商"
placeholder="请选择oauth商"
value={oauthIds}
onChange={(value: string[]) => {
updateConfig(id, config!, 'oauthIds', value, 'oauth');

View File

@ -159,6 +159,7 @@ export default function render(props: WebComponentProps<
<div>* ,</div>
<div>* application,</div>
<div>* application,</div>
<div>* OAuth授权登录OAuth管理OAuth供应商,</div>
</div>
</Row>
{passports && passports.map((passport) => {

View File

@ -268,7 +268,8 @@ const i18ns: I18n[] = [
"missing_client_id": "缺少 client_id 参数",
"unknown": "未知错误,请稍后重试"
}
}
},
"login": "当前暂未支持该第三方应用授权登录"
}
}
},

View File

@ -147,6 +147,69 @@ const triggers: Trigger<EntityDict, "oauthProvider", BRC<EntityDict>>[] = [
return count;
}
} 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;