@semapps/auth 0.4.0-alpha.4 → 0.4.0-alpha.42
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.
- package/index.js +2 -1
- package/mixins/auth.js +19 -6
- package/mixins/auth.sso.js +14 -6
- package/package.json +8 -5
- package/services/account.js +105 -14
- package/services/auth.local.js +85 -8
- package/services/auth.oidc.js +8 -4
- package/services/jwt.js +3 -0
- package/services/mail.js +49 -0
- package/services/migration.js +2 -1
- package/templates/reset-password/en-EN/subject.hbs +1 -0
- package/templates/reset-password/en-EN/text.hbs +7 -0
- package/templates/reset-password/fr-FR/subject.hbs +1 -0
- package/templates/reset-password/fr-FR/text.hbs +7 -0
package/index.js
CHANGED
@@ -4,5 +4,6 @@ module.exports = {
|
|
4
4
|
AuthOIDCService: require('./services/auth.oidc'),
|
5
5
|
AuthAccountService: require('./services/account'),
|
6
6
|
AuthJWTService: require('./services/jwt'),
|
7
|
-
AuthMigrationService: require('./services/migration')
|
7
|
+
AuthMigrationService: require('./services/migration'),
|
8
|
+
AuthMailService: require('./services/mail')
|
8
9
|
};
|
package/mixins/auth.js
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
+
const passport = require('passport');
|
2
|
+
const { Errors: E } = require('moleculer-web');
|
3
|
+
const { TripleStoreAdapter } = require('@semapps/triplestore');
|
1
4
|
const AuthAccountService = require('../services/account');
|
2
5
|
const AuthJWTService = require('../services/jwt');
|
3
|
-
const { Errors: E } = require('moleculer-web');
|
4
|
-
const passport = require('passport');
|
5
6
|
|
6
7
|
const AuthMixin = {
|
7
8
|
settings: {
|
@@ -9,18 +10,21 @@ const AuthMixin = {
|
|
9
10
|
jwtPath: null,
|
10
11
|
registrationAllowed: true,
|
11
12
|
reservedUsernames: [],
|
12
|
-
webIdSelection: []
|
13
|
+
webIdSelection: [],
|
14
|
+
accountSelection: [],
|
15
|
+
accountsDataset: 'settings'
|
13
16
|
},
|
14
17
|
dependencies: ['api', 'webid'],
|
15
18
|
async created() {
|
16
|
-
const { jwtPath, reservedUsernames } = this.settings;
|
19
|
+
const { jwtPath, reservedUsernames, accountsDataset } = this.settings;
|
17
20
|
|
18
21
|
await this.broker.createService(AuthJWTService, {
|
19
22
|
settings: { jwtPath }
|
20
23
|
});
|
21
24
|
|
22
25
|
await this.broker.createService(AuthAccountService, {
|
23
|
-
settings: { reservedUsernames }
|
26
|
+
settings: { reservedUsernames },
|
27
|
+
adapter: new TripleStoreAdapter({ type: 'AuthAccount', dataset: accountsDataset })
|
24
28
|
});
|
25
29
|
},
|
26
30
|
async started() {
|
@@ -34,7 +38,7 @@ const AuthMixin = {
|
|
34
38
|
done(null, user);
|
35
39
|
});
|
36
40
|
|
37
|
-
this.strategy = this.getStrategy();
|
41
|
+
this.strategy = await this.getStrategy();
|
38
42
|
|
39
43
|
this.passport.use(this.passportId, this.strategy);
|
40
44
|
|
@@ -108,6 +112,15 @@ const AuthMixin = {
|
|
108
112
|
} else {
|
109
113
|
return data;
|
110
114
|
}
|
115
|
+
},
|
116
|
+
pickAccountData(data) {
|
117
|
+
if (this.settings.accountSelection.length > 0) {
|
118
|
+
return Object.fromEntries(
|
119
|
+
this.settings.accountSelection.filter(key => key in data).map(key => [key, data[key]])
|
120
|
+
);
|
121
|
+
} else {
|
122
|
+
return data || {};
|
123
|
+
}
|
111
124
|
}
|
112
125
|
}
|
113
126
|
};
|
package/mixins/auth.sso.js
CHANGED
@@ -32,25 +32,33 @@ const AuthSSOMixin = {
|
|
32
32
|
newUser = false;
|
33
33
|
|
34
34
|
// TODO update account with recent information
|
35
|
-
// await
|
35
|
+
// await ctx.call('webid.edit', profileData, { meta: { webId } });
|
36
36
|
|
37
|
-
|
37
|
+
ctx.emit('auth.connected', { webId, accountData, ssoData }, { meta: { webId: null, dataset: null } });
|
38
38
|
} else {
|
39
39
|
if (!this.settings.registrationAllowed) {
|
40
40
|
throw new Error('registration.not-allowed');
|
41
41
|
}
|
42
42
|
|
43
|
-
accountData = await ctx.call('auth.account.create', {
|
44
|
-
|
43
|
+
accountData = await ctx.call('auth.account.create', {
|
44
|
+
uuid: profileData.uuid,
|
45
|
+
email: profileData.email,
|
46
|
+
username: profileData.username
|
47
|
+
});
|
48
|
+
webId = await ctx.call('webid.create', this.pickWebIdData({ nick: accountData.username, ...profileData }));
|
45
49
|
newUser = true;
|
46
50
|
|
47
51
|
// Link the webId with the account
|
48
52
|
await ctx.call('auth.account.attachWebId', { accountUri: accountData['@id'], webId });
|
49
53
|
|
50
|
-
ctx.emit(
|
54
|
+
ctx.emit(
|
55
|
+
'auth.registered',
|
56
|
+
{ webId, profileData, accountData, ssoData },
|
57
|
+
{ meta: { webId: null, dataset: null } }
|
58
|
+
);
|
51
59
|
}
|
52
60
|
|
53
|
-
const token = await ctx.call('auth.jwt.generateToken', { payload: { webId
|
61
|
+
const token = await ctx.call('auth.jwt.generateToken', { payload: { webId } });
|
54
62
|
|
55
63
|
return { token, newUser };
|
56
64
|
}
|
package/package.json
CHANGED
@@ -1,26 +1,29 @@
|
|
1
1
|
{
|
2
2
|
"name": "@semapps/auth",
|
3
|
-
"version": "0.4.0-alpha.
|
3
|
+
"version": "0.4.0-alpha.42",
|
4
4
|
"description": "Authentification module for SemApps",
|
5
5
|
"license": "Apache-2.0",
|
6
6
|
"author": "Virtual Assembly",
|
7
7
|
"dependencies": {
|
8
|
-
"@semapps/mime-types": "0.4.0-alpha.
|
9
|
-
"@semapps/triplestore": "0.4.0-alpha.
|
8
|
+
"@semapps/mime-types": "0.4.0-alpha.42",
|
9
|
+
"@semapps/triplestore": "0.4.0-alpha.42",
|
10
10
|
"bcrypt": "^5.0.1",
|
11
11
|
"express-session": "^1.17.0",
|
12
12
|
"jsonwebtoken": "^8.5.1",
|
13
13
|
"moleculer": "^0.14.17",
|
14
14
|
"moleculer-db": "^0.8.16",
|
15
|
+
"moleculer-mail": "^1.2.5",
|
15
16
|
"moleculer-web": "^0.10.0-beta1",
|
17
|
+
"node-sass": "^7.0.1",
|
16
18
|
"openid-client": "^4.7.4",
|
17
19
|
"passport": "^0.4.1",
|
18
|
-
"passport-cas2": "0.0.
|
20
|
+
"passport-cas2": "0.0.12",
|
19
21
|
"passport-local": "^1.0.0",
|
22
|
+
"pug": "^3.0.2",
|
20
23
|
"url-join": "^4.0.1"
|
21
24
|
},
|
22
25
|
"publishConfig": {
|
23
26
|
"access": "public"
|
24
27
|
},
|
25
|
-
"gitHead": "
|
28
|
+
"gitHead": "e2711d7d742bc8a1399472567bbbe5bf82d063f9"
|
26
29
|
}
|
package/services/account.js
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
const bcrypt = require('bcrypt');
|
2
2
|
const DbService = require('moleculer-db');
|
3
3
|
const { TripleStoreAdapter } = require('@semapps/triplestore');
|
4
|
+
const crypto = require('crypto');
|
4
5
|
|
5
6
|
module.exports = {
|
6
7
|
name: 'auth.account',
|
@@ -8,34 +9,41 @@ module.exports = {
|
|
8
9
|
adapter: new TripleStoreAdapter({ type: 'AuthAccount', dataset: 'settings' }),
|
9
10
|
settings: {
|
10
11
|
idField: '@id',
|
11
|
-
reservedUsernames: []
|
12
|
+
reservedUsernames: ['relay']
|
12
13
|
},
|
13
14
|
dependencies: ['triplestore'],
|
14
15
|
actions: {
|
15
16
|
async create(ctx) {
|
16
|
-
let { uuid, username, password, email, webId } = ctx.params;
|
17
|
+
let { uuid, username, password, email, webId, ...rest } = ctx.params;
|
17
18
|
const hashedPassword = password ? await this.hashPassword(password) : undefined;
|
18
19
|
|
19
|
-
|
20
|
+
email = email && email.toLowerCase();
|
21
|
+
|
22
|
+
const emailExists = !email ? false : await ctx.call('auth.account.emailExists', { email });
|
20
23
|
if (emailExists) {
|
21
24
|
throw new Error('email.already.exists');
|
22
25
|
}
|
23
26
|
|
24
27
|
if (username) {
|
25
|
-
await this.isValidUsername(ctx, username);
|
26
|
-
} else {
|
28
|
+
if (!ctx.meta.isSystemCall) await this.isValidUsername(ctx, username);
|
29
|
+
} else if (email) {
|
27
30
|
// If username is not provided, find an username based on the email
|
31
|
+
const usernameFromEmail = email.split('@')[0].toLowerCase();
|
28
32
|
let usernameValid = false,
|
29
|
-
i =
|
33
|
+
i = 0;
|
30
34
|
do {
|
35
|
+
username = i === 0 ? usernameFromEmail : usernameFromEmail + i;
|
36
|
+
try {
|
37
|
+
usernameValid = await this.isValidUsername(ctx, username);
|
38
|
+
} catch (e) {
|
39
|
+
// Do nothing, the loop will continue
|
40
|
+
}
|
31
41
|
i++;
|
32
|
-
|
33
|
-
|
34
|
-
usernameValid = await this.isValidUsername(ctx, username);
|
35
|
-
} while (usernameValid);
|
36
|
-
}
|
42
|
+
} while (!usernameValid);
|
43
|
+
} else throw new Error('you must provide at least a username or an email address');
|
37
44
|
|
38
45
|
return await this._create(ctx, {
|
46
|
+
...rest,
|
39
47
|
uuid,
|
40
48
|
username,
|
41
49
|
email,
|
@@ -85,21 +93,92 @@ module.exports = {
|
|
85
93
|
const accounts = await this._find(ctx, { query: { webId } });
|
86
94
|
return accounts.length > 0 ? accounts[0] : null;
|
87
95
|
},
|
96
|
+
async findByEmail(ctx) {
|
97
|
+
const { email } = ctx.params;
|
98
|
+
const accounts = await this._find(ctx, { query: { email } });
|
99
|
+
return accounts.length > 0 ? accounts[0] : null;
|
100
|
+
},
|
88
101
|
async setPassword(ctx) {
|
89
102
|
const { webId, password } = ctx.params;
|
90
103
|
const hashedPassword = await this.hashPassword(password);
|
91
104
|
const account = await ctx.call('auth.account.findByWebId', { webId });
|
92
105
|
|
93
106
|
return await this._update(ctx, {
|
94
|
-
'@id': account
|
107
|
+
'@id': account['@id'],
|
95
108
|
hashedPassword
|
96
109
|
});
|
110
|
+
},
|
111
|
+
async setNewPassword(ctx) {
|
112
|
+
const { webId, token, password } = ctx.params;
|
113
|
+
const hashedPassword = await this.hashPassword(password);
|
114
|
+
const account = await ctx.call('auth.account.findByWebId', { webId });
|
115
|
+
|
116
|
+
if (account.resetPasswordToken !== token) {
|
117
|
+
throw new Error('auth.password.invalid_reset_token');
|
118
|
+
}
|
119
|
+
|
120
|
+
return await this._update(ctx, {
|
121
|
+
'@id': account['@id'],
|
122
|
+
hashedPassword,
|
123
|
+
resetPasswordToken: undefined
|
124
|
+
});
|
125
|
+
},
|
126
|
+
async generateResetPasswordToken(ctx) {
|
127
|
+
const { webId } = ctx.params;
|
128
|
+
const resetPasswordToken = await this.generateResetPasswordToken();
|
129
|
+
const account = await ctx.call('auth.account.findByWebId', { webId });
|
130
|
+
|
131
|
+
await this._update(ctx, {
|
132
|
+
'@id': account['@id'],
|
133
|
+
resetPasswordToken
|
134
|
+
});
|
135
|
+
|
136
|
+
return resetPasswordToken;
|
137
|
+
},
|
138
|
+
async findSettingsByWebId(ctx) {
|
139
|
+
const { webId } = ctx.meta;
|
140
|
+
const account = await ctx.call('auth.account.findByWebId', { webId });
|
141
|
+
|
142
|
+
return {
|
143
|
+
email: account['email'],
|
144
|
+
preferredLocale: account['preferredLocale']
|
145
|
+
};
|
146
|
+
},
|
147
|
+
async updateAccountSettings(ctx) {
|
148
|
+
const { currentPassword, email, newPassword } = ctx.params;
|
149
|
+
const { webId } = ctx.meta;
|
150
|
+
const account = await ctx.call('auth.account.findByWebId', { webId });
|
151
|
+
const passwordMatch = await this.comparePassword(currentPassword, account.hashedPassword);
|
152
|
+
let params = {};
|
153
|
+
|
154
|
+
if (!passwordMatch) {
|
155
|
+
throw new Error('auth.account.invalid_password');
|
156
|
+
}
|
157
|
+
|
158
|
+
if (newPassword) {
|
159
|
+
const hashedPassword = await this.hashPassword(newPassword);
|
160
|
+
params = { ...params, hashedPassword };
|
161
|
+
}
|
162
|
+
|
163
|
+
if (email !== account['email']) {
|
164
|
+
const existing = await ctx.call('auth.account.findByEmail', { email });
|
165
|
+
if (existing) {
|
166
|
+
throw new Error('email.already.exists');
|
167
|
+
}
|
168
|
+
|
169
|
+
params = { ...params, email };
|
170
|
+
}
|
171
|
+
|
172
|
+
return await this._update(ctx, {
|
173
|
+
'@id': account['@id'],
|
174
|
+
...params
|
175
|
+
});
|
97
176
|
}
|
98
177
|
},
|
99
178
|
methods: {
|
100
179
|
async isValidUsername(ctx, username) {
|
101
180
|
// Ensure the username has no space or special characters
|
102
|
-
if (!/^[a-z0-9
|
181
|
+
if (!/^[a-z0-9\-+_.]+$/.exec(username)) {
|
103
182
|
throw new Error('username.invalid');
|
104
183
|
}
|
105
184
|
|
@@ -108,11 +187,13 @@ module.exports = {
|
|
108
187
|
throw new Error('username.already.exists');
|
109
188
|
}
|
110
189
|
|
111
|
-
// Ensure
|
190
|
+
// Ensure username doesn't already exist
|
112
191
|
const usernameExists = await ctx.call('auth.account.usernameExists', { username });
|
113
192
|
if (usernameExists) {
|
114
193
|
throw new Error('username.already.exists');
|
115
194
|
}
|
195
|
+
|
196
|
+
return true;
|
116
197
|
},
|
117
198
|
async hashPassword(password) {
|
118
199
|
return new Promise((resolve, reject) => {
|
@@ -135,6 +216,16 @@ module.exports = {
|
|
135
216
|
}
|
136
217
|
});
|
137
218
|
});
|
219
|
+
},
|
220
|
+
async generateResetPasswordToken() {
|
221
|
+
return new Promise(resolve => {
|
222
|
+
crypto.randomBytes(32, function(ex, buf) {
|
223
|
+
if (ex) {
|
224
|
+
reject(ex);
|
225
|
+
}
|
226
|
+
resolve(buf.toString('hex'));
|
227
|
+
});
|
228
|
+
});
|
138
229
|
}
|
139
230
|
}
|
140
231
|
};
|
package/services/auth.local.js
CHANGED
@@ -2,6 +2,7 @@ const { Strategy } = require('passport-local');
|
|
2
2
|
const AuthMixin = require('../mixins/auth');
|
3
3
|
const sendToken = require('../middlewares/sendToken');
|
4
4
|
const { MoleculerError } = require('moleculer').Errors;
|
5
|
+
const AuthMailService = require('../services/mail');
|
5
6
|
|
6
7
|
const AuthLocalService = {
|
7
8
|
name: 'auth',
|
@@ -11,18 +12,43 @@ const AuthLocalService = {
|
|
11
12
|
jwtPath: null,
|
12
13
|
registrationAllowed: true,
|
13
14
|
reservedUsernames: [],
|
14
|
-
webIdSelection: []
|
15
|
+
webIdSelection: [],
|
16
|
+
accountSelection: [],
|
17
|
+
mail: {
|
18
|
+
from: null,
|
19
|
+
transport: {
|
20
|
+
host: null,
|
21
|
+
port: null
|
22
|
+
},
|
23
|
+
defaults: {
|
24
|
+
locale: null,
|
25
|
+
frontUrl: null
|
26
|
+
}
|
27
|
+
}
|
15
28
|
},
|
16
|
-
created() {
|
29
|
+
async created() {
|
30
|
+
const { mail } = this.settings;
|
31
|
+
|
17
32
|
this.passportId = 'local';
|
33
|
+
|
34
|
+
await this.broker.createService(AuthMailService, {
|
35
|
+
settings: {
|
36
|
+
...mail
|
37
|
+
}
|
38
|
+
});
|
18
39
|
},
|
19
40
|
actions: {
|
20
41
|
async signup(ctx) {
|
21
|
-
const { username, email, password, ...
|
42
|
+
const { username, email, password, ...rest } = ctx.params;
|
22
43
|
|
23
|
-
let accountData = await ctx.call('auth.account.create', {
|
44
|
+
let accountData = await ctx.call('auth.account.create', {
|
45
|
+
username,
|
46
|
+
email,
|
47
|
+
password,
|
48
|
+
...this.pickAccountData(rest)
|
49
|
+
});
|
24
50
|
|
25
|
-
const profileData = { nick: username, email, ...
|
51
|
+
const profileData = { nick: username, email, ...rest };
|
26
52
|
const webId = await ctx.call('webid.create', this.pickWebIdData(profileData));
|
27
53
|
|
28
54
|
// Link the webId with the account
|
@@ -44,6 +70,33 @@ const AuthLocalService = {
|
|
44
70
|
const token = await ctx.call('auth.jwt.generateToken', { payload: { webId: accountData.webId } });
|
45
71
|
|
46
72
|
return { token, webId: accountData.webId, newUser: true };
|
73
|
+
},
|
74
|
+
async resetPassword(ctx) {
|
75
|
+
const { email } = ctx.params;
|
76
|
+
|
77
|
+
const account = await ctx.call('auth.account.findByEmail', { email });
|
78
|
+
|
79
|
+
if (!account) {
|
80
|
+
throw new Error('email.not.exists');
|
81
|
+
}
|
82
|
+
|
83
|
+
const token = await ctx.call('auth.account.generateResetPasswordToken', { webId: account.webId });
|
84
|
+
|
85
|
+
await ctx.call('auth.mail.sendResetPasswordEmail', {
|
86
|
+
account,
|
87
|
+
token
|
88
|
+
});
|
89
|
+
},
|
90
|
+
async setNewPassword(ctx) {
|
91
|
+
const { email, token, password } = ctx.params;
|
92
|
+
|
93
|
+
const account = await ctx.call('auth.account.findByEmail', { email });
|
94
|
+
|
95
|
+
if (!account) {
|
96
|
+
throw new Error('email.not.exists');
|
97
|
+
}
|
98
|
+
|
99
|
+
await ctx.call('auth.account.setNewPassword', { webId: account.webId, token, password });
|
47
100
|
}
|
48
101
|
},
|
49
102
|
methods: {
|
@@ -83,11 +136,35 @@ const AuthLocalService = {
|
|
83
136
|
}
|
84
137
|
};
|
85
138
|
|
139
|
+
const resetPasswordRoute = {
|
140
|
+
path: '/auth/reset_password',
|
141
|
+
aliases: {
|
142
|
+
'POST /': 'auth.resetPassword'
|
143
|
+
}
|
144
|
+
};
|
145
|
+
const setNewPasswordRoute = {
|
146
|
+
path: '/auth/new_password',
|
147
|
+
aliases: {
|
148
|
+
'POST /': 'auth.setNewPassword'
|
149
|
+
}
|
150
|
+
};
|
151
|
+
|
152
|
+
const accountSettingsRoute = {
|
153
|
+
path: '/auth/account',
|
154
|
+
aliases: {
|
155
|
+
'GET /': 'auth.account.findSettingsByWebId',
|
156
|
+
'POST /': 'auth.account.updateAccountSettings'
|
157
|
+
},
|
158
|
+
authorization: true
|
159
|
+
};
|
160
|
+
|
161
|
+
const routes = [loginRoute, resetPasswordRoute, setNewPasswordRoute, accountSettingsRoute];
|
162
|
+
|
86
163
|
if (this.settings.registrationAllowed) {
|
87
|
-
return [
|
88
|
-
} else {
|
89
|
-
return [loginRoute];
|
164
|
+
return [...routes, signupRoute];
|
90
165
|
}
|
166
|
+
|
167
|
+
return routes;
|
91
168
|
}
|
92
169
|
}
|
93
170
|
};
|
package/services/auth.oidc.js
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
const urlJoin = require('url-join');
|
2
|
-
const { Issuer, Strategy } = require('openid-client');
|
2
|
+
const { Issuer, Strategy, custom } = require('openid-client');
|
3
|
+
custom.setHttpOptionsDefaults({
|
4
|
+
timeout: 10000
|
5
|
+
});
|
3
6
|
const AuthSSOMixin = require('../mixins/auth.sso');
|
4
7
|
|
5
8
|
const AuthOIDCService = {
|
@@ -21,11 +24,12 @@ const AuthOIDCService = {
|
|
21
24
|
},
|
22
25
|
async created() {
|
23
26
|
this.passportId = 'oidc';
|
24
|
-
this.issuer = await Issuer.discover(this.settings.issuer);
|
25
27
|
},
|
26
28
|
methods: {
|
27
|
-
getStrategy() {
|
28
|
-
const
|
29
|
+
async getStrategy() {
|
30
|
+
const issuer = await Issuer.discover(this.settings.issuer);
|
31
|
+
|
32
|
+
const client = new issuer.Client({
|
29
33
|
client_id: this.settings.clientId,
|
30
34
|
client_secret: this.settings.clientSecret,
|
31
35
|
redirect_uri: urlJoin(this.settings.baseUrl, 'auth'),
|
package/services/jwt.js
CHANGED
@@ -14,6 +14,9 @@ module.exports = {
|
|
14
14
|
|
15
15
|
if (!fs.existsSync(privateKeyPath) && !fs.existsSync(publicKeyPath)) {
|
16
16
|
console.log('JWT keypair not found, generating...');
|
17
|
+
if (!fs.existsSync(this.settings.jwtPath)) {
|
18
|
+
fs.mkdirSync(this.settings.jwtPath);
|
19
|
+
}
|
17
20
|
await this.actions.generateKeyPair({ privateKeyPath, publicKeyPath });
|
18
21
|
}
|
19
22
|
|
package/services/mail.js
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
const MailService = require('moleculer-mail');
|
2
|
+
const path = require('path');
|
3
|
+
|
4
|
+
module.exports = {
|
5
|
+
name: 'auth.mail',
|
6
|
+
mixins: [MailService],
|
7
|
+
settings: {
|
8
|
+
defaults: {
|
9
|
+
locale: 'en',
|
10
|
+
frontUrl: null
|
11
|
+
},
|
12
|
+
templateFolder: path.join(__dirname, '../templates'),
|
13
|
+
from: null,
|
14
|
+
transport: null
|
15
|
+
},
|
16
|
+
actions: {
|
17
|
+
async sendResetPasswordEmail(ctx) {
|
18
|
+
const { account, token } = ctx.params;
|
19
|
+
|
20
|
+
await this.actions.send(
|
21
|
+
{
|
22
|
+
to: account.email,
|
23
|
+
template: 'reset-password',
|
24
|
+
locale: this.getTemplateLocale(account.preferredLocale || this.settings.defaults.locale),
|
25
|
+
data: {
|
26
|
+
account,
|
27
|
+
token,
|
28
|
+
frontUrl: account.preferredFrontUrl || this.settings.defaults.frontUrl
|
29
|
+
}
|
30
|
+
},
|
31
|
+
{
|
32
|
+
parentCtx: ctx
|
33
|
+
}
|
34
|
+
);
|
35
|
+
}
|
36
|
+
},
|
37
|
+
methods: {
|
38
|
+
getTemplateLocale(userLocale) {
|
39
|
+
switch (userLocale) {
|
40
|
+
case 'fr':
|
41
|
+
return 'fr-FR';
|
42
|
+
case 'en':
|
43
|
+
return 'en-EN';
|
44
|
+
default:
|
45
|
+
return 'en-EN';
|
46
|
+
}
|
47
|
+
}
|
48
|
+
}
|
49
|
+
};
|
package/services/migration.js
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
const { MIME_TYPES } = require('@semapps/mime-types');
|
2
|
+
const { getSlugFromUri } = require('@semapps/ldp');
|
2
3
|
|
3
4
|
module.exports = {
|
4
5
|
name: 'auth.migration',
|
@@ -13,7 +14,7 @@ module.exports = {
|
|
13
14
|
try {
|
14
15
|
await ctx.call('auth.account.create', {
|
15
16
|
email: user[emailPredicate],
|
16
|
-
username: user[usernamePredicate],
|
17
|
+
username: usernamePredicate ? user[usernamePredicate] : getSlugFromUri(user.id),
|
17
18
|
webId: user.id
|
18
19
|
});
|
19
20
|
} catch (e) {
|
@@ -0,0 +1 @@
|
|
1
|
+
Password reset for {{account.username}} account
|
@@ -0,0 +1 @@
|
|
1
|
+
Réinitialisation du mot de passe du compte {{account.username}}
|