fix: sso optimization (#1159)
* fix: update submodules * fix: improve code
This commit is contained in:
parent
37b4151da1
commit
205fafcd4a
@ -1,9 +1,9 @@
|
|||||||
import React, { useEffect, useState, useRef } from 'react';
|
|
||||||
import { Button, Space } from 'antd';
|
|
||||||
import { LoginOutlined } from '@ant-design/icons';
|
import { LoginOutlined } from '@ant-design/icons';
|
||||||
import { useMemoizedFn } from 'ahooks';
|
|
||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import { useAPIClient, useRedirect } from '@nocobase/client';
|
import { useAPIClient, useRedirect, useRequest } from '@nocobase/client';
|
||||||
|
import { useMemoizedFn } from 'ahooks';
|
||||||
|
import { Button, Space } from 'antd';
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
export interface OIDCProvider {
|
export interface OIDCProvider {
|
||||||
clientId: string;
|
clientId: string;
|
||||||
@ -12,26 +12,14 @@ export interface OIDCProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const OIDCList = () => {
|
export const OIDCList = () => {
|
||||||
const [list, setList] = useState<OIDCProvider[]>([]);
|
|
||||||
const [windowHandler, setWindowHandler] = useState<Window | undefined>();
|
const [windowHandler, setWindowHandler] = useState<Window | undefined>();
|
||||||
const api = useAPIClient();
|
const api = useAPIClient();
|
||||||
const redirect = useRedirect();
|
const redirect = useRedirect();
|
||||||
|
|
||||||
const getOidcList = async () => {
|
const { data, loading } = useRequest({
|
||||||
const { data: pluginsRes } = await api.request({
|
resource: 'oidc',
|
||||||
url: 'app:getPlugins',
|
action: 'getEnabledProviders',
|
||||||
});
|
});
|
||||||
if (!(pluginsRes.data as string[]).includes('oidc')) return;
|
|
||||||
const { data: providersRes } = await api.request({
|
|
||||||
url: 'oidcProviders:list',
|
|
||||||
params: {
|
|
||||||
filter: {
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
setList(providersRes.data);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 打开登录弹出框
|
* 打开登录弹出框
|
||||||
@ -80,10 +68,6 @@ export const OIDCList = () => {
|
|||||||
};
|
};
|
||||||
}, [windowHandler]);
|
}, [windowHandler]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
getOidcList();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Space
|
<Space
|
||||||
direction="vertical"
|
direction="vertical"
|
||||||
@ -91,9 +75,9 @@ export const OIDCList = () => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
{list.map((item) => (
|
{data?.data?.map?.((item) => (
|
||||||
<Button shape="round" block key={item.clientId} icon={<LoginOutlined />} onClick={() => handleOpen(item)}>
|
<Button shape="round" block key={item.clientId} icon={<LoginOutlined />} onClick={() => handleOpen(item)}>
|
||||||
OIDC: {item.title}
|
{item.buttonTitle}
|
||||||
</Button>
|
</Button>
|
||||||
))}
|
))}
|
||||||
</Space>
|
</Space>
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { i18n } from '@nocobase/client';
|
import { i18n } from '@nocobase/client';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import zhCN from './zh-CN';
|
|
||||||
import enUS from './en-US';
|
import enUS from './en-US';
|
||||||
import jaJP from './ja-JP';
|
import jaJP from './ja-JP';
|
||||||
import ruRU from './ru-RU';
|
import ruRU from './ru-RU';
|
||||||
import trTR from './tr-TR';
|
import trTR from './tr-TR';
|
||||||
|
import zhCN from './zh-CN';
|
||||||
|
|
||||||
export const NAMESPACE = 'workflow';
|
export const NAMESPACE = 'oidc';
|
||||||
|
|
||||||
i18n.addResources('zh-CN', NAMESPACE, zhCN);
|
i18n.addResources('zh-CN', NAMESPACE, zhCN);
|
||||||
i18n.addResources('en-US', NAMESPACE, enUS);
|
i18n.addResources('en-US', NAMESPACE, enUS);
|
||||||
|
@ -4,6 +4,7 @@ export default {
|
|||||||
Actions: '操作',
|
Actions: '操作',
|
||||||
Delete: '删除',
|
Delete: '删除',
|
||||||
Edit: '编辑',
|
Edit: '编辑',
|
||||||
|
'Button title': '登录按钮标题',
|
||||||
'OIDC manager': 'OIDC 管理',
|
'OIDC manager': 'OIDC 管理',
|
||||||
'OIDC Providers': 'OIDC 身份提供者',
|
'OIDC Providers': 'OIDC 身份提供者',
|
||||||
'Provider name': '名称',
|
'Provider name': '名称',
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { ISchema } from '@formily/react';
|
import { ISchema } from '@formily/react';
|
||||||
import { uid } from '@formily/shared';
|
|
||||||
import { useActionContext, useRequest } from '@nocobase/client';
|
import { useActionContext, useRequest } from '@nocobase/client';
|
||||||
import { IDTOKEN_SIGN_ALG } from '../../server/shared/types';
|
import { IDTOKEN_SIGN_ALG } from '../../server/shared/types';
|
||||||
|
|
||||||
@ -17,6 +16,17 @@ const collection = {
|
|||||||
required: true,
|
required: true,
|
||||||
} as ISchema,
|
} as ISchema,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
name: 'buttonTitle',
|
||||||
|
interface: 'input',
|
||||||
|
uiSchema: {
|
||||||
|
title: '{{t("Button title")}}',
|
||||||
|
type: 'string',
|
||||||
|
'x-component': 'Input',
|
||||||
|
required: true,
|
||||||
|
} as ISchema,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: 'string',
|
type: 'string',
|
||||||
name: 'clientId',
|
name: 'clientId',
|
||||||
@ -169,6 +179,10 @@ export const formProperties = {
|
|||||||
'x-component': 'CollectionField',
|
'x-component': 'CollectionField',
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
},
|
},
|
||||||
|
buttonTitle: {
|
||||||
|
'x-component': 'CollectionField',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
},
|
||||||
clientId: {
|
clientId: {
|
||||||
'x-component': 'CollectionField',
|
'x-component': 'CollectionField',
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
import { Context } from '@nocobase/actions';
|
||||||
|
|
||||||
|
export const getEnabledProviders = async (ctx: Context, next) => {
|
||||||
|
const repository = ctx.db.getRepository('oidcProviders');
|
||||||
|
ctx.body = await repository.find({
|
||||||
|
filter: {
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
fields: ['buttonTitle', 'clientId'],
|
||||||
|
});
|
||||||
|
return next();
|
||||||
|
};
|
@ -10,6 +10,10 @@ export default {
|
|||||||
type: 'string',
|
type: 'string',
|
||||||
name: 'title',
|
name: 'title',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'buttonTitle',
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
comment: '客户端id',
|
comment: '客户端id',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import UsersPlugin from '@nocobase/plugin-users';
|
|
||||||
import { InstallOptions, Plugin } from '@nocobase/server';
|
import { InstallOptions, Plugin } from '@nocobase/server';
|
||||||
import { resolve } from 'path';
|
|
||||||
import { generators } from 'openid-client';
|
import { generators } from 'openid-client';
|
||||||
import { oidc } from './authenticators/oidc';
|
import { resolve } from 'path';
|
||||||
import { getAuthUrl } from './actions/getAuthUrl';
|
import { getAuthUrl } from './actions/getAuthUrl';
|
||||||
|
import { getEnabledProviders } from './actions/getEnabledProviders';
|
||||||
import { redirect } from './actions/redirect';
|
import { redirect } from './actions/redirect';
|
||||||
|
import { oidc } from './authenticators/oidc';
|
||||||
|
|
||||||
export class OidcPlugin extends Plugin {
|
export class OidcPlugin extends Plugin {
|
||||||
#OIDC_NONCE = null;
|
#OIDC_NONCE = null;
|
||||||
@ -20,7 +20,7 @@ export class OidcPlugin extends Plugin {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 获取 User 插件
|
// 获取 User 插件
|
||||||
const userPlugin = this.app.getPlugin('users') as UsersPlugin;
|
const userPlugin = this.app.getPlugin<any>('users');
|
||||||
|
|
||||||
// 注册 OIDC 验证器
|
// 注册 OIDC 验证器
|
||||||
userPlugin.authenticators.register('oidc', oidc);
|
userPlugin.authenticators.register('oidc', oidc);
|
||||||
@ -31,6 +31,7 @@ export class OidcPlugin extends Plugin {
|
|||||||
actions: {
|
actions: {
|
||||||
getAuthUrl,
|
getAuthUrl,
|
||||||
redirect,
|
redirect,
|
||||||
|
getEnabledProviders,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -46,7 +47,7 @@ export class OidcPlugin extends Plugin {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 开放访问权限
|
// 开放访问权限
|
||||||
this.app.acl.allow('oidcProviders', '*');
|
this.app.acl.allow('oidcProviders', '*', 'allowConfigure');
|
||||||
this.app.acl.allow('oidc', '*');
|
this.app.acl.allow('oidc', '*');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import React, { useEffect, useState, useRef } from 'react';
|
|
||||||
import { Button, Space } from 'antd';
|
|
||||||
import { LoginOutlined } from '@ant-design/icons';
|
import { LoginOutlined } from '@ant-design/icons';
|
||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import { useAPIClient, useRedirect } from '@nocobase/client';
|
import { useAPIClient, useRedirect, useRequest } from '@nocobase/client';
|
||||||
|
import { Button, Space } from 'antd';
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
export interface SAMLProvider {
|
export interface SAMLProvider {
|
||||||
title: string;
|
title: string;
|
||||||
@ -11,26 +11,14 @@ export interface SAMLProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const SAMLList = () => {
|
export const SAMLList = () => {
|
||||||
const [list, setList] = useState<SAMLProvider[]>([]);
|
|
||||||
const [windowHandler, setWindowHandler] = useState<Window | undefined>();
|
const [windowHandler, setWindowHandler] = useState<Window | undefined>();
|
||||||
const api = useAPIClient();
|
const api = useAPIClient();
|
||||||
const redirect = useRedirect();
|
const redirect = useRedirect();
|
||||||
|
|
||||||
const getSamlList = async () => {
|
const { data, loading } = useRequest({
|
||||||
const { data: pluginsRes } = await api.request({
|
resource: 'saml',
|
||||||
url: 'app:getPlugins',
|
action: 'getEnabledProviders',
|
||||||
});
|
});
|
||||||
if (!(pluginsRes.data as string[]).includes('saml')) return;
|
|
||||||
const { data: providersRes } = await api.request({
|
|
||||||
url: 'samlProviders:list',
|
|
||||||
params: {
|
|
||||||
filter: {
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
setList(providersRes.data);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 打开登录弹出框
|
* 打开登录弹出框
|
||||||
@ -79,10 +67,6 @@ export const SAMLList = () => {
|
|||||||
};
|
};
|
||||||
}, [windowHandler]);
|
}, [windowHandler]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
getSamlList();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Space
|
<Space
|
||||||
direction="vertical"
|
direction="vertical"
|
||||||
@ -90,9 +74,9 @@ export const SAMLList = () => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
{list.map((item) => (
|
{data?.data?.map?.((item) => (
|
||||||
<Button shape="round" block key={item.clientId} icon={<LoginOutlined />} onClick={() => handleOpen(item)}>
|
<Button shape="round" block key={item.clientId} icon={<LoginOutlined />} onClick={() => handleOpen(item)}>
|
||||||
SAML: {item.title}
|
{item.buttonTitle}
|
||||||
</Button>
|
</Button>
|
||||||
))}
|
))}
|
||||||
</Space>
|
</Space>
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { i18n } from '@nocobase/client';
|
import { i18n } from '@nocobase/client';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import zhCN from './zh-CN';
|
|
||||||
import enUS from './en-US';
|
import enUS from './en-US';
|
||||||
import jaJP from './ja-JP';
|
import jaJP from './ja-JP';
|
||||||
import ruRU from './ru-RU';
|
import ruRU from './ru-RU';
|
||||||
import trTR from './tr-TR';
|
import trTR from './tr-TR';
|
||||||
|
import zhCN from './zh-CN';
|
||||||
|
|
||||||
export const NAMESPACE = 'workflow';
|
export const NAMESPACE = 'saml';
|
||||||
|
|
||||||
i18n.addResources('zh-CN', NAMESPACE, zhCN);
|
i18n.addResources('zh-CN', NAMESPACE, zhCN);
|
||||||
i18n.addResources('en-US', NAMESPACE, enUS);
|
i18n.addResources('en-US', NAMESPACE, enUS);
|
||||||
|
@ -6,6 +6,7 @@ export default {
|
|||||||
Actions: '操作',
|
Actions: '操作',
|
||||||
Title: '身份提供者名称',
|
Title: '身份提供者名称',
|
||||||
Enable: '启用',
|
Enable: '启用',
|
||||||
|
'Button title': '登录按钮标题',
|
||||||
'SAML manager': 'SAML 管理',
|
'SAML manager': 'SAML 管理',
|
||||||
'SAML Providers': 'SAML 身份提供者',
|
'SAML Providers': 'SAML 身份提供者',
|
||||||
'Redirect url': '重定向地址',
|
'Redirect url': '重定向地址',
|
||||||
|
@ -15,6 +15,17 @@ const collection = {
|
|||||||
required: true,
|
required: true,
|
||||||
} as ISchema,
|
} as ISchema,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
name: 'buttonTitle',
|
||||||
|
interface: 'input',
|
||||||
|
uiSchema: {
|
||||||
|
title: '{{t("Button title")}}',
|
||||||
|
type: 'string',
|
||||||
|
'x-component': 'Input',
|
||||||
|
required: true,
|
||||||
|
} as ISchema,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: 'string',
|
type: 'string',
|
||||||
name: 'clientId',
|
name: 'clientId',
|
||||||
@ -98,6 +109,10 @@ export const formProperties = {
|
|||||||
'x-component': 'CollectionField',
|
'x-component': 'CollectionField',
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
},
|
},
|
||||||
|
buttonTitle: {
|
||||||
|
'x-component': 'CollectionField',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
},
|
||||||
clientId: {
|
clientId: {
|
||||||
'x-component': 'CollectionField',
|
'x-component': 'CollectionField',
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
import { Context } from '@nocobase/actions';
|
||||||
|
|
||||||
|
export const getEnabledProviders = async (ctx: Context, next) => {
|
||||||
|
const repository = ctx.db.getRepository('samlProviders');
|
||||||
|
ctx.body = await repository.find({
|
||||||
|
filter: {
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
fields: ['buttonTitle', 'clientId'],
|
||||||
|
});
|
||||||
|
return next();
|
||||||
|
};
|
@ -9,6 +9,10 @@ export default {
|
|||||||
type: 'string',
|
type: 'string',
|
||||||
name: 'title',
|
name: 'title',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
name: 'buttonTitle',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
comment: '客户端id',
|
comment: '客户端id',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import UsersPlugin from 'packages/plugins/users/src/server';
|
|
||||||
import { InstallOptions, Plugin } from '@nocobase/server';
|
import { InstallOptions, Plugin } from '@nocobase/server';
|
||||||
import { resolve } from 'path';
|
import { resolve } from 'path';
|
||||||
import { saml } from './authenticators/saml';
|
|
||||||
import { redirect } from './actions/redirect';
|
|
||||||
import { metadata } from './actions/metadata';
|
|
||||||
import { getAuthUrl } from './actions/getAuthUrl';
|
import { getAuthUrl } from './actions/getAuthUrl';
|
||||||
|
import { getEnabledProviders } from './actions/getEnabledProviders';
|
||||||
|
import { metadata } from './actions/metadata';
|
||||||
|
import { redirect } from './actions/redirect';
|
||||||
|
import { saml } from './authenticators/saml';
|
||||||
|
|
||||||
export class SAMLPlugin extends Plugin {
|
export class SAMLPlugin extends Plugin {
|
||||||
afterAdd() {}
|
afterAdd() {}
|
||||||
@ -18,7 +18,7 @@ export class SAMLPlugin extends Plugin {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 获取 User 插件
|
// 获取 User 插件
|
||||||
const userPlugin = this.app.getPlugin('users') as UsersPlugin;
|
const userPlugin = this.app.getPlugin<any>('users');
|
||||||
|
|
||||||
// 注册 SAML 验证器
|
// 注册 SAML 验证器
|
||||||
userPlugin.authenticators.register('saml', saml);
|
userPlugin.authenticators.register('saml', saml);
|
||||||
@ -30,11 +30,12 @@ export class SAMLPlugin extends Plugin {
|
|||||||
redirect,
|
redirect,
|
||||||
metadata,
|
metadata,
|
||||||
getAuthUrl,
|
getAuthUrl,
|
||||||
|
getEnabledProviders,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// 开放访问权限
|
// 开放访问权限
|
||||||
this.app.acl.allow('samlProviders', '*');
|
this.app.acl.allow('samlProviders', '*', 'allowConfigure');
|
||||||
this.app.acl.allow('saml', '*');
|
this.app.acl.allow('saml', '*');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 5a5fba41ae1cecaf3b073fcb610e9dc68dad0b85
|
Subproject commit cf44c47ff9b5ef41e8becde3f1c8b5b1c2af6cda
|
Loading…
Reference in New Issue
Block a user