@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,18 @@
1
+ import { createContext, useContext } from 'react';
2
+
3
+ export const AuthTypeContext = createContext<{
4
+ type: string;
5
+ }>({ type: '' });
6
+
7
+ export const AuthTypesContext = createContext<{
8
+ types: {
9
+ key: string;
10
+ label: string;
11
+ value: string;
12
+ }[];
13
+ }>({ types: [] });
14
+
15
+ export const useAuthTypes = () => {
16
+ const { types } = useContext(AuthTypesContext);
17
+ return types;
18
+ };
@@ -0,0 +1,402 @@
1
+ import { uid } from '@formily/shared';
2
+ import { ISchema } from '@formily/react';
3
+ import { useAPIClient, useActionContext, useRequest } from '@nocobase/client';
4
+ import { useContext } from 'react';
5
+ import { AuthTypeContext } from '../authType';
6
+ import { message } from 'antd';
7
+ import { useTranslation } from 'react-i18next';
8
+
9
+ const collection = {
10
+ name: 'authenticators',
11
+ sortable: true,
12
+ fields: [
13
+ {
14
+ name: 'id',
15
+ type: 'string',
16
+ interface: 'id',
17
+ },
18
+ {
19
+ interface: 'input',
20
+ type: 'string',
21
+ name: 'name',
22
+ uiSchema: {
23
+ type: 'string',
24
+ title: '{{t("Name")}}',
25
+ 'x-component': 'Input',
26
+ required: true,
27
+ },
28
+ },
29
+ {
30
+ interface: 'input',
31
+ type: 'string',
32
+ name: 'authType',
33
+ uiSchema: {
34
+ type: 'string',
35
+ title: '{{t("Auth Type", {ns: "auth"})}}',
36
+ 'x-component': 'Select',
37
+ required: true,
38
+ },
39
+ },
40
+ {
41
+ interface: 'input',
42
+ type: 'string',
43
+ name: 'title',
44
+ uiSchema: {
45
+ type: 'string',
46
+ title: '{{t("Title")}}',
47
+ 'x-component': 'Input',
48
+ },
49
+ },
50
+ {
51
+ interface: 'textarea',
52
+ type: 'string',
53
+ name: 'description',
54
+ uiSchema: {
55
+ type: 'string',
56
+ title: '{{t("Description")}}',
57
+ 'x-component': 'Input',
58
+ },
59
+ },
60
+ {
61
+ type: 'boolean',
62
+ name: 'enabled',
63
+ uiSchema: {
64
+ type: 'boolean',
65
+ title: '{{t("Enabled")}}',
66
+ 'x-component': 'Checkbox',
67
+ },
68
+ },
69
+ ],
70
+ };
71
+
72
+ export const createFormSchema: ISchema = {
73
+ type: 'object',
74
+ properties: {
75
+ drawer: {
76
+ type: 'void',
77
+ 'x-component': 'Action.Drawer',
78
+ 'x-decorator': 'Form',
79
+ 'x-decorator-props': {
80
+ useValues(options) {
81
+ const ctx = useActionContext();
82
+ const { type: authType } = useContext(AuthTypeContext);
83
+ return useRequest(
84
+ () =>
85
+ Promise.resolve({
86
+ data: {
87
+ name: `s_${uid()}`,
88
+ authType,
89
+ },
90
+ }),
91
+ { ...options, refreshDeps: [ctx.visible] },
92
+ );
93
+ },
94
+ },
95
+ title: '{{t("Add new")}}',
96
+ properties: {
97
+ name: {
98
+ 'x-component': 'CollectionField',
99
+ 'x-decorator': 'FormItem',
100
+ },
101
+ authType: {
102
+ 'x-component': 'CollectionField',
103
+ 'x-decorator': 'FormItem',
104
+ 'x-component-props': {
105
+ options: '{{ types }}',
106
+ },
107
+ },
108
+ title: {
109
+ 'x-component': 'CollectionField',
110
+ 'x-decorator': 'FormItem',
111
+ },
112
+ description: {
113
+ 'x-component': 'CollectionField',
114
+ 'x-decorator': 'FormItem',
115
+ },
116
+ enabled: {
117
+ 'x-component': 'CollectionField',
118
+ 'x-decorator': 'FormItem',
119
+ },
120
+ options: {
121
+ type: 'object',
122
+ 'x-component': 'Options',
123
+ },
124
+ footer: {
125
+ type: 'void',
126
+ 'x-component': 'Action.Drawer.Footer',
127
+ properties: {
128
+ cancel: {
129
+ title: '{{t("Cancel")}}',
130
+ 'x-component': 'Action',
131
+ 'x-component-props': {
132
+ useAction: '{{ cm.useCancelAction }}',
133
+ },
134
+ },
135
+ submit: {
136
+ title: '{{t("Submit")}}',
137
+ 'x-component': 'Action',
138
+ 'x-component-props': {
139
+ type: 'primary',
140
+ useAction: '{{ cm.useCreateAction }}',
141
+ },
142
+ },
143
+ },
144
+ },
145
+ },
146
+ },
147
+ },
148
+ };
149
+
150
+ export const authenticatorsSchema: ISchema = {
151
+ type: 'void',
152
+ name: 'authenticators',
153
+ 'x-decorator': 'ResourceActionProvider',
154
+ 'x-decorator-props': {
155
+ collection,
156
+ resourceName: 'authenticators',
157
+ dragSort: true,
158
+ request: {
159
+ resource: 'authenticators',
160
+ action: 'list',
161
+ params: {
162
+ pageSize: 50,
163
+ sort: 'sort',
164
+ appends: [],
165
+ },
166
+ },
167
+ },
168
+ 'x-component': 'CollectionProvider',
169
+ 'x-component-props': {
170
+ collection,
171
+ },
172
+ properties: {
173
+ actions: {
174
+ type: 'void',
175
+ 'x-component': 'ActionBar',
176
+ 'x-component-props': {
177
+ style: {
178
+ marginBottom: 16,
179
+ },
180
+ },
181
+ properties: {
182
+ delete: {
183
+ type: 'void',
184
+ title: '{{t("Delete")}}',
185
+ 'x-component': 'Action',
186
+ 'x-component-props': {
187
+ useAction: '{{ cm.useBulkDestroyAction }}',
188
+ confirm: {
189
+ title: "{{t('Delete')}}",
190
+ content: "{{t('Are you sure you want to delete it?')}}",
191
+ },
192
+ },
193
+ },
194
+ create: {
195
+ type: 'void',
196
+ title: '{{t("Add new")}}',
197
+ 'x-component': 'AddNew',
198
+ 'x-component-props': {
199
+ type: 'primary',
200
+ },
201
+ },
202
+ },
203
+ },
204
+ table: {
205
+ type: 'void',
206
+ 'x-uid': 'input',
207
+ 'x-component': 'Table.Void',
208
+ 'x-component-props': {
209
+ rowKey: 'id',
210
+ rowSelection: {
211
+ type: 'checkbox',
212
+ },
213
+ useDataSource: '{{ cm.useDataSourceFromRAC }}',
214
+ useAction() {
215
+ const api = useAPIClient();
216
+ const { t } = useTranslation();
217
+ return {
218
+ async move(from, to) {
219
+ await api.resource('authenticators').move({
220
+ sourceId: from.id,
221
+ targetId: to.id,
222
+ });
223
+ message.success(t('Saved successfully'), 0.2);
224
+ },
225
+ };
226
+ },
227
+ },
228
+ properties: {
229
+ id: {
230
+ type: 'void',
231
+ 'x-decorator': 'Table.Column.Decorator',
232
+ 'x-component': 'Table.Column',
233
+ properties: {
234
+ id: {
235
+ type: 'number',
236
+ 'x-component': 'CollectionField',
237
+ 'x-read-pretty': true,
238
+ },
239
+ },
240
+ },
241
+ name: {
242
+ type: 'void',
243
+ 'x-decorator': 'Table.Column.Decorator',
244
+ 'x-component': 'Table.Column',
245
+ properties: {
246
+ name: {
247
+ type: 'string',
248
+ 'x-component': 'CollectionField',
249
+ 'x-read-pretty': true,
250
+ },
251
+ },
252
+ },
253
+ authType: {
254
+ type: 'void',
255
+ 'x-decorator': 'Table.Column.Decorator',
256
+ 'x-component': 'Table.Column',
257
+ properties: {
258
+ authType: {
259
+ type: 'string',
260
+ 'x-component': 'CollectionField',
261
+ 'x-read-pretty': true,
262
+ },
263
+ },
264
+ },
265
+ title: {
266
+ type: 'void',
267
+ 'x-decorator': 'Table.Column.Decorator',
268
+ 'x-component': 'Table.Column',
269
+ properties: {
270
+ title: {
271
+ type: 'string',
272
+ 'x-component': 'CollectionField',
273
+ 'x-read-pretty': true,
274
+ },
275
+ },
276
+ },
277
+ description: {
278
+ type: 'void',
279
+ 'x-decorator': 'Table.Column.Decorator',
280
+ 'x-component': 'Table.Column',
281
+ properties: {
282
+ description: {
283
+ type: 'boolean',
284
+ 'x-component': 'CollectionField',
285
+ 'x-read-pretty': true,
286
+ },
287
+ },
288
+ },
289
+ enabled: {
290
+ type: 'void',
291
+ 'x-decorator': 'Table.Column.Decorator',
292
+ 'x-component': 'Table.Column',
293
+ properties: {
294
+ enabled: {
295
+ type: 'boolean',
296
+ 'x-component': 'CollectionField',
297
+ 'x-read-pretty': true,
298
+ },
299
+ },
300
+ },
301
+ actions: {
302
+ type: 'void',
303
+ title: '{{t("Actions")}}',
304
+ 'x-component': 'Table.Column',
305
+ properties: {
306
+ actions: {
307
+ type: 'void',
308
+ 'x-component': 'Space',
309
+ 'x-component-props': {
310
+ split: '|',
311
+ },
312
+ properties: {
313
+ update: {
314
+ type: 'void',
315
+ title: '{{t("Configure")}}',
316
+ 'x-component': 'Action.Link',
317
+ 'x-component-props': {
318
+ type: 'primary',
319
+ },
320
+ properties: {
321
+ drawer: {
322
+ type: 'void',
323
+ 'x-component': 'Action.Drawer',
324
+ 'x-decorator': 'Form',
325
+ 'x-decorator-props': {
326
+ useValues: '{{ cm.useValuesFromRecord }}',
327
+ },
328
+ title: '{{t("Configure")}}',
329
+ properties: {
330
+ name: {
331
+ 'x-component': 'CollectionField',
332
+ 'x-decorator': 'FormItem',
333
+ },
334
+ authType: {
335
+ 'x-component': 'CollectionField',
336
+ 'x-decorator': 'FormItem',
337
+ 'x-component-props': {
338
+ options: '{{ types }}',
339
+ },
340
+ },
341
+ title: {
342
+ 'x-component': 'CollectionField',
343
+ 'x-decorator': 'FormItem',
344
+ },
345
+ description: {
346
+ 'x-component': 'CollectionField',
347
+ 'x-decorator': 'FormItem',
348
+ },
349
+ enabled: {
350
+ 'x-component': 'CollectionField',
351
+ 'x-decorator': 'FormItem',
352
+ },
353
+ options: {
354
+ type: 'object',
355
+ 'x-component': 'Options',
356
+ },
357
+ footer: {
358
+ type: 'void',
359
+ 'x-component': 'Action.Drawer.Footer',
360
+ properties: {
361
+ cancel: {
362
+ title: '{{t("Cancel")}}',
363
+ 'x-component': 'Action',
364
+ 'x-component-props': {
365
+ useAction: '{{ cm.useCancelAction }}',
366
+ },
367
+ },
368
+ submit: {
369
+ title: '{{t("Submit")}}',
370
+ 'x-component': 'Action',
371
+ 'x-component-props': {
372
+ type: 'primary',
373
+ useAction: '{{ cm.useUpdateAction }}',
374
+ },
375
+ },
376
+ },
377
+ },
378
+ },
379
+ },
380
+ },
381
+ },
382
+ delete: {
383
+ type: 'void',
384
+ title: '{{ t("Delete") }}',
385
+ 'x-component': 'Action.Link',
386
+ 'x-component-props': {
387
+ confirm: {
388
+ title: "{{t('Delete record')}}",
389
+ content: "{{t('Are you sure you want to delete it?')}}",
390
+ },
391
+ useAction: '{{cm.useDestroyAction}}',
392
+ },
393
+ 'x-disabled': '{{ useCanNotDelete() }}',
394
+ },
395
+ },
396
+ },
397
+ },
398
+ },
399
+ },
400
+ },
401
+ },
402
+ };
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export { default, AuthModel } from './server';
package/src/preset.ts ADDED
@@ -0,0 +1,4 @@
1
+ export const presetAuthType = 'Email/Password';
2
+ export const presetAuthenticator = 'basic';
3
+
4
+ export const namespace = require('../package.json').name;
@@ -0,0 +1,142 @@
1
+ import Database, { Repository } from '@nocobase/database';
2
+ import { mockServer, MockServer } from '@nocobase/test';
3
+ import AuthPlugin from '../';
4
+ import UsersPlugin from '@nocobase/plugin-users';
5
+
6
+ describe('actions', () => {
7
+ describe('authenticators', () => {
8
+ let app: MockServer;
9
+ let db: Database;
10
+ let repo: Repository;
11
+ let agent;
12
+
13
+ beforeAll(async () => {
14
+ app = mockServer();
15
+ app.plugin(AuthPlugin);
16
+ await app.loadAndInstall({ clean: true });
17
+ db = app.db;
18
+ repo = db.getRepository('authenticators');
19
+
20
+ agent = app.agent();
21
+ });
22
+
23
+ afterEach(async () => {
24
+ await repo.destroy({
25
+ truncate: true,
26
+ });
27
+ });
28
+
29
+ afterAll(async () => {
30
+ await db.close();
31
+ });
32
+
33
+ it('should list authenticator types', async () => {
34
+ const res = await agent.resource('authenticators').listTypes();
35
+ expect(res.body.data).toEqual(['Email/Password']);
36
+ });
37
+
38
+ it('should return enabled authenticators with public options', async () => {
39
+ await repo.destroy({
40
+ truncate: true,
41
+ });
42
+ await repo.createMany({
43
+ records: [
44
+ { name: 'test', authType: 'testType', enabled: true, options: { public: { test: 1 }, private: { test: 2 } } },
45
+ { name: 'test2', authType: 'testType' },
46
+ ],
47
+ });
48
+ const res = await agent.resource('authenticators').publicList();
49
+ expect(res.body.data.length).toBe(1);
50
+ expect(res.body.data[0].name).toBe('test');
51
+ });
52
+
53
+ it('should keep at least one authenticator', async () => {
54
+ await repo.createMany({
55
+ records: [{ name: 'test', authType: 'testType', enabled: true }],
56
+ });
57
+ const res = await agent.resource('authenticators').destroy();
58
+ expect(res.statusCode).toBe(400);
59
+ expect(await repo.count()).toBe(1);
60
+ });
61
+
62
+ it('shoud enable at least one authenticator', async () => {
63
+ await repo.createMany({
64
+ records: [{ name: 'test', authType: 'testType', enabled: true }],
65
+ });
66
+ const res = await agent.resource('authenticators').update({
67
+ filterByTk: 1,
68
+ values: {
69
+ enabled: false,
70
+ },
71
+ });
72
+ expect(res.statusCode).toBe(400);
73
+ expect(await repo.count()).toBe(1);
74
+ });
75
+ });
76
+
77
+ describe('auth', () => {
78
+ let app: MockServer;
79
+ let db: Database;
80
+ let agent;
81
+
82
+ beforeAll(async () => {
83
+ app = mockServer();
84
+ process.env.INIT_ROOT_EMAIL = 'test@nocobase.com';
85
+ process.env.INIT_ROOT_PASSWORD = '123456';
86
+ process.env.INIT_ROOT_NICKNAME = 'Test';
87
+ app.plugin(AuthPlugin);
88
+ app.plugin(UsersPlugin, { name: 'users' });
89
+ await app.loadAndInstall({ clean: true });
90
+ db = app.db;
91
+ agent = app.agent();
92
+ });
93
+
94
+ afterAll(async () => {
95
+ await db.close();
96
+ });
97
+
98
+ it('should sign in with email and password', async () => {
99
+ let res = await agent.resource('auth').check();
100
+ expect(res.body.data.id).toBeUndefined();
101
+
102
+ res = await agent.post('/auth:signIn').set({ 'X-Authenticator': 'basic' }).send({
103
+ email: process.env.INIT_ROOT_EMAIL,
104
+ password: process.env.INIT_ROOT_PASSWORD,
105
+ });
106
+ expect(res.statusCode).toEqual(200);
107
+ const data = res.body.data;
108
+ const token = data.token;
109
+ expect(token).toBeDefined();
110
+
111
+ res = await agent.get('/auth:check').set({ Authorization: `Bearer ${token}`, 'X-Authenticator': 'basic' });
112
+ expect(res.body.data.id).toBeDefined();
113
+ });
114
+
115
+ it('should disable sign up', async () => {
116
+ let res = await agent.post('/auth:signUp').set({ 'X-Authenticator': 'basic' }).send({
117
+ email: 'new',
118
+ password: 'new',
119
+ });
120
+ expect(res.statusCode).toEqual(200);
121
+
122
+ const repo = db.getRepository('authenticators');
123
+ await repo.update({
124
+ filter: {
125
+ name: 'basic',
126
+ },
127
+ values: {
128
+ options: {
129
+ public: {
130
+ allowSignUp: false,
131
+ },
132
+ },
133
+ },
134
+ });
135
+ res = await agent.post('/auth:signUp').set({ 'X-Authenticator': 'basic' }).send({
136
+ email: process.env.INIT_ROOT_EMAIL,
137
+ password: process.env.INIT_ROOT_PASSWORD,
138
+ });
139
+ expect(res.statusCode).toEqual(403);
140
+ });
141
+ });
142
+ });
@@ -0,0 +1,20 @@
1
+ import { Context, Next } from '@nocobase/actions';
2
+
3
+ export default {
4
+ lostPassword: async (ctx: Context, next: Next) => {
5
+ ctx.body = await ctx.auth.lostPassword();
6
+ await next();
7
+ },
8
+ resetPassword: async (ctx: Context, next: Next) => {
9
+ ctx.body = await ctx.auth.resetPassword();
10
+ await next();
11
+ },
12
+ getUserByResetToken: async (ctx: Context, next: Next) => {
13
+ ctx.body = await ctx.auth.getUserByResetToken();
14
+ await next();
15
+ },
16
+ changePassword: async (ctx: Context, next: Next) => {
17
+ ctx.body = await ctx.auth.changePassword();
18
+ await next();
19
+ },
20
+ };
@@ -0,0 +1,85 @@
1
+ import { Context, Next } from '@nocobase/actions';
2
+ import { namespace } from '../../preset';
3
+ import { Model, Repository } from '@nocobase/database';
4
+
5
+ async function checkCount(repository: Repository, id: number) {
6
+ // TODO(yangqia): This is a temporary solution, may cause concurrency problem.
7
+ const count = await repository.count({
8
+ filter: {
9
+ enabled: true,
10
+ id: {
11
+ $ne: id,
12
+ },
13
+ },
14
+ });
15
+ if (count <= 0) {
16
+ throw new Error('Please keep and enable at least one authenticator');
17
+ }
18
+ }
19
+
20
+ export default {
21
+ listTypes: async (ctx: Context, next: Next) => {
22
+ ctx.body = ctx.app.authManager.listTypes();
23
+ await next();
24
+ },
25
+ publicList: async (ctx: Context, next: Next) => {
26
+ const repo = ctx.db.getRepository('authenticators');
27
+ const authenticators = await repo.find({
28
+ fields: ['name', 'authType', 'title', 'options', 'sort'],
29
+ filter: {
30
+ enabled: true,
31
+ },
32
+ sort: 'sort',
33
+ });
34
+ ctx.body = authenticators.map((authenticator: Model) => ({
35
+ name: authenticator.name,
36
+ authType: authenticator.authType,
37
+ title: authenticator.title,
38
+ options: authenticator.options?.public || {},
39
+ }));
40
+ await next();
41
+ },
42
+ destroy: async (ctx: Context, next: Next) => {
43
+ const repository = ctx.db.getRepository('authenticators');
44
+ const { filterByTk, filter } = ctx.action.params;
45
+ try {
46
+ await checkCount(repository, filterByTk);
47
+ } catch (err) {
48
+ ctx.throw(400, ctx.t(err.message, { ns: namespace }));
49
+ }
50
+ const instance = await repository.destroy({
51
+ filter,
52
+ filterByTk,
53
+ context: ctx,
54
+ });
55
+
56
+ ctx.body = instance;
57
+ await next();
58
+ },
59
+ update: async (ctx: Context, next: Next) => {
60
+ const repository = ctx.db.getRepository('authenticators');
61
+ const { forceUpdate, filterByTk, values, whitelist, blacklist, filter, updateAssociationValues } =
62
+ ctx.action.params;
63
+
64
+ if (!values.enabled) {
65
+ try {
66
+ await checkCount(repository, values.id);
67
+ } catch (err) {
68
+ ctx.throw(400, ctx.t(err.message, { ns: namespace }));
69
+ }
70
+ }
71
+
72
+ ctx.body = await repository.update({
73
+ filterByTk,
74
+ values,
75
+ whitelist,
76
+ blacklist,
77
+ filter,
78
+ updateAssociationValues,
79
+ context: ctx,
80
+ forceUpdate,
81
+ });
82
+
83
+ await next();
84
+ },
85
+ };