feat: 修改实体,将provider的type定义改为string,以实现自定义的类型注入。
feat: 在OAuthApplication中新增强制PKCE参数 fix: 修复了oauth相关upsert组件的部分问题
This commit is contained in:
parent
8367447700
commit
0f90d5c360
|
|
@ -11,6 +11,7 @@ export default OakComponent({
|
|||
isConfidential: 1,
|
||||
scopes: 1,
|
||||
ableState: 1,
|
||||
requirePKCE: 1,
|
||||
},
|
||||
filters: [{
|
||||
filter() {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ export default OakComponent({
|
|||
isConfidential: 1,
|
||||
scopes: 1, // string[]
|
||||
ableState: 1,
|
||||
requirePKCE: 1,
|
||||
},
|
||||
formData({ data, features }) {
|
||||
if (!data) {
|
||||
|
|
|
|||
|
|
@ -18,5 +18,7 @@
|
|||
"ableState": "启用状态",
|
||||
"noData": "无数据",
|
||||
"clientId": "客户端ID",
|
||||
"clientIdPlaceholder": "自动生成的客户端ID"
|
||||
"clientIdPlaceholder": "自动生成的客户端ID",
|
||||
"requirePKCE": "强制 PKCE",
|
||||
"requirePKCETooltip": "启用后,授权请求必须使用PKCE扩展以增强安全性。"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,6 +62,11 @@ const Upsert = (props) => {
|
|||
<Form.Item label={t('ableState')} valuePropName="checked">
|
||||
<Switch checked={!!item.ableState} onChange={(checked) => update({ ableState: checked ? "enabled" : "disabled" })}/>
|
||||
</Form.Item>
|
||||
|
||||
{/* requirePKCE */}
|
||||
<Form.Item label={t('requirePKCE')} valuePropName="checked" tooltip={t('requirePKCETooltip')}>
|
||||
<Switch checked={!!item.requirePKCE} onChange={(checked) => update({ requirePKCE: checked })}/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ const OauthProvider = (props) => {
|
|||
const attrs = [
|
||||
"id", "name", "description", "redirectUris",
|
||||
"logo", "isConfidential", "scopes",
|
||||
"ableState"
|
||||
"ableState", "requirePKCE",
|
||||
];
|
||||
const [upsertId, setUpsertId] = React.useState(null);
|
||||
const handleAction = (row, action) => {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ export default OakComponent({
|
|||
revokeEndpoint: 1,
|
||||
refreshEndpoint: 1,
|
||||
clientId: 1,
|
||||
scopes: 1,
|
||||
clientSecret: 1,
|
||||
redirectUri: 1,
|
||||
autoRegister: 1,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ export default OakComponent({
|
|||
name: 1,
|
||||
type: 1,
|
||||
logo: 1,
|
||||
scopes: 1,
|
||||
authorizationEndpoint: 1,
|
||||
tokenEndpoint: 1,
|
||||
userInfoEndpoint: 1,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import React from 'react';
|
||||
import { Form, Input, Switch, Select } from 'antd';
|
||||
import { Form, Input, Switch, Select, Typography } from 'antd';
|
||||
import Styles from './styles.module.less';
|
||||
const { Text } = Typography;
|
||||
const Upsert = (props) => {
|
||||
const { item } = props.data;
|
||||
const { t, update } = props.methods;
|
||||
|
|
@ -15,26 +16,19 @@ const Upsert = (props) => {
|
|||
}}/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t('type')} rules={[{ required: true, message: t('typeRequired') }]}>
|
||||
<Select placeholder={t('typePlaceholder')} value={item.type || ""} onChange={(v) => {
|
||||
update({ type: v });
|
||||
}}>
|
||||
<Select.Option value="oak">Oak</Select.Option>
|
||||
<Select.Option value="gitea">Gitea</Select.Option>
|
||||
<Select.Option value="github">GitHub</Select.Option>
|
||||
<Select.Option value="google">Google</Select.Option>
|
||||
<Select.Option value="facebook">Facebook</Select.Option>
|
||||
<Select.Option value="twitter">Twitter</Select.Option>
|
||||
<Select.Option value="linkedin">LinkedIn</Select.Option>
|
||||
<Select.Option value="custom">Custom</Select.Option>
|
||||
<Select.Option value="gitlab">GitLab</Select.Option>
|
||||
<Select.Option value="microsoft">Microsoft</Select.Option>
|
||||
<Select.Option value="apple">Apple</Select.Option>
|
||||
<Select.Option value="tencent">Tencent</Select.Option>
|
||||
<Select.Option value="weixin">Weixin</Select.Option>
|
||||
<Select.Option value="weibo">Weibo</Select.Option>
|
||||
<Select.Option value="dingtalk">DingTalk</Select.Option>
|
||||
</Select>
|
||||
<Form.Item label={t('type')} 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] : []} // 保持数组形式
|
||||
onChange={(v) => {
|
||||
// 只取最后一个输入或选择的值
|
||||
const last = v.slice(-1)[0];
|
||||
update({ type: last });
|
||||
}} tokenSeparators={[',']} maxTagCount={1} // 只显示一个标签
|
||||
options={[
|
||||
{ value: 'oak', label: 'Oak' },
|
||||
{ value: 'gitea', label: 'Gitea' },
|
||||
]}/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t('logo')}>
|
||||
|
|
@ -54,7 +48,7 @@ const Upsert = (props) => {
|
|||
update({ tokenEndpoint: v.target.value });
|
||||
}}/>
|
||||
</Form.Item>
|
||||
|
||||
|
||||
<Form.Item label={t('refreshEndpoint')}>
|
||||
<Input placeholder={t('refreshEndpointPlaceholder')} value={item.refreshEndpoint || ""} onChange={(v) => {
|
||||
update({ refreshEndpoint: v.target.value });
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ export const selectFreeEntities = [
|
|||
'userEntityGrant',
|
||||
'wechatMpJump',
|
||||
'applicationPassport',
|
||||
'passport'
|
||||
'passport',
|
||||
'oauthProvider'
|
||||
];
|
||||
// 可以自由更新的对象
|
||||
export const updateFreeDict = {
|
||||
|
|
|
|||
|
|
@ -398,7 +398,9 @@ const i18ns = [
|
|||
"ableState": "启用状态",
|
||||
"noData": "无数据",
|
||||
"clientId": "客户端ID",
|
||||
"clientIdPlaceholder": "自动生成的客户端ID"
|
||||
"clientIdPlaceholder": "自动生成的客户端ID",
|
||||
"requirePKCE": "强制 PKCE",
|
||||
"requirePKCETooltip": "启用后,授权请求必须使用PKCE扩展以增强安全性。"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ export interface Schema extends EntityShape {
|
|||
logo?: String<512>;
|
||||
isConfidential: Boolean;
|
||||
scopes?: StringListJson;
|
||||
requirePKCE: Boolean;
|
||||
}
|
||||
export type SecretAction = 'resetSecret';
|
||||
export type Action = AbleAction | SecretAction;
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ export const entityDesc = {
|
|||
logo: '应用 Logo',
|
||||
isConfidential: '是否保密',
|
||||
scopes: '应用权限范围',
|
||||
requirePKCE: '强制 PKCE',
|
||||
},
|
||||
action: {
|
||||
enable: '启用',
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { StringListJson } from '../types/datatype';
|
|||
export interface Schema extends EntityShape {
|
||||
system: System;
|
||||
name: String<64>;
|
||||
type: "oak" | "gitea" | "github" | "google" | "facebook" | "twitter" | "linkedin" | "custom" | "gitlab" | "microsoft" | "apple" | "tencent" | "weixin" | "weibo" | "dingtalk";
|
||||
type: String<64>;
|
||||
logo?: String<512>;
|
||||
authorizationEndpoint: String<512>;
|
||||
tokenEndpoint: String<512>;
|
||||
|
|
|
|||
|
|
@ -40,6 +40,10 @@ export const desc = {
|
|||
scopes: {
|
||||
type: "object"
|
||||
},
|
||||
requirePKCE: {
|
||||
notNull: true,
|
||||
type: "boolean"
|
||||
},
|
||||
ableState: {
|
||||
type: "enum",
|
||||
enumeration: ["enabled", "disabled"]
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ export type OpSchema = EntityShape & {
|
|||
logo?: String<512> | null;
|
||||
isConfidential: Boolean;
|
||||
scopes?: StringListJson | null;
|
||||
requirePKCE: Boolean;
|
||||
ableState?: AbleState | null;
|
||||
} & {
|
||||
[A in ExpressionKey]?: any;
|
||||
|
|
@ -32,6 +33,7 @@ export type OpFilter = {
|
|||
logo: Q_StringValue;
|
||||
isConfidential: Q_BooleanValue;
|
||||
scopes: JsonFilter<StringListJson>;
|
||||
requirePKCE: Q_BooleanValue;
|
||||
ableState: Q_EnumValue<AbleState>;
|
||||
} & ExprOp<OpAttr | string>;
|
||||
export type OpProjection = {
|
||||
|
|
@ -49,6 +51,7 @@ export type OpProjection = {
|
|||
logo?: number;
|
||||
isConfidential?: number;
|
||||
scopes?: number | JsonProjection<StringListJson>;
|
||||
requirePKCE?: number;
|
||||
ableState?: number;
|
||||
} & Partial<ExprOp<OpAttr | string>>;
|
||||
export type OpSortAttr = Partial<{
|
||||
|
|
@ -64,6 +67,7 @@ export type OpSortAttr = Partial<{
|
|||
logo: number;
|
||||
isConfidential: number;
|
||||
scopes: number;
|
||||
requirePKCE: number;
|
||||
ableState: number;
|
||||
[k: string]: any;
|
||||
} | ExprOp<OpAttr | string>>;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@
|
|||
"redirectUris": "重定向 URI",
|
||||
"logo": "应用 Logo",
|
||||
"isConfidential": "是否保密",
|
||||
"scopes": "应用权限范围"
|
||||
"scopes": "应用权限范围",
|
||||
"requirePKCE": "强制 PKCE"
|
||||
},
|
||||
"action": {
|
||||
"enable": "启用",
|
||||
|
|
|
|||
|
|
@ -15,8 +15,10 @@ export const desc = {
|
|||
},
|
||||
type: {
|
||||
notNull: true,
|
||||
type: "enum",
|
||||
enumeration: ["oak", "gitea", "github", "google", "facebook", "twitter", "linkedin", "custom", "gitlab", "microsoft", "apple", "tencent", "weixin", "weibo", "dingtalk"]
|
||||
type: "varchar",
|
||||
params: {
|
||||
length: 64
|
||||
}
|
||||
},
|
||||
logo: {
|
||||
type: "varchar",
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { StringListJson } from "../../types/datatype";
|
|||
export type OpSchema = EntityShape & {
|
||||
systemId: ForeignKey<"system">;
|
||||
name: String<64>;
|
||||
type: "oak" | "gitea" | "github" | "google" | "facebook" | "twitter" | "linkedin" | "custom" | "gitlab" | "microsoft" | "apple" | "tencent" | "weixin" | "weibo" | "dingtalk";
|
||||
type: String<64>;
|
||||
logo?: String<512> | null;
|
||||
authorizationEndpoint: String<512>;
|
||||
tokenEndpoint: String<512>;
|
||||
|
|
@ -32,7 +32,7 @@ export type OpFilter = {
|
|||
$$updateAt$$: Q_DateValue;
|
||||
systemId: Q_StringValue;
|
||||
name: Q_StringValue;
|
||||
type: Q_EnumValue<"oak" | "gitea" | "github" | "google" | "facebook" | "twitter" | "linkedin" | "custom" | "gitlab" | "microsoft" | "apple" | "tencent" | "weixin" | "weibo" | "dingtalk">;
|
||||
type: Q_StringValue;
|
||||
logo: Q_StringValue;
|
||||
authorizationEndpoint: Q_StringValue;
|
||||
tokenEndpoint: Q_StringValue;
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
declare const _default: (import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "message", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "address", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "application", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "article", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "articleMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "extraFile", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "user", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "userEntityGrant", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatQrCode", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "notification", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatLogin", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "parasite", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "sessionMessage", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatPublicTag", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMpJump", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "system", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "passport", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthApplication", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthProvider", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthUser", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthUserAuthorization", import("../context/BackendRuntimeContext").BackendRuntimeContext<import("../oak-app-domain").EntityDict>>)[];
|
||||
declare const _default: (import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthApplication", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "message", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "address", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "application", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "article", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "articleMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "extraFile", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "user", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "userEntityGrant", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatQrCode", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "notification", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatLogin", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "parasite", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "sessionMessage", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatPublicTag", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMpJump", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "system", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "passport", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthProvider", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthUser", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthUserAuthorization", import("../context/BackendRuntimeContext").BackendRuntimeContext<import("../oak-app-domain").EntityDict>>)[];
|
||||
export default _default;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ const triggers = [
|
|||
const systemId = context.getSystemId();
|
||||
data.systemId = systemId;
|
||||
data.clientSecret = randomUUID();
|
||||
// 默认不强制 PKCE
|
||||
data.requirePKCE = data.requirePKCE ?? false;
|
||||
return 0; // 没有引起数据库行修改
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ export declare function createToDo<ED extends EntityDict & BaseEntityDict, T ext
|
|||
redirectTo: EntityDict['toDo']['OpSchema']['redirectTo'];
|
||||
entity: any;
|
||||
entityId: string;
|
||||
}, userIds?: string[]): Promise<1 | 0>;
|
||||
}, userIds?: string[]): Promise<0 | 1>;
|
||||
/**
|
||||
* 完成todo例程,当在entity对象上进行action操作时(操作条件是filter),将对应的todo完成
|
||||
* 必须在entity的action的后trigger中调用
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@ exports.selectFreeEntities = [
|
|||
'userEntityGrant',
|
||||
'wechatMpJump',
|
||||
'applicationPassport',
|
||||
'passport'
|
||||
'passport',
|
||||
'oauthProvider'
|
||||
];
|
||||
// 可以自由更新的对象
|
||||
exports.updateFreeDict = {
|
||||
|
|
|
|||
|
|
@ -400,7 +400,9 @@ const i18ns = [
|
|||
"ableState": "启用状态",
|
||||
"noData": "无数据",
|
||||
"clientId": "客户端ID",
|
||||
"clientIdPlaceholder": "自动生成的客户端ID"
|
||||
"clientIdPlaceholder": "自动生成的客户端ID",
|
||||
"requirePKCE": "强制 PKCE",
|
||||
"requirePKCETooltip": "启用后,授权请求必须使用PKCE扩展以增强安全性。"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ export interface Schema extends EntityShape {
|
|||
logo?: String<512>;
|
||||
isConfidential: Boolean;
|
||||
scopes?: StringListJson;
|
||||
requirePKCE: Boolean;
|
||||
}
|
||||
export type SecretAction = 'resetSecret';
|
||||
export type Action = AbleAction | SecretAction;
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ exports.entityDesc = {
|
|||
logo: '应用 Logo',
|
||||
isConfidential: '是否保密',
|
||||
scopes: '应用权限范围',
|
||||
requirePKCE: '强制 PKCE',
|
||||
},
|
||||
action: {
|
||||
enable: '启用',
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { StringListJson } from '../types/datatype';
|
|||
export interface Schema extends EntityShape {
|
||||
system: System;
|
||||
name: String<64>;
|
||||
type: "oak" | "gitea" | "github" | "google" | "facebook" | "twitter" | "linkedin" | "custom" | "gitlab" | "microsoft" | "apple" | "tencent" | "weixin" | "weibo" | "dingtalk";
|
||||
type: String<64>;
|
||||
logo?: String<512>;
|
||||
authorizationEndpoint: String<512>;
|
||||
tokenEndpoint: String<512>;
|
||||
|
|
|
|||
|
|
@ -43,6 +43,10 @@ exports.desc = {
|
|||
scopes: {
|
||||
type: "object"
|
||||
},
|
||||
requirePKCE: {
|
||||
notNull: true,
|
||||
type: "boolean"
|
||||
},
|
||||
ableState: {
|
||||
type: "enum",
|
||||
enumeration: ["enabled", "disabled"]
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ export type OpSchema = EntityShape & {
|
|||
logo?: String<512> | null;
|
||||
isConfidential: Boolean;
|
||||
scopes?: StringListJson | null;
|
||||
requirePKCE: Boolean;
|
||||
ableState?: AbleState | null;
|
||||
} & {
|
||||
[A in ExpressionKey]?: any;
|
||||
|
|
@ -32,6 +33,7 @@ export type OpFilter = {
|
|||
logo: Q_StringValue;
|
||||
isConfidential: Q_BooleanValue;
|
||||
scopes: JsonFilter<StringListJson>;
|
||||
requirePKCE: Q_BooleanValue;
|
||||
ableState: Q_EnumValue<AbleState>;
|
||||
} & ExprOp<OpAttr | string>;
|
||||
export type OpProjection = {
|
||||
|
|
@ -49,6 +51,7 @@ export type OpProjection = {
|
|||
logo?: number;
|
||||
isConfidential?: number;
|
||||
scopes?: number | JsonProjection<StringListJson>;
|
||||
requirePKCE?: number;
|
||||
ableState?: number;
|
||||
} & Partial<ExprOp<OpAttr | string>>;
|
||||
export type OpSortAttr = Partial<{
|
||||
|
|
@ -64,6 +67,7 @@ export type OpSortAttr = Partial<{
|
|||
logo: number;
|
||||
isConfidential: number;
|
||||
scopes: number;
|
||||
requirePKCE: number;
|
||||
ableState: number;
|
||||
[k: string]: any;
|
||||
} | ExprOp<OpAttr | string>>;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@
|
|||
"redirectUris": "重定向 URI",
|
||||
"logo": "应用 Logo",
|
||||
"isConfidential": "是否保密",
|
||||
"scopes": "应用权限范围"
|
||||
"scopes": "应用权限范围",
|
||||
"requirePKCE": "强制 PKCE"
|
||||
},
|
||||
"action": {
|
||||
"enable": "启用",
|
||||
|
|
|
|||
|
|
@ -18,8 +18,10 @@ exports.desc = {
|
|||
},
|
||||
type: {
|
||||
notNull: true,
|
||||
type: "enum",
|
||||
enumeration: ["oak", "gitea", "github", "google", "facebook", "twitter", "linkedin", "custom", "gitlab", "microsoft", "apple", "tencent", "weixin", "weibo", "dingtalk"]
|
||||
type: "varchar",
|
||||
params: {
|
||||
length: 64
|
||||
}
|
||||
},
|
||||
logo: {
|
||||
type: "varchar",
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { StringListJson } from "../../types/datatype";
|
|||
export type OpSchema = EntityShape & {
|
||||
systemId: ForeignKey<"system">;
|
||||
name: String<64>;
|
||||
type: "oak" | "gitea" | "github" | "google" | "facebook" | "twitter" | "linkedin" | "custom" | "gitlab" | "microsoft" | "apple" | "tencent" | "weixin" | "weibo" | "dingtalk";
|
||||
type: String<64>;
|
||||
logo?: String<512> | null;
|
||||
authorizationEndpoint: String<512>;
|
||||
tokenEndpoint: String<512>;
|
||||
|
|
@ -32,7 +32,7 @@ export type OpFilter = {
|
|||
$$updateAt$$: Q_DateValue;
|
||||
systemId: Q_StringValue;
|
||||
name: Q_StringValue;
|
||||
type: Q_EnumValue<"oak" | "gitea" | "github" | "google" | "facebook" | "twitter" | "linkedin" | "custom" | "gitlab" | "microsoft" | "apple" | "tencent" | "weixin" | "weibo" | "dingtalk">;
|
||||
type: Q_StringValue;
|
||||
logo: Q_StringValue;
|
||||
authorizationEndpoint: Q_StringValue;
|
||||
tokenEndpoint: Q_StringValue;
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
declare const _default: (import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "message", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "address", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "application", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "article", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "articleMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "extraFile", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "user", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "userEntityGrant", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatQrCode", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "notification", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatLogin", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "parasite", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "sessionMessage", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatPublicTag", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMpJump", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "system", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "passport", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthApplication", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthProvider", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthUser", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthUserAuthorization", import("../context/BackendRuntimeContext").BackendRuntimeContext<import("../oak-app-domain").EntityDict>>)[];
|
||||
declare const _default: (import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthApplication", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "message", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "address", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "application", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "article", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "articleMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "extraFile", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "user", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "userEntityGrant", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatQrCode", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "notification", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatLogin", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "parasite", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "sessionMessage", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatPublicTag", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMpJump", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "system", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "passport", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthProvider", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthUser", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthUserAuthorization", import("../context/BackendRuntimeContext").BackendRuntimeContext<import("../oak-app-domain").EntityDict>>)[];
|
||||
export default _default;
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ const triggers = [
|
|||
const systemId = context.getSystemId();
|
||||
data.systemId = systemId;
|
||||
data.clientSecret = (0, crypto_1.randomUUID)();
|
||||
// 默认不强制 PKCE
|
||||
data.requirePKCE = data.requirePKCE ?? false;
|
||||
return 0; // 没有引起数据库行修改
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ export default OakComponent({
|
|||
isConfidential: 1,
|
||||
scopes: 1,
|
||||
ableState: 1,
|
||||
requirePKCE: 1,
|
||||
},
|
||||
filters: [{
|
||||
filter() {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ export default OakComponent({
|
|||
isConfidential: 1,
|
||||
scopes: 1, // string[]
|
||||
ableState: 1,
|
||||
requirePKCE: 1,
|
||||
},
|
||||
formData({ data, features }) {
|
||||
if (!data) {
|
||||
|
|
|
|||
|
|
@ -18,5 +18,7 @@
|
|||
"ableState": "启用状态",
|
||||
"noData": "无数据",
|
||||
"clientId": "客户端ID",
|
||||
"clientIdPlaceholder": "自动生成的客户端ID"
|
||||
"clientIdPlaceholder": "自动生成的客户端ID",
|
||||
"requirePKCE": "强制 PKCE",
|
||||
"requirePKCETooltip": "启用后,授权请求必须使用PKCE扩展以增强安全性。"
|
||||
}
|
||||
|
|
@ -149,6 +149,18 @@ const Upsert = (
|
|||
onChange={(checked) => update({ ableState: checked ? "enabled" : "disabled" })}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
{/* requirePKCE */}
|
||||
<Form.Item
|
||||
label={t('requirePKCE')}
|
||||
valuePropName="checked"
|
||||
tooltip={t('requirePKCETooltip')}
|
||||
>
|
||||
<Switch
|
||||
checked={!!item.requirePKCE}
|
||||
onChange={(checked) => update({ requirePKCE: checked })}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ const OauthProvider = (
|
|||
const attrs = [
|
||||
"id", "name", "description", "redirectUris",
|
||||
"logo", "isConfidential", "scopes",
|
||||
"ableState"
|
||||
"ableState", "requirePKCE",
|
||||
]
|
||||
|
||||
const [upsertId, setUpsertId] = React.useState<string | null>(null);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ export default OakComponent({
|
|||
revokeEndpoint: 1,
|
||||
refreshEndpoint: 1,
|
||||
clientId: 1,
|
||||
scopes: 1,
|
||||
clientSecret: 1,
|
||||
redirectUri: 1,
|
||||
autoRegister: 1,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ export default OakComponent({
|
|||
name: 1,
|
||||
type: 1,
|
||||
logo: 1,
|
||||
scopes: 1,
|
||||
authorizationEndpoint: 1,
|
||||
tokenEndpoint: 1,
|
||||
userInfoEndpoint: 1,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import { Form, Input, Switch, Button, Space, Upload, Select } from 'antd';
|
||||
import { Form, Input, Switch, Button, Space, Upload, Select, Typography } from 'antd';
|
||||
import { UploadOutlined } from '@ant-design/icons';
|
||||
import { RowWithActions, WebComponentProps } from 'oak-frontend-base';
|
||||
import Styles from './styles.module.less';
|
||||
import { EntityDict } from '../../../../../oak-app-domain';
|
||||
const { Text } = Typography;
|
||||
|
||||
const Upsert = (
|
||||
props: WebComponentProps<
|
||||
|
|
@ -42,28 +43,36 @@ const Upsert = (
|
|||
<Form.Item
|
||||
label={t('type')}
|
||||
rules={[{ required: true, message: t('typeRequired') }]}
|
||||
extra={
|
||||
item.type && item.type !== 'oak' && item.type !== 'gitea' ? (
|
||||
<Text type="warning">
|
||||
「{item.type}」不是预设类型,请自行注入 handler。
|
||||
</Text>
|
||||
) : undefined
|
||||
}
|
||||
>
|
||||
<Select placeholder={t('typePlaceholder')} value={item.type || ""}
|
||||
<Select
|
||||
mode="tags"
|
||||
placeholder={t('typePlaceholder')}
|
||||
value={item.type ? [item.type] : []} // 保持数组形式
|
||||
onChange={(v) => {
|
||||
update({ type: v as "oak" | "gitea" | "github" | "google" | "facebook" | "twitter" | "linkedin" | "custom" | "gitlab" | "microsoft" | "apple" | "tencent" | "weixin" | "weibo" | "dingtalk" | null | undefined });
|
||||
// 只取最后一个输入或选择的值
|
||||
const last = v.slice(-1)[0] as
|
||||
| "oak"
|
||||
| "gitea"
|
||||
| string
|
||||
| undefined;
|
||||
|
||||
update({ type: last });
|
||||
}}
|
||||
>
|
||||
<Select.Option value="oak">Oak</Select.Option>
|
||||
<Select.Option value="gitea">Gitea</Select.Option>
|
||||
<Select.Option value="github">GitHub</Select.Option>
|
||||
<Select.Option value="google">Google</Select.Option>
|
||||
<Select.Option value="facebook">Facebook</Select.Option>
|
||||
<Select.Option value="twitter">Twitter</Select.Option>
|
||||
<Select.Option value="linkedin">LinkedIn</Select.Option>
|
||||
<Select.Option value="custom">Custom</Select.Option>
|
||||
<Select.Option value="gitlab">GitLab</Select.Option>
|
||||
<Select.Option value="microsoft">Microsoft</Select.Option>
|
||||
<Select.Option value="apple">Apple</Select.Option>
|
||||
<Select.Option value="tencent">Tencent</Select.Option>
|
||||
<Select.Option value="weixin">Weixin</Select.Option>
|
||||
<Select.Option value="weibo">Weibo</Select.Option>
|
||||
<Select.Option value="dingtalk">DingTalk</Select.Option>
|
||||
</Select>
|
||||
tokenSeparators={[',']}
|
||||
maxTagCount={1} // 只显示一个标签
|
||||
options={[
|
||||
{ value: 'oak', label: 'Oak' },
|
||||
{ value: 'gitea', label: 'Gitea' },
|
||||
]}
|
||||
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
|
|
@ -97,7 +106,7 @@ const Upsert = (
|
|||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
|
||||
<Form.Item
|
||||
label={t('refreshEndpoint')}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -22,7 +22,8 @@ export const selectFreeEntities: SelectFreeEntities<EntityDict> = [
|
|||
'userEntityGrant',
|
||||
'wechatMpJump',
|
||||
'applicationPassport',
|
||||
'passport'
|
||||
'passport',
|
||||
'oauthProvider'
|
||||
];
|
||||
|
||||
// 可以自由更新的对象
|
||||
|
|
|
|||
|
|
@ -400,7 +400,9 @@ const i18ns: I18n[] = [
|
|||
"ableState": "启用状态",
|
||||
"noData": "无数据",
|
||||
"clientId": "客户端ID",
|
||||
"clientIdPlaceholder": "自动生成的客户端ID"
|
||||
"clientIdPlaceholder": "自动生成的客户端ID",
|
||||
"requirePKCE": "强制 PKCE",
|
||||
"requirePKCETooltip": "启用后,授权请求必须使用PKCE扩展以增强安全性。"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ export interface Schema extends EntityShape {
|
|||
logo?: String<512>;
|
||||
isConfidential: Boolean;
|
||||
scopes?: StringListJson;
|
||||
requirePKCE: Boolean;
|
||||
};
|
||||
|
||||
export type SecretAction = 'resetSecret';
|
||||
|
|
@ -43,6 +44,7 @@ export const entityDesc: EntityDesc<Schema, Action, '', {
|
|||
logo: '应用 Logo',
|
||||
isConfidential: '是否保密',
|
||||
scopes: '应用权限范围',
|
||||
requirePKCE: '强制 PKCE',
|
||||
},
|
||||
action: {
|
||||
enable: '启用',
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import { StringListJson } from '../types/datatype';
|
|||
export interface Schema extends EntityShape {
|
||||
system: System;
|
||||
name: String<64>;
|
||||
type: "oak" | "gitea" | "github" | "google" | "facebook" | "twitter" | "linkedin" | "custom" | "gitlab" | "microsoft" | "apple" | "tencent" | "weixin" | "weibo" | "dingtalk";
|
||||
type: String<64>; // OAuth 提供方类型 (可以自己实现,默认注入oak, gitea)
|
||||
logo?: String<512>;
|
||||
|
||||
// OAuth 端点 (RFC 6749 Section 3)
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ const triggers: Trigger<EntityDict, "oauthApplication", BRC<EntityDict>>[] = [
|
|||
data.systemId = systemId;
|
||||
data.clientSecret = randomUUID();
|
||||
|
||||
// 默认不强制 PKCE
|
||||
data.requirePKCE = data.requirePKCE ?? false;
|
||||
|
||||
return 0; // 没有引起数据库行修改
|
||||
}
|
||||
} as CreateTrigger<EntityDict, "oauthApplication", BRC<EntityDict>>,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
SET SESSION sql_mode = 'TRADITIONAL';
|
||||
START TRANSACTION;
|
||||
|
||||
alter table oauthApplication
|
||||
add requirePKCE tinyint(1) default false not null after ableState;
|
||||
|
||||
alter table oauthProvider
|
||||
modify type varchar(64) not null;
|
||||
|
||||
COMMIT;
|
||||
Loading…
Reference in New Issue