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