@nocobase/plugin-auth 0.10.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/README.md +117 -0
  2. package/client.d.ts +3 -0
  3. package/client.js +65 -0
  4. package/lib/client/basic/Options.d.ts +2 -0
  5. package/lib/client/basic/Options.js +51 -0
  6. package/lib/client/basic/SigninPage.d.ts +6 -0
  7. package/lib/client/basic/SigninPage.js +100 -0
  8. package/lib/client/basic/SignupPage.d.ts +5 -0
  9. package/lib/client/basic/SignupPage.js +131 -0
  10. package/lib/client/index.d.ts +3 -0
  11. package/lib/client/index.js +56 -0
  12. package/lib/client/locale/index.d.ts +2 -0
  13. package/lib/client/locale/index.js +19 -0
  14. package/lib/client/locale/zh-CN.d.ts +9 -0
  15. package/lib/client/locale/zh-CN.js +16 -0
  16. package/lib/client/settings/Authenticator.d.ts +2 -0
  17. package/lib/client/settings/Authenticator.js +159 -0
  18. package/lib/client/settings/Options.d.ts +3 -0
  19. package/lib/client/settings/Options.js +56 -0
  20. package/lib/client/settings/authType.d.ts +16 -0
  21. package/lib/client/settings/authType.js +27 -0
  22. package/lib/client/settings/schemas/authenticators.d.ts +3 -0
  23. package/lib/client/settings/schemas/authenticators.js +438 -0
  24. package/lib/index.d.ts +1 -0
  25. package/lib/index.js +20 -0
  26. package/lib/preset.d.ts +3 -0
  27. package/lib/preset.js +12 -0
  28. package/lib/server/actions/auth.d.ts +8 -0
  29. package/lib/server/actions/auth.js +51 -0
  30. package/lib/server/actions/authenticators.d.ts +8 -0
  31. package/lib/server/actions/authenticators.js +131 -0
  32. package/lib/server/basic-auth.d.ts +10 -0
  33. package/lib/server/basic-auth.js +183 -0
  34. package/lib/server/collections/authenticators.d.ts +6 -0
  35. package/lib/server/collections/authenticators.js +93 -0
  36. package/lib/server/collections/users-authenticators.d.ts +7 -0
  37. package/lib/server/collections/users-authenticators.js +75 -0
  38. package/lib/server/index.d.ts +2 -0
  39. package/lib/server/index.js +20 -0
  40. package/lib/server/locale/en-US.d.ts +9 -0
  41. package/lib/server/locale/en-US.js +15 -0
  42. package/lib/server/locale/index.d.ts +3 -0
  43. package/lib/server/locale/index.js +27 -0
  44. package/lib/server/locale/ja-JP.d.ts +5 -0
  45. package/lib/server/locale/ja-JP.js +11 -0
  46. package/lib/server/locale/pt-BR.d.ts +9 -0
  47. package/lib/server/locale/pt-BR.js +15 -0
  48. package/lib/server/locale/zh-CN.d.ts +10 -0
  49. package/lib/server/locale/zh-CN.js +16 -0
  50. package/lib/server/migrations/20230506152253-basic-authenticator.d.ts +5 -0
  51. package/lib/server/migrations/20230506152253-basic-authenticator.js +40 -0
  52. package/lib/server/migrations/20230607174500-update-basic.d.ts +5 -0
  53. package/lib/server/migrations/20230607174500-update-basic.js +43 -0
  54. package/lib/server/model/authenticator.d.ts +6 -0
  55. package/lib/server/model/authenticator.js +72 -0
  56. package/lib/server/plugin.d.ts +11 -0
  57. package/lib/server/plugin.js +130 -0
  58. package/package.json +17 -0
  59. package/server.d.ts +3 -0
  60. package/server.js +65 -0
  61. package/src/client/basic/Options.tsx +31 -0
  62. package/src/client/basic/SigninPage.tsx +65 -0
  63. package/src/client/basic/SignupPage.tsx +91 -0
  64. package/src/client/index.tsx +41 -0
  65. package/src/client/locale/index.ts +7 -0
  66. package/src/client/locale/zh-CN.ts +10 -0
  67. package/src/client/settings/Authenticator.tsx +95 -0
  68. package/src/client/settings/Options.tsx +35 -0
  69. package/src/client/settings/authType.ts +18 -0
  70. package/src/client/settings/schemas/authenticators.ts +402 -0
  71. package/src/index.ts +1 -0
  72. package/src/preset.ts +4 -0
  73. package/src/server/__tests__/actions.test.ts +142 -0
  74. package/src/server/actions/auth.ts +20 -0
  75. package/src/server/actions/authenticators.ts +85 -0
  76. package/src/server/basic-auth.ts +128 -0
  77. package/src/server/collections/.gitkeep +0 -0
  78. package/src/server/collections/authenticators.ts +97 -0
  79. package/src/server/collections/users-authenticators.ts +73 -0
  80. package/src/server/index.ts +2 -0
  81. package/src/server/locale/en-US.ts +10 -0
  82. package/src/server/locale/index.ts +3 -0
  83. package/src/server/locale/ja-JP.ts +4 -0
  84. package/src/server/locale/pt-BR.ts +10 -0
  85. package/src/server/locale/zh-CN.ts +9 -0
  86. package/src/server/migrations/20230506152253-basic-authenticator.ts +22 -0
  87. package/src/server/migrations/20230607174500-update-basic.ts +25 -0
  88. package/src/server/model/authenticator.ts +48 -0
  89. package/src/server/plugin.ts +92 -0
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.AuthModel = void 0;
7
+ function _database() {
8
+ const data = require("@nocobase/database");
9
+ _database = function _database() {
10
+ return data;
11
+ };
12
+ return data;
13
+ }
14
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
15
+ function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
16
+ class AuthModel extends _database().Model {
17
+ findUser(uuid) {
18
+ var _this = this;
19
+ return _asyncToGenerator(function* () {
20
+ let user;
21
+ const users = yield _this.getUsers({
22
+ through: {
23
+ where: {
24
+ uuid
25
+ }
26
+ }
27
+ });
28
+ if (users.length) {
29
+ user = users[0];
30
+ return user;
31
+ }
32
+ })();
33
+ }
34
+ newUser(uuid, values) {
35
+ var _this2 = this;
36
+ return _asyncToGenerator(function* () {
37
+ let user;
38
+ const db = _this2.constructor.database;
39
+ yield _this2.sequelize.transaction( /*#__PURE__*/function () {
40
+ var _ref = _asyncToGenerator(function* (transaction) {
41
+ // Create a new user if not exists
42
+ user = yield _this2.createUser(values || {
43
+ nickname: uuid
44
+ }, {
45
+ through: {
46
+ uuid: uuid
47
+ },
48
+ transaction
49
+ });
50
+ yield db.emitAsync(`users.afterCreateWithAssociations`, user, {
51
+ transaction
52
+ });
53
+ });
54
+ return function (_x) {
55
+ return _ref.apply(this, arguments);
56
+ };
57
+ }());
58
+ return user;
59
+ })();
60
+ }
61
+ findOrCreateUser(uuid, userValues) {
62
+ var _this3 = this;
63
+ return _asyncToGenerator(function* () {
64
+ const user = yield _this3.findUser(uuid);
65
+ if (user) {
66
+ return user;
67
+ }
68
+ return yield _this3.newUser(uuid, userValues);
69
+ })();
70
+ }
71
+ }
72
+ exports.AuthModel = AuthModel;
@@ -0,0 +1,11 @@
1
+ import { InstallOptions, Plugin } from '@nocobase/server';
2
+ export declare class AuthPlugin extends Plugin {
3
+ afterAdd(): void;
4
+ beforeLoad(): Promise<void>;
5
+ load(): Promise<void>;
6
+ install(options?: InstallOptions): Promise<void>;
7
+ afterEnable(): Promise<void>;
8
+ afterDisable(): Promise<void>;
9
+ remove(): Promise<void>;
10
+ }
11
+ export default AuthPlugin;
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = exports.AuthPlugin = void 0;
7
+ function _server() {
8
+ const data = require("@nocobase/server");
9
+ _server = function _server() {
10
+ return data;
11
+ };
12
+ return data;
13
+ }
14
+ function _path() {
15
+ const data = require("path");
16
+ _path = function _path() {
17
+ return data;
18
+ };
19
+ return data;
20
+ }
21
+ var _basicAuth = require("./basic-auth");
22
+ var _preset = require("../preset");
23
+ var _auth = _interopRequireDefault(require("./actions/auth"));
24
+ var _authenticators = _interopRequireDefault(require("./actions/authenticators"));
25
+ var _locale = require("./locale");
26
+ var _authenticator = require("./model/authenticator");
27
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
28
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
29
+ function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
30
+ class AuthPlugin extends _server().Plugin {
31
+ afterAdd() {}
32
+ beforeLoad() {
33
+ var _this = this;
34
+ return _asyncToGenerator(function* () {
35
+ _this.app.i18n.addResources('zh-CN', _preset.namespace, _locale.zhCN);
36
+ _this.app.i18n.addResources('en-US', _preset.namespace, _locale.enUS);
37
+ _this.app.db.registerModels({
38
+ AuthModel: _authenticator.AuthModel
39
+ });
40
+ })();
41
+ }
42
+ load() {
43
+ var _this2 = this;
44
+ return _asyncToGenerator(function* () {
45
+ // Set up database
46
+ yield _this2.db.import({
47
+ directory: (0, _path().resolve)(__dirname, 'collections')
48
+ });
49
+ _this2.db.addMigrations({
50
+ namespace: 'auth',
51
+ directory: (0, _path().resolve)(__dirname, 'migrations'),
52
+ context: {
53
+ plugin: _this2
54
+ }
55
+ });
56
+ // Set up auth manager and register preset auth type
57
+ _this2.app.authManager.setStorer({
58
+ get: function () {
59
+ var _get = _asyncToGenerator(function* (name) {
60
+ const repo = _this2.db.getRepository('authenticators');
61
+ const authenticators = yield repo.find({
62
+ filter: {
63
+ enabled: true
64
+ }
65
+ });
66
+ const authenticator = authenticators.find(authenticator => authenticator.name === name);
67
+ return authenticator || authenticators[0];
68
+ });
69
+ function get(_x) {
70
+ return _get.apply(this, arguments);
71
+ }
72
+ return get;
73
+ }()
74
+ });
75
+ _this2.app.authManager.registerTypes(_preset.presetAuthType, {
76
+ auth: _basicAuth.BasicAuth
77
+ });
78
+ // Register actions
79
+ Object.entries(_auth.default).forEach(([action, handler]) => _this2.app.resourcer.registerAction(`auth:${action}`, handler));
80
+ Object.entries(_authenticators.default).forEach(([action, handler]) => _this2.app.resourcer.registerAction(`authenticators:${action}`, handler));
81
+ // Set up ACL
82
+ ['check', 'signIn', 'signUp'].forEach(action => _this2.app.acl.allow('auth', action));
83
+ ['signOut', 'changePassword'].forEach(action => _this2.app.acl.allow('auth', action, 'loggedIn'));
84
+ _this2.app.acl.allow('authenticators', 'publicList');
85
+ _this2.app.acl.registerSnippet({
86
+ name: `pm.${_this2.name}.authenticators`,
87
+ actions: ['authenticators:*']
88
+ });
89
+ })();
90
+ }
91
+ install(options) {
92
+ var _this3 = this;
93
+ return _asyncToGenerator(function* () {
94
+ const repository = _this3.db.getRepository('authenticators');
95
+ const exist = yield repository.findOne({
96
+ filter: {
97
+ name: _preset.presetAuthenticator
98
+ }
99
+ });
100
+ if (exist) {
101
+ return;
102
+ }
103
+ yield repository.create({
104
+ values: {
105
+ name: _preset.presetAuthenticator,
106
+ authType: _preset.presetAuthType,
107
+ description: 'Sign in with email and password.',
108
+ enabled: true,
109
+ options: {
110
+ public: {
111
+ allowSignUp: true
112
+ }
113
+ }
114
+ }
115
+ });
116
+ })();
117
+ }
118
+ afterEnable() {
119
+ return _asyncToGenerator(function* () {})();
120
+ }
121
+ afterDisable() {
122
+ return _asyncToGenerator(function* () {})();
123
+ }
124
+ remove() {
125
+ return _asyncToGenerator(function* () {})();
126
+ }
127
+ }
128
+ exports.AuthPlugin = AuthPlugin;
129
+ var _default = AuthPlugin;
130
+ exports.default = _default;
package/package.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "@nocobase/plugin-auth",
3
+ "version": "0.10.0-alpha.2",
4
+ "main": "lib/server/index.js",
5
+ "devDependencies": {
6
+ "@nocobase/actions": "0.10.0-alpha.2",
7
+ "@nocobase/client": "0.10.0-alpha.2",
8
+ "@nocobase/database": "0.10.0-alpha.2",
9
+ "@nocobase/server": "0.10.0-alpha.2",
10
+ "@nocobase/test": "0.10.0-alpha.2"
11
+ },
12
+ "displayName": "Authentication",
13
+ "displayName.zh-CN": "用户认证",
14
+ "description": "Basic authentication and authenticator management.",
15
+ "description.zh-CN": "提供基础认证功能和扩展认证器管理功能。",
16
+ "gitHead": "85028ae1733fcbd46ecd5d291dacbdc175f7f073"
17
+ }
package/server.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ // @ts-nocheck
2
+ export * from './lib/server';
3
+ export { default } from './lib/server';
package/server.js ADDED
@@ -0,0 +1,65 @@
1
+ 'use strict';
2
+
3
+ function _getRequireWildcardCache(nodeInterop) {
4
+ if (typeof WeakMap !== 'function') return null;
5
+ var cacheBabelInterop = new WeakMap();
6
+ var cacheNodeInterop = new WeakMap();
7
+ return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) {
8
+ return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
9
+ })(nodeInterop);
10
+ }
11
+
12
+ function _interopRequireWildcard(obj, nodeInterop) {
13
+ if (!nodeInterop && obj && obj.__esModule) {
14
+ return obj;
15
+ }
16
+ if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) {
17
+ return { default: obj };
18
+ }
19
+ var cache = _getRequireWildcardCache(nodeInterop);
20
+ if (cache && cache.has(obj)) {
21
+ return cache.get(obj);
22
+ }
23
+ var newObj = {};
24
+ var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
25
+ for (var key in obj) {
26
+ if (key !== 'default' && Object.prototype.hasOwnProperty.call(obj, key)) {
27
+ var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
28
+ if (desc && (desc.get || desc.set)) {
29
+ Object.defineProperty(newObj, key, desc);
30
+ } else {
31
+ newObj[key] = obj[key];
32
+ }
33
+ }
34
+ }
35
+ newObj.default = obj;
36
+ if (cache) {
37
+ cache.set(obj, newObj);
38
+ }
39
+ return newObj;
40
+ }
41
+
42
+ var _index = _interopRequireWildcard(require('./lib/server'));
43
+
44
+ Object.defineProperty(exports, '__esModule', {
45
+ value: true,
46
+ });
47
+ var _exportNames = {};
48
+ Object.defineProperty(exports, 'default', {
49
+ enumerable: true,
50
+ get: function get() {
51
+ return _index.default;
52
+ },
53
+ });
54
+
55
+ Object.keys(_index).forEach(function (key) {
56
+ if (key === 'default' || key === '__esModule') return;
57
+ if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
58
+ if (key in exports && exports[key] === _index[key]) return;
59
+ Object.defineProperty(exports, key, {
60
+ enumerable: true,
61
+ get: function get() {
62
+ return _index[key];
63
+ },
64
+ });
65
+ });
@@ -0,0 +1,31 @@
1
+ import { SchemaComponent } from '@nocobase/client';
2
+ import React from 'react';
3
+ import { useAuthTranslation } from '../locale';
4
+
5
+ export const Options = () => {
6
+ const { t } = useAuthTranslation();
7
+ return (
8
+ <SchemaComponent
9
+ scope={{ t }}
10
+ schema={{
11
+ type: 'object',
12
+ properties: {
13
+ public: {
14
+ type: 'object',
15
+ properties: {
16
+ allowSignup: {
17
+ 'x-decorator': 'FormItem',
18
+ type: 'boolean',
19
+ title: '{{t("Allow to sign up")}}',
20
+ 'x-component': 'Checkbox',
21
+ 'x-component-props': {
22
+ defaultChecked: true,
23
+ },
24
+ },
25
+ },
26
+ },
27
+ },
28
+ }}
29
+ />
30
+ );
31
+ };
@@ -0,0 +1,65 @@
1
+ import { Authenticator, SchemaComponent, SignupPageContext, useSignIn } from '@nocobase/client';
2
+ import { ISchema } from '@formily/react';
3
+ import React, { useContext } from 'react';
4
+
5
+ const passwordForm: ISchema = {
6
+ type: 'object',
7
+ name: 'passwordForm',
8
+ 'x-component': 'FormV2',
9
+ properties: {
10
+ email: {
11
+ type: 'string',
12
+ required: true,
13
+ 'x-component': 'Input',
14
+ 'x-validator': 'email',
15
+ 'x-decorator': 'FormItem',
16
+ 'x-component-props': { placeholder: '{{t("Email")}}', style: {} },
17
+ },
18
+ password: {
19
+ type: 'string',
20
+ 'x-component': 'Password',
21
+ required: true,
22
+ 'x-decorator': 'FormItem',
23
+ 'x-component-props': { placeholder: '{{t("Password")}}', style: {} },
24
+ },
25
+ actions: {
26
+ type: 'void',
27
+ 'x-component': 'div',
28
+ properties: {
29
+ submit: {
30
+ title: '{{t("Sign in")}}',
31
+ type: 'void',
32
+ 'x-component': 'Action',
33
+ 'x-component-props': {
34
+ htmlType: 'submit',
35
+ block: true,
36
+ type: 'primary',
37
+ useAction: `{{ useBasicSignIn }}`,
38
+ style: { width: '100%' },
39
+ },
40
+ },
41
+ },
42
+ },
43
+ signup: {
44
+ type: 'void',
45
+ 'x-component': 'Link',
46
+ 'x-component-props': {
47
+ to: '{{ signupLink }}',
48
+ },
49
+ 'x-content': '{{t("Create an account")}}',
50
+ 'x-visible': '{{ allowSignUp }}',
51
+ },
52
+ },
53
+ };
54
+ export default (props: { authenticator: Authenticator }) => {
55
+ const authenticator = props.authenticator;
56
+ const { authType, name, options } = authenticator;
57
+ const signupPages = useContext(SignupPageContext);
58
+ const allowSignUp = !!signupPages[authType] && options?.allowSignup;
59
+ const signupLink = `/signup?authType=${authType}&name=${name}`;
60
+
61
+ const useBasicSignIn = () => {
62
+ return useSignIn(name);
63
+ };
64
+ return <SchemaComponent schema={passwordForm} scope={{ useBasicSignIn, allowSignUp, signupLink }} />;
65
+ };
@@ -0,0 +1,91 @@
1
+ import { SchemaComponent, useSignup } from '@nocobase/client';
2
+ import { ISchema } from '@formily/react';
3
+ import React from 'react';
4
+ import { uid } from '@formily/shared';
5
+
6
+ const signupPageSchema: ISchema = {
7
+ type: 'object',
8
+ name: uid(),
9
+ 'x-component': 'FormV2',
10
+ properties: {
11
+ email: {
12
+ type: 'string',
13
+ required: true,
14
+ 'x-component': 'Input',
15
+ 'x-validator': 'email',
16
+ 'x-decorator': 'FormItem',
17
+ 'x-component-props': { placeholder: '{{t("Email")}}', style: {} },
18
+ },
19
+ password: {
20
+ type: 'string',
21
+ required: true,
22
+ 'x-component': 'Password',
23
+ 'x-decorator': 'FormItem',
24
+ 'x-component-props': { placeholder: '{{t("Password")}}', checkStrength: true, style: {} },
25
+ 'x-reactions': [
26
+ {
27
+ dependencies: ['.confirm_password'],
28
+ fulfill: {
29
+ state: {
30
+ selfErrors: '{{$deps[0] && $self.value && $self.value !== $deps[0] ? t("Password mismatch") : ""}}',
31
+ },
32
+ },
33
+ },
34
+ ],
35
+ },
36
+ confirm_password: {
37
+ type: 'string',
38
+ required: true,
39
+ 'x-component': 'Password',
40
+ 'x-decorator': 'FormItem',
41
+ 'x-component-props': { placeholder: '{{t("Confirm password")}}', style: {} },
42
+ 'x-reactions': [
43
+ {
44
+ dependencies: ['.password'],
45
+ fulfill: {
46
+ state: {
47
+ selfErrors: '{{$deps[0] && $self.value && $self.value !== $deps[0] ? t("Password mismatch") : ""}}',
48
+ },
49
+ },
50
+ },
51
+ ],
52
+ },
53
+ actions: {
54
+ type: 'void',
55
+ 'x-component': 'div',
56
+ properties: {
57
+ submit: {
58
+ title: '{{t("Sign up")}}',
59
+ type: 'void',
60
+ 'x-component': 'Action',
61
+ 'x-component-props': {
62
+ block: true,
63
+ type: 'primary',
64
+ htmlType: 'submit',
65
+ useAction: '{{ useBasicSignup }}',
66
+ style: { width: '100%' },
67
+ },
68
+ },
69
+ },
70
+ },
71
+ link: {
72
+ type: 'void',
73
+ 'x-component': 'div',
74
+ properties: {
75
+ link: {
76
+ type: 'void',
77
+ 'x-component': 'Link',
78
+ 'x-component-props': { to: '/signin' },
79
+ 'x-content': '{{t("Log in with an existing account")}}',
80
+ },
81
+ },
82
+ },
83
+ },
84
+ };
85
+
86
+ export default (props: { name: string }) => {
87
+ const useBasicSignup = () => {
88
+ return useSignup({ authenticator: props.name });
89
+ };
90
+ return <SchemaComponent schema={signupPageSchema} scope={{ useBasicSignup }} />;
91
+ };
@@ -0,0 +1,41 @@
1
+ import {
2
+ OptionsComponentProvider,
3
+ SettingsCenterProvider,
4
+ SigninPageProvider,
5
+ SignupPageProvider,
6
+ } from '@nocobase/client';
7
+ import React from 'react';
8
+ import { Authenticator } from './settings/Authenticator';
9
+ import SigninPage from './basic/SigninPage';
10
+ import { presetAuthType } from '../preset';
11
+ import SignupPage from './basic/SignupPage';
12
+ import { useAuthTranslation } from './locale';
13
+ import { Options } from './basic/Options';
14
+
15
+ export default (props) => {
16
+ const { t } = useAuthTranslation();
17
+ return (
18
+ <SettingsCenterProvider
19
+ settings={{
20
+ auth: {
21
+ title: t('Authentication'),
22
+ icon: 'LoginOutlined',
23
+ tabs: {
24
+ authenticators: {
25
+ title: t('Authenticators'),
26
+ component: () => <Authenticator />,
27
+ },
28
+ },
29
+ },
30
+ }}
31
+ >
32
+ <OptionsComponentProvider authType={presetAuthType} component={Options}>
33
+ <SigninPageProvider authType={presetAuthType} tabTitle={t('Sign in via email')} component={SigninPage}>
34
+ <SignupPageProvider authType={presetAuthType} component={SignupPage}>
35
+ {props.children}
36
+ </SignupPageProvider>
37
+ </SigninPageProvider>
38
+ </OptionsComponentProvider>
39
+ </SettingsCenterProvider>
40
+ );
41
+ };
@@ -0,0 +1,7 @@
1
+ import { useTranslation } from 'react-i18next';
2
+
3
+ export const NAMESPACE = 'auth';
4
+
5
+ export function useAuthTranslation() {
6
+ return useTranslation(NAMESPACE);
7
+ }
@@ -0,0 +1,10 @@
1
+ const locale = {
2
+ 'Auth Type': '认证类型',
3
+ Authenticators: '认证器',
4
+ Authentication: '用户认证',
5
+ 'Sign in via email': '邮箱登录',
6
+ 'Not allowed to sign up': '禁止注册',
7
+ 'Allow to sign up': '允许注册',
8
+ };
9
+
10
+ export default locale;
@@ -0,0 +1,95 @@
1
+ import {
2
+ ActionContextProvider,
3
+ SchemaComponent,
4
+ useAPIClient,
5
+ useActionContext,
6
+ useAsyncData,
7
+ useRequest,
8
+ } from '@nocobase/client';
9
+ import { Card } from 'antd';
10
+ import React, { useState } from 'react';
11
+ import { authenticatorsSchema, createFormSchema } from './schemas/authenticators';
12
+ import { Button, Dropdown } from 'antd';
13
+ import { PlusOutlined, DownOutlined } from '@ant-design/icons';
14
+ import { AuthTypeContext, AuthTypesContext, useAuthTypes } from './authType';
15
+ import { useValuesFromOptions, Options } from './Options';
16
+ import { useTranslation } from 'react-i18next';
17
+
18
+ const useCloseAction = () => {
19
+ const { setVisible } = useActionContext();
20
+ return {
21
+ async run() {
22
+ setVisible(false);
23
+ },
24
+ };
25
+ };
26
+
27
+ const AddNew = () => {
28
+ const { t } = useTranslation();
29
+ const [visible, setVisible] = useState(false);
30
+ const [type, setType] = useState('');
31
+ const types = useAuthTypes();
32
+ const items = types.map((item) => ({
33
+ ...item,
34
+ onClick: () => {
35
+ setVisible(true);
36
+ setType(item.value);
37
+ },
38
+ }));
39
+
40
+ return (
41
+ <ActionContextProvider value={{ visible, setVisible }}>
42
+ <AuthTypeContext.Provider value={{ type }}>
43
+ <Dropdown menu={{ items }}>
44
+ <Button icon={<PlusOutlined />} type={'primary'}>
45
+ {t('Add new')} <DownOutlined />
46
+ </Button>
47
+ </Dropdown>
48
+ <SchemaComponent scope={{ useCloseAction, types, setType }} schema={createFormSchema} />
49
+ </AuthTypeContext.Provider>
50
+ </ActionContextProvider>
51
+ );
52
+ };
53
+
54
+ // Disable delete button when there is only one authenticator
55
+ const useCanNotDelete = () => {
56
+ const { data } = useAsyncData();
57
+ // return data?.meta?.count === 1;
58
+ return false;
59
+ };
60
+
61
+ export const Authenticator = () => {
62
+ const [types, setTypes] = useState([]);
63
+ const api = useAPIClient();
64
+ useRequest(
65
+ () =>
66
+ api
67
+ .resource('authenticators')
68
+ .listTypes()
69
+ .then((res) => {
70
+ const types = res?.data?.data || [];
71
+ return types.map((type: string) => ({
72
+ key: type,
73
+ label: type,
74
+ value: type,
75
+ }));
76
+ }),
77
+ {
78
+ onSuccess: (types) => {
79
+ setTypes(types);
80
+ },
81
+ },
82
+ );
83
+
84
+ return (
85
+ <Card bordered={false}>
86
+ <AuthTypesContext.Provider value={{ types }}>
87
+ <SchemaComponent
88
+ schema={authenticatorsSchema}
89
+ components={{ AddNew, Options }}
90
+ scope={{ types, useValuesFromOptions, useCanNotDelete }}
91
+ />
92
+ </AuthTypesContext.Provider>
93
+ </Card>
94
+ );
95
+ };
@@ -0,0 +1,35 @@
1
+ import { useRequest, useRecord, useActionContext } from '@nocobase/client';
2
+ import { useEffect } from 'react';
3
+ import { useOptionsComponent } from '@nocobase/client';
4
+ import { observer, useForm } from '@formily/react';
5
+
6
+ export const useValuesFromOptions = (options) => {
7
+ const record = useRecord();
8
+ const result = useRequest(
9
+ () =>
10
+ Promise.resolve({
11
+ data: {
12
+ ...record.options,
13
+ },
14
+ }),
15
+ {
16
+ ...options,
17
+ manual: true,
18
+ },
19
+ );
20
+ const { run } = result;
21
+ const ctx = useActionContext();
22
+ useEffect(() => {
23
+ if (ctx.visible) {
24
+ run();
25
+ }
26
+ }, [ctx.visible, run]);
27
+ return result;
28
+ };
29
+
30
+ export const Options = observer(() => {
31
+ const form = useForm();
32
+ const record = useRecord();
33
+ const component = useOptionsComponent(form.values.authType || record.authType);
34
+ return component;
35
+ });