@formio/appserver 2.0.0-rc.7 → 2.0.0

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.
@@ -1,159 +1,6 @@
1
1
  declare const _default: {
2
- save: {
3
- readonly info: {
4
- name: string;
5
- title: string;
6
- description: string;
7
- priority: number;
8
- defaults: {
9
- handler: string[];
10
- method: string[];
11
- };
12
- access: {
13
- handler: boolean;
14
- method: boolean;
15
- };
16
- };
17
- mappingComponents(scope: import("@formio/appserver-types").ActionScope): Promise<any>;
18
- settingsForm(scope: import("@formio/appserver-types").ActionScope): Promise<any>;
19
- saveToForm(scope: import("@formio/appserver-types").ActionScope, resource: string): undefined;
20
- childSubmission(scope: import("@formio/appserver-types").ActionScope, req: import("@formio/appserver-types").SubmissionRequest, res: import("@formio/appserver-types").SubmissionResponse, submission: any): any;
21
- childResponse(scope: import("@formio/appserver-types").ActionScope, req: import("@formio/appserver-types").SubmissionRequest, res: import("@formio/appserver-types").SubmissionResponse): void;
22
- executor(scope: import("@formio/appserver-types").ActionScope): Promise<(req: import("@formio/appserver-types").SubmissionRequest, res: import("@formio/appserver-types").SubmissionResponse, next: import("express").NextFunction) => Promise<void>>;
23
- };
24
- login: {
25
- readonly info: {
26
- name: string;
27
- title: string;
28
- description: string;
29
- priority: number;
30
- defaults: {
31
- handler: string[];
32
- method: string[];
33
- };
34
- access: {
35
- handler: boolean;
36
- method: boolean;
37
- };
38
- };
39
- settingsForm(scope: import("@formio/appserver-types").ActionScope): Promise<({
40
- type: string;
41
- label: string;
42
- key: string;
43
- placeholder: string;
44
- dataSrc: string;
45
- valueProperty: string;
46
- data: {
47
- json: any;
48
- };
49
- multiple: boolean;
50
- validate: {
51
- required: boolean;
52
- };
53
- input?: undefined;
54
- description?: undefined;
55
- defaultValue?: undefined;
56
- suffix?: undefined;
57
- } | {
58
- type: string;
59
- input: boolean;
60
- label: string;
61
- key: string;
62
- placeholder: string;
63
- dataSrc: string;
64
- valueProperty: string;
65
- data: {
66
- json: any;
67
- };
68
- multiple: boolean;
69
- validate: {
70
- required: boolean;
71
- };
72
- description?: undefined;
73
- defaultValue?: undefined;
74
- suffix?: undefined;
75
- } | {
76
- type: string;
77
- key: string;
78
- input: boolean;
79
- label: string;
80
- description: string;
81
- defaultValue: string;
82
- placeholder?: undefined;
83
- dataSrc?: undefined;
84
- valueProperty?: undefined;
85
- data?: undefined;
86
- multiple?: undefined;
87
- validate?: undefined;
88
- suffix?: undefined;
89
- } | {
90
- type: string;
91
- key: string;
92
- input: boolean;
93
- label: string;
94
- description: string;
95
- defaultValue: string;
96
- suffix: string;
97
- placeholder?: undefined;
98
- dataSrc?: undefined;
99
- valueProperty?: undefined;
100
- data?: undefined;
101
- multiple?: undefined;
102
- validate?: undefined;
103
- })[]>;
104
- waitText(time: number): string;
105
- checkAttempts(scope: import("@formio/appserver-types").ActionScope, error: any, user: import("@formio/core").Submission): Promise<any>;
106
- executor(scope: import("@formio/appserver-types").ActionScope): Promise<(req: import("@formio/appserver-types").SubmissionRequest, res: import("@formio/appserver-types").SubmissionResponse, next: import("express").NextFunction) => Promise<void>>;
107
- };
108
- role: {
109
- readonly info: {
110
- name: string;
111
- title: string;
112
- description: string;
113
- priority: number;
114
- defaults: {
115
- handler: string[];
116
- method: string[];
117
- };
118
- access: {
119
- handler: boolean;
120
- method: boolean;
121
- };
122
- };
123
- settingsForm(scope: import("@formio/appserver-types").ActionScope): Promise<({
124
- type: string;
125
- input: boolean;
126
- label: string;
127
- key: string;
128
- placeholder: string;
129
- template: string;
130
- dataSrc: string;
131
- data: {
132
- json: string;
133
- };
134
- valueProperty: string;
135
- multiple: boolean;
136
- validate: {
137
- required: boolean;
138
- };
139
- } | {
140
- type: string;
141
- input: boolean;
142
- label: string;
143
- key: string;
144
- placeholder: string;
145
- template: string;
146
- dataSrc: string;
147
- data: {
148
- json: unknown[];
149
- };
150
- valueProperty: string;
151
- multiple: boolean;
152
- validate: {
153
- required: boolean;
154
- };
155
- })[]>;
156
- executor(scope: import("@formio/appserver-types").ActionScope): Promise<(req: import("@formio/appserver-types").SubmissionRequest, res: import("@formio/appserver-types").SubmissionResponse, next: import("express").NextFunction) => Promise<void | import("@formio/appserver-types").SubmissionResponse>>;
157
- };
2
+ save: import("@formio/appserver-core").Action;
3
+ login: import("@formio/appserver-core").Action;
4
+ role: import("@formio/appserver-core").Action;
158
5
  };
159
6
  export default _default;
@@ -1,109 +1,2 @@
1
- import { NextFunction } from 'express';
2
- import { ActionScope, SubmissionRequest, SubmissionResponse } from '@formio/appserver-types';
3
- import { Submission } from '@formio/core/types';
4
- export declare const LoginAction: {
5
- readonly info: {
6
- name: string;
7
- title: string;
8
- description: string;
9
- priority: number;
10
- defaults: {
11
- handler: string[];
12
- method: string[];
13
- };
14
- access: {
15
- handler: boolean;
16
- method: boolean;
17
- };
18
- };
19
- /**
20
- * The settings form for this action.
21
- * @param {*} scope
22
- */
23
- settingsForm(scope: ActionScope): Promise<({
24
- type: string;
25
- label: string;
26
- key: string;
27
- placeholder: string;
28
- dataSrc: string;
29
- valueProperty: string;
30
- data: {
31
- json: any;
32
- };
33
- multiple: boolean;
34
- validate: {
35
- required: boolean;
36
- };
37
- input?: undefined;
38
- description?: undefined;
39
- defaultValue?: undefined;
40
- suffix?: undefined;
41
- } | {
42
- type: string;
43
- input: boolean;
44
- label: string;
45
- key: string;
46
- placeholder: string;
47
- dataSrc: string;
48
- valueProperty: string;
49
- data: {
50
- json: any;
51
- };
52
- multiple: boolean;
53
- validate: {
54
- required: boolean;
55
- };
56
- description?: undefined;
57
- defaultValue?: undefined;
58
- suffix?: undefined;
59
- } | {
60
- type: string;
61
- key: string;
62
- input: boolean;
63
- label: string;
64
- description: string;
65
- defaultValue: string;
66
- placeholder?: undefined;
67
- dataSrc?: undefined;
68
- valueProperty?: undefined;
69
- data?: undefined;
70
- multiple?: undefined;
71
- validate?: undefined;
72
- suffix?: undefined;
73
- } | {
74
- type: string;
75
- key: string;
76
- input: boolean;
77
- label: string;
78
- description: string;
79
- defaultValue: string;
80
- suffix: string;
81
- placeholder?: undefined;
82
- dataSrc?: undefined;
83
- valueProperty?: undefined;
84
- data?: undefined;
85
- multiple?: undefined;
86
- validate?: undefined;
87
- })[]>;
88
- /**
89
- * Format a string to show how long one must wait.
90
- *
91
- * @param time - In seconds.
92
- * @returns {string}
93
- */
94
- waitText(time: number): string;
95
- /**
96
- * Checks the login attempts for a certain login.
97
- *
98
- * @param user
99
- * @param next
100
- * @returns {*}
101
- */
102
- checkAttempts(scope: ActionScope, error: any, user: Submission): Promise<any>;
103
- /**
104
- * Returns the action middleware.
105
- *
106
- * @param {*} scope
107
- */
108
- executor(scope: ActionScope): Promise<(req: SubmissionRequest, res: SubmissionResponse, next: NextFunction) => Promise<void>>;
109
- };
1
+ import { Action } from '@formio/appserver-core';
2
+ export declare const LoginAction: Action;
@@ -8,18 +8,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
- var __importDefault = (this && this.__importDefault) || function (mod) {
12
- return (mod && mod.__esModule) ? mod : { "default": mod };
13
- };
14
11
  Object.defineProperty(exports, "__esModule", { value: true });
15
12
  exports.LoginAction = void 0;
16
- const bcryptjs_1 = __importDefault(require("bcryptjs"));
17
- const has_1 = __importDefault(require("lodash/has"));
18
- const set_1 = __importDefault(require("lodash/set"));
19
- const get_1 = __importDefault(require("lodash/get"));
20
- const unset_1 = __importDefault(require("lodash/unset"));
13
+ const appserver_core_1 = require("@formio/appserver-core");
14
+ const lodash_1 = require("lodash");
21
15
  const debug = require('debug')('formio:action:login');
22
- const error = require('debug')('formio:error');
23
16
  exports.LoginAction = {
24
17
  // The action information.
25
18
  get info() {
@@ -38,22 +31,19 @@ exports.LoginAction = {
38
31
  },
39
32
  };
40
33
  },
41
- /**
42
- * The settings form for this action.
43
- * @param {*} scope
44
- */
45
- settingsForm(scope) {
34
+ settingsForm(form) {
46
35
  return __awaiter(this, void 0, void 0, function* () {
36
+ var _a, _b;
47
37
  const fields = [];
48
38
  const resources = [];
49
- for (let name in scope.template.resources) {
50
- const resource = scope.template.resources[name];
39
+ for (let name in (_a = form.project.template) === null || _a === void 0 ? void 0 : _a.resources) {
40
+ const resource = form.project.template.resources[name];
51
41
  resources.push({
52
42
  label: resource.title,
53
43
  value: resource.name
54
44
  });
55
45
  }
56
- scope.utils.eachComponent(scope.form.components, (component) => {
46
+ appserver_core_1.Utils.eachComponent((_b = form.form) === null || _b === void 0 ? void 0 : _b.components, (component) => {
57
47
  if (['button'].indexOf(component.type) !== -1) {
58
48
  return;
59
49
  }
@@ -65,223 +55,144 @@ exports.LoginAction = {
65
55
  return [
66
56
  {
67
57
  type: 'select',
68
- label: 'Resources',
69
- key: 'resources',
70
- placeholder: 'Select the resources we should login against.',
71
- dataSrc: 'json',
72
- valueProperty: 'value',
73
- data: { json: resources },
74
- multiple: true,
75
- validate: {
76
- required: true,
77
- },
78
- },
79
- {
80
- type: 'select',
58
+ label: 'Authentication Provider',
59
+ key: 'provider',
60
+ placeholder: 'Select the authentication provider to use for login.',
61
+ dataSrc: 'values',
62
+ defaultValue: 'local',
81
63
  input: true,
82
- label: 'Username Field',
83
- key: 'username',
84
- placeholder: 'Select the username field',
85
- dataSrc: 'json',
86
- valueProperty: 'value',
87
- data: { json: fields },
88
- multiple: false,
89
- validate: {
90
- required: true,
91
- },
64
+ data: {
65
+ values: [
66
+ { label: 'Local', value: 'local' },
67
+ { label: 'OpenID Connect', value: 'oidc' },
68
+ ]
69
+ }
92
70
  },
93
71
  {
94
- type: 'select',
95
- label: 'Password Field',
96
- key: 'password',
97
- placeholder: 'Select the password field',
98
- dataSrc: 'json',
99
- valueProperty: 'value',
100
- data: { json: fields },
101
- multiple: false,
102
- validate: {
103
- required: true,
72
+ type: 'panel',
73
+ label: 'Login Settings',
74
+ key: 'localSettings',
75
+ input: false,
76
+ conditional: {
77
+ when: 'provider',
78
+ eq: 'local',
79
+ show: 'true'
104
80
  },
105
- },
106
- {
107
- type: 'textfield',
108
- key: 'allowedAttempts',
109
- input: true,
110
- label: 'Maximum Login Attempts',
111
- description: 'Use 0 for unlimited attempts',
112
- defaultValue: '5',
113
- },
114
- {
115
- type: 'textfield',
116
- key: 'attemptWindow',
117
- input: true,
118
- label: 'Login Attempt Time Window',
119
- description: 'This is the window of time to count the login attempts.',
120
- defaultValue: '30',
121
- suffix: 'seconds',
122
- },
123
- {
124
- type: 'textfield',
125
- key: 'lockWait',
126
- input: true,
127
- label: 'Locked Account Wait Time',
128
- description: 'The amount of time a person needs to wait before they can try to login again.',
129
- defaultValue: '1800',
130
- suffix: 'seconds',
81
+ components: [
82
+ {
83
+ type: 'select',
84
+ label: 'Resources',
85
+ key: 'resources',
86
+ placeholder: 'Select the resources we should login against.',
87
+ dataSrc: 'json',
88
+ valueProperty: 'value',
89
+ data: { json: resources },
90
+ multiple: true,
91
+ input: true,
92
+ validate: {
93
+ required: true,
94
+ },
95
+ },
96
+ {
97
+ type: 'select',
98
+ input: true,
99
+ label: 'Username Field',
100
+ key: 'username',
101
+ placeholder: 'Select the username field',
102
+ dataSrc: 'json',
103
+ valueProperty: 'value',
104
+ data: { json: fields },
105
+ multiple: false,
106
+ validate: {
107
+ required: true,
108
+ },
109
+ },
110
+ {
111
+ type: 'select',
112
+ label: 'Password Field',
113
+ key: 'password',
114
+ placeholder: 'Select the password field',
115
+ dataSrc: 'json',
116
+ valueProperty: 'value',
117
+ data: { json: fields },
118
+ multiple: false,
119
+ input: true,
120
+ validate: {
121
+ required: true,
122
+ },
123
+ },
124
+ {
125
+ type: 'textfield',
126
+ key: 'allowedAttempts',
127
+ input: true,
128
+ label: 'Maximum Login Attempts',
129
+ description: 'Use 0 for unlimited attempts',
130
+ defaultValue: '5',
131
+ },
132
+ {
133
+ type: 'textfield',
134
+ key: 'attemptWindow',
135
+ input: true,
136
+ label: 'Login Attempt Time Window',
137
+ description: 'This is the window of time to count the login attempts.',
138
+ defaultValue: '30',
139
+ suffix: 'seconds',
140
+ },
141
+ {
142
+ type: 'textfield',
143
+ key: 'lockWait',
144
+ input: true,
145
+ label: 'Locked Account Wait Time',
146
+ description: 'The amount of time a person needs to wait before they can try to login again.',
147
+ defaultValue: '1800',
148
+ suffix: 'seconds',
149
+ }
150
+ ]
131
151
  },
132
152
  ];
133
153
  });
134
154
  },
135
- /**
136
- * Format a string to show how long one must wait.
137
- *
138
- * @param time - In seconds.
139
- * @returns {string}
140
- */
141
- waitText(time) {
142
- return (time > 60) ? `${(time / 60)} minutes` : `${time} seconds`;
143
- },
144
- /**
145
- * Checks the login attempts for a certain login.
146
- *
147
- * @param user
148
- * @param next
149
- * @returns {*}
150
- */
151
- checkAttempts(scope, error, user) {
155
+ executor(actionForm, action, handler, method) {
152
156
  return __awaiter(this, void 0, void 0, function* () {
153
- const action = scope.action;
154
- if (!user || !user._id || !action.settings.allowedAttempts) {
155
- return;
156
- }
157
- const allowedAttempts = parseInt(action.settings.allowedAttempts, 10);
158
- if (Number.isNaN(allowedAttempts) || allowedAttempts === 0) {
159
- return;
160
- }
161
- // Initialize the login metadata.
162
- if (!(0, has_1.default)(user, 'metadata.login')) {
163
- (0, set_1.default)(user, 'metadata.login', {});
164
- }
165
- const now = (new Date()).getTime();
166
- const { login: loginMetadata, } = user.metadata;
167
- const lastAttempt = parseInt(loginMetadata.last, 10) || 0;
168
- // See if the login is locked.
169
- if (loginMetadata.locked) {
170
- // Get how long they must wait to be locked out.
171
- let lockWait = parseInt(action.settings.lockWait, 10) || 1800;
172
- // Normalize to milliseconds.
173
- lockWait *= 1000;
174
- // See if the time has expired.
175
- if ((lastAttempt + lockWait) < now) {
176
- // Reset the locked state and attempts totals.
177
- loginMetadata.attempts = 0;
178
- loginMetadata.locked = false;
179
- loginMetadata.last = now;
180
- }
181
- else {
182
- const howLong = (lastAttempt + lockWait) - now;
183
- return `You must wait ${exports.LoginAction.waitText(howLong / 1000)} before you can login.`;
184
- }
157
+ var _a, _b, _c, _d;
158
+ let loginProvider = null;
159
+ if (!action.settings.provider || action.settings.provider !== 'local') {
160
+ loginProvider = new appserver_core_1.LocalAuthProvider(actionForm.project, (0, lodash_1.defaultsDeep)((_b = (_a = actionForm.project) === null || _a === void 0 ? void 0 : _a.config) === null || _b === void 0 ? void 0 : _b.auth, {
161
+ local: action.settings
162
+ }));
185
163
  }
186
- else if (error) {
187
- let attemptWindow = parseInt(action.settings.attemptWindow, 10) || 30;
188
- // Normalize to milliseconds.
189
- attemptWindow *= 1000;
190
- // Determine the login attempts within a certain window.
191
- const withinWindow = lastAttempt ? ((lastAttempt + attemptWindow) > now) : false;
192
- if (withinWindow) {
193
- const attempts = (parseInt(loginMetadata.attempts, 10) || 0) + 1;
194
- // If they exceeded the login attempts.
195
- if (attempts >= allowedAttempts) {
196
- const lockWait = parseInt(action.settings.lockWait, 10) || 1800;
197
- error = `Maximum Login attempts. Please wait ${exports.LoginAction.waitText(lockWait)} before trying again.`;
198
- loginMetadata.locked = true;
199
- }
200
- // Set the login attempts.
201
- loginMetadata.attempts = attempts;
202
- }
203
- else {
204
- loginMetadata.attempts = 0;
205
- loginMetadata.last = now;
206
- }
164
+ else if (action.settings.provider === 'oidc') {
165
+ loginProvider = new appserver_core_1.OIDCProvider(actionForm.project, (0, lodash_1.defaultsDeep)((_d = (_c = actionForm.project) === null || _c === void 0 ? void 0 : _c.config) === null || _d === void 0 ? void 0 : _d.auth, {
166
+ oidc: action.settings
167
+ }));
207
168
  }
208
- else {
209
- // If there was no error, then reset the attempts to zero.
210
- loginMetadata.attempts = 0;
211
- loginMetadata.last = now;
169
+ if (loginProvider === null || loginProvider === void 0 ? void 0 : loginProvider.initialize) {
170
+ yield loginProvider.initialize();
212
171
  }
213
- // Update the user record
214
- yield scope.db.update(scope, user._id.toString(), user);
215
- return error;
216
- });
217
- },
218
- /**
219
- * Returns the action middleware.
220
- *
221
- * @param {*} scope
222
- */
223
- executor(scope) {
224
- return __awaiter(this, void 0, void 0, function* () {
225
- const action = scope.action;
226
- const userfield = action.settings.username;
227
- const passfield = action.settings.password;
228
- const forms = yield scope.utils.getForms(scope, action.settings.resources);
229
172
  return (req, res, next) => __awaiter(this, void 0, void 0, function* () {
230
- var _a, _b;
231
173
  debug('Login Action');
232
- const username = (0, get_1.default)((_a = (req.originalBody || req.body)) === null || _a === void 0 ? void 0 : _a.data, userfield);
233
- const password = (0, get_1.default)((_b = (req.originalBody || req.body)) === null || _b === void 0 ? void 0 : _b.data, passfield, '');
234
- if (!username) {
235
- error('Missing username');
236
- return next('Missing username');
237
- }
238
- if (!password) {
239
- error('Missing password');
240
- return next('Missing password');
174
+ const result = yield (loginProvider === null || loginProvider === void 0 ? void 0 : loginProvider.login(req.auth, req.body, actionForm));
175
+ if (!result) {
176
+ return next('Unable to login');
241
177
  }
242
- // Find the user
243
- debug('Finding user', `${userfield} = ${username}`);
244
- const user = yield scope.utils.findUser(scope, forms, `data.${userfield}`, username);
245
- if (!user) {
246
- error('User not found');
247
- return next('User or password was incorrect');
178
+ if (typeof result === 'string') {
179
+ // If a string is returned, then it is a redirect.
180
+ return res.redirect(result);
248
181
  }
249
- // Get the current password hash.
250
- const hash = (0, get_1.default)(user.data, passfield);
251
- if (!hash || !hash.hash) {
252
- error('User does not have a password');
253
- return next('Your account does not have a password. You must reset your password to login.');
254
- }
255
- // Compare with bcrypt.
256
- debug('Comparing password');
257
- bcryptjs_1.default.compare(password, hash.hash, (err, value) => __awaiter(this, void 0, void 0, function* () {
258
- if (err) {
259
- error(err);
260
- return next(err);
261
- }
262
- // Check the login attempts.
263
- const errorAttempts = yield exports.LoginAction.checkAttempts(scope, !value, user);
264
- if (errorAttempts) {
265
- error(errorAttempts);
266
- return next(errorAttempts === true ? 'User or password was incorrect' : errorAttempts);
267
- }
268
- // Unset the password field before we create a token from it.
269
- (0, unset_1.default)(user.data, passfield);
270
- // Set the user object.
271
- req.user = user;
272
- req.token = yield scope.auth.token(scope.utils.tokenFromUser(user), scope.config.auth.secret, scope.config.auth.expire);
273
- // Set the auth headers.
274
- res.setHeader('x-jwt-token', req.token);
275
- req.headers['x-jwt-token'] = req.token;
182
+ if (typeof result === 'object') {
183
+ res.set('x-jwt-token', req.auth.token);
276
184
  if (res.resource) {
277
- res.resource.item = req.user;
185
+ res.resource.item = req.auth.user;
278
186
  }
279
187
  else {
280
- res.resource = { status: 200, item: req.user };
188
+ res.resource = { status: 200, item: req.auth.user };
281
189
  }
282
- debug('Login Successful', req.user);
190
+ debug('Login Successful', req.auth.user);
283
191
  next();
284
- }));
192
+ }
193
+ else {
194
+ return next('Unable to login');
195
+ }
285
196
  });
286
197
  });
287
198
  }