solid-server 5.6.9-beta
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/.acl +10 -0
- package/.github/workflows/ci.yml +47 -0
- package/.nvmrc +1 -0
- package/.snyk +35 -0
- package/.well-known/.acl +15 -0
- package/CHANGELOG.md +198 -0
- package/CONTRIBUTING.md +139 -0
- package/CONTRIBUTORS.md +36 -0
- package/Dockerfile +22 -0
- package/LICENSE.md +23 -0
- package/README.md +453 -0
- package/bin/lib/cli-utils.js +85 -0
- package/bin/lib/cli.js +39 -0
- package/bin/lib/init.js +94 -0
- package/bin/lib/invalidUsernames.js +148 -0
- package/bin/lib/migrateLegacyResources.js +69 -0
- package/bin/lib/options.js +399 -0
- package/bin/lib/start.js +148 -0
- package/bin/lib/updateIndex.js +56 -0
- package/bin/solid +3 -0
- package/bin/solid-test +12 -0
- package/bin/solid.js +3 -0
- package/common/css/solid.css +58 -0
- package/common/fonts/glyphicons-halflings-regular.eot +0 -0
- package/common/fonts/glyphicons-halflings-regular.svg +288 -0
- package/common/fonts/glyphicons-halflings-regular.ttf +0 -0
- package/common/fonts/glyphicons-halflings-regular.woff +0 -0
- package/common/fonts/glyphicons-halflings-regular.woff2 +0 -0
- package/common/img/.gitkeep +0 -0
- package/common/js/auth-buttons.js +65 -0
- package/common/js/solid.js +454 -0
- package/common/well-known/security.txt +2 -0
- package/config/defaults.js +25 -0
- package/config/usernames-blacklist.json +4 -0
- package/config.json-default +22 -0
- package/default-templates/emails/delete-account.js +49 -0
- package/default-templates/emails/invalid-username.js +30 -0
- package/default-templates/emails/reset-password.js +49 -0
- package/default-templates/emails/welcome.js +39 -0
- package/default-templates/new-account/.acl +26 -0
- package/default-templates/new-account/.meta +5 -0
- package/default-templates/new-account/.meta.acl +25 -0
- package/default-templates/new-account/.well-known/.acl +19 -0
- package/default-templates/new-account/favicon.ico +0 -0
- package/default-templates/new-account/favicon.ico.acl +26 -0
- package/default-templates/new-account/inbox/.acl +26 -0
- package/default-templates/new-account/private/.acl +10 -0
- package/default-templates/new-account/profile/.acl +19 -0
- package/default-templates/new-account/profile/card$.ttl +25 -0
- package/default-templates/new-account/public/.acl +19 -0
- package/default-templates/new-account/robots.txt +3 -0
- package/default-templates/new-account/robots.txt.acl +26 -0
- package/default-templates/new-account/settings/.acl +20 -0
- package/default-templates/new-account/settings/prefs.ttl +15 -0
- package/default-templates/new-account/settings/privateTypeIndex.ttl +4 -0
- package/default-templates/new-account/settings/publicTypeIndex.ttl +4 -0
- package/default-templates/new-account/settings/publicTypeIndex.ttl.acl +25 -0
- package/default-templates/new-account/settings/serverSide.ttl.acl +13 -0
- package/default-templates/new-account/settings/serverSide.ttl.inactive +12 -0
- package/default-templates/server/.acl +10 -0
- package/default-templates/server/.well-known/.acl +15 -0
- package/default-templates/server/favicon.ico +0 -0
- package/default-templates/server/favicon.ico.acl +15 -0
- package/default-templates/server/index.html +55 -0
- package/default-templates/server/robots.txt +3 -0
- package/default-templates/server/robots.txt.acl +15 -0
- package/default-views/account/account-deleted.hbs +17 -0
- package/default-views/account/delete-confirm.hbs +51 -0
- package/default-views/account/delete-link-sent.hbs +17 -0
- package/default-views/account/delete.hbs +51 -0
- package/default-views/account/invalid-username.hbs +22 -0
- package/default-views/account/register-disabled.hbs +6 -0
- package/default-views/account/register-form.hbs +132 -0
- package/default-views/account/register.hbs +24 -0
- package/default-views/auth/auth-hidden-fields.hbs +8 -0
- package/default-views/auth/change-password.hbs +58 -0
- package/default-views/auth/goodbye.hbs +23 -0
- package/default-views/auth/login-required.hbs +34 -0
- package/default-views/auth/login-tls.hbs +11 -0
- package/default-views/auth/login-username-password.hbs +28 -0
- package/default-views/auth/login.hbs +55 -0
- package/default-views/auth/no-permission.hbs +29 -0
- package/default-views/auth/password-changed.hbs +27 -0
- package/default-views/auth/reset-link-sent.hbs +21 -0
- package/default-views/auth/reset-password.hbs +52 -0
- package/default-views/auth/sharing.hbs +49 -0
- package/default-views/shared/create-account.hbs +8 -0
- package/default-views/shared/error.hbs +5 -0
- package/docs/how-to-delete-your-account.md +56 -0
- package/docs/login-and-grant-access-to-application.md +32 -0
- package/examples/custom-error-handling.js +31 -0
- package/examples/ldp-with-webid.js +12 -0
- package/examples/simple-express-app.js +20 -0
- package/examples/simple-ldp-server.js +8 -0
- package/favicon.ico +0 -0
- package/favicon.ico.acl +15 -0
- package/index.html +48 -0
- package/index.js +3 -0
- package/lib/acl-checker.js +274 -0
- package/lib/api/accounts/user-accounts.js +88 -0
- package/lib/api/authn/force-user.js +21 -0
- package/lib/api/authn/index.js +5 -0
- package/lib/api/authn/webid-oidc.js +202 -0
- package/lib/api/authn/webid-tls.js +69 -0
- package/lib/api/index.js +6 -0
- package/lib/capability-discovery.js +54 -0
- package/lib/common/fs-utils.js +43 -0
- package/lib/common/template-utils.js +50 -0
- package/lib/common/user-utils.js +28 -0
- package/lib/create-app.js +322 -0
- package/lib/create-server.js +107 -0
- package/lib/debug.js +17 -0
- package/lib/handlers/allow.js +82 -0
- package/lib/handlers/auth-proxy.js +63 -0
- package/lib/handlers/copy.js +39 -0
- package/lib/handlers/cors-proxy.js +95 -0
- package/lib/handlers/delete.js +23 -0
- package/lib/handlers/error-pages.js +212 -0
- package/lib/handlers/get.js +219 -0
- package/lib/handlers/index.js +42 -0
- package/lib/handlers/options.js +33 -0
- package/lib/handlers/patch/n3-patch-parser.js +49 -0
- package/lib/handlers/patch/sparql-update-parser.js +16 -0
- package/lib/handlers/patch.js +203 -0
- package/lib/handlers/post.js +99 -0
- package/lib/handlers/put.js +56 -0
- package/lib/handlers/restrict-to-top-domain.js +13 -0
- package/lib/header.js +136 -0
- package/lib/http-error.js +34 -0
- package/lib/ldp-container.js +161 -0
- package/lib/ldp-copy.js +73 -0
- package/lib/ldp-middleware.js +32 -0
- package/lib/ldp.js +620 -0
- package/lib/lock.js +10 -0
- package/lib/metadata.js +10 -0
- package/lib/models/account-manager.js +603 -0
- package/lib/models/account-template.js +152 -0
- package/lib/models/authenticator.js +333 -0
- package/lib/models/oidc-manager.js +53 -0
- package/lib/models/solid-host.js +131 -0
- package/lib/models/user-account.js +112 -0
- package/lib/models/webid-tls-certificate.js +184 -0
- package/lib/payment-pointer-discovery.js +83 -0
- package/lib/requests/add-cert-request.js +138 -0
- package/lib/requests/auth-request.js +234 -0
- package/lib/requests/create-account-request.js +468 -0
- package/lib/requests/delete-account-confirm-request.js +170 -0
- package/lib/requests/delete-account-request.js +144 -0
- package/lib/requests/login-request.js +205 -0
- package/lib/requests/password-change-request.js +201 -0
- package/lib/requests/password-reset-email-request.js +199 -0
- package/lib/requests/sharing-request.js +259 -0
- package/lib/resource-mapper.js +198 -0
- package/lib/server-config.js +167 -0
- package/lib/services/blacklist-service.js +33 -0
- package/lib/services/email-service.js +162 -0
- package/lib/services/token-service.js +47 -0
- package/lib/utils.js +254 -0
- package/lib/webid/index.js +13 -0
- package/lib/webid/lib/get.js +27 -0
- package/lib/webid/lib/parse.js +12 -0
- package/lib/webid/tls/index.js +185 -0
- package/package.json +172 -0
- package/renovate.json +5 -0
- package/robots.txt +3 -0
- package/robots.txt.acl +15 -0
- package/static/account-recovery.html +78 -0
- package/static/popup-redirect.html +1 -0
- package/static/signup.html +108 -0
- package/static/signup.html.acl +14 -0
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
/* global owaspPasswordStrengthTest, TextEncoder, crypto, fetch */
|
|
2
|
+
(function () {
|
|
3
|
+
'use strict'
|
|
4
|
+
|
|
5
|
+
const PasswordValidator = function (passwordField, repeatedPasswordField) {
|
|
6
|
+
if (
|
|
7
|
+
passwordField === null || passwordField === undefined ||
|
|
8
|
+
repeatedPasswordField === null || repeatedPasswordField === undefined
|
|
9
|
+
) {
|
|
10
|
+
return
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
this.passwordField = passwordField
|
|
14
|
+
this.repeatedPasswordField = repeatedPasswordField
|
|
15
|
+
|
|
16
|
+
this.fetchDomNodes()
|
|
17
|
+
this.bindEvents()
|
|
18
|
+
|
|
19
|
+
this.currentStrengthLevel = 0
|
|
20
|
+
this.errors = []
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const FEEDBACK_SUCCESS = 'success'
|
|
24
|
+
const FEEDBACK_WARNING = 'warning'
|
|
25
|
+
const FEEDBACK_ERROR = 'error'
|
|
26
|
+
|
|
27
|
+
const ICON_SUCCESS = 'glyphicon-ok'
|
|
28
|
+
const ICON_WARNING = 'glyphicon-warning-sign'
|
|
29
|
+
const ICON_ERROR = 'glyphicon-remove'
|
|
30
|
+
|
|
31
|
+
const VALIDATION_SUCCESS = 'has-success'
|
|
32
|
+
const VALIDATION_WARNING = 'has-warning'
|
|
33
|
+
const VALIDATION_ERROR = 'has-error'
|
|
34
|
+
|
|
35
|
+
const STRENGTH_PROGRESS_0 = 'progress-bar-danger level-0'
|
|
36
|
+
const STRENGTH_PROGRESS_1 = 'progress-bar-danger level-1'
|
|
37
|
+
const STRENGTH_PROGRESS_2 = 'progress-bar-warning level-2'
|
|
38
|
+
const STRENGTH_PROGRESS_3 = 'progress-bar-success level-3'
|
|
39
|
+
const STRENGTH_PROGRESS_4 = 'progress-bar-success level-4'
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Prefetch all dom nodes at initialisation in order to gain time at execution since DOM manipulations
|
|
43
|
+
* are really time consuming
|
|
44
|
+
*/
|
|
45
|
+
PasswordValidator.prototype.fetchDomNodes = function () {
|
|
46
|
+
this.form = this.passwordField.closest('form')
|
|
47
|
+
|
|
48
|
+
this.disablePasswordChecks = this.passwordField.classList.contains('disable-password-checks')
|
|
49
|
+
|
|
50
|
+
this.passwordGroup = this.passwordField.closest('.form-group')
|
|
51
|
+
this.passwordFeedback = this.passwordGroup.querySelector('.form-control-feedback')
|
|
52
|
+
this.passwordStrengthMeter = this.passwordGroup.querySelector('.progress-bar')
|
|
53
|
+
this.passwordHelpText = this.passwordGroup.querySelector('.help-block')
|
|
54
|
+
|
|
55
|
+
this.repeatedPasswordGroup = this.repeatedPasswordField.closest('.form-group')
|
|
56
|
+
this.repeatedPasswordFeedback = this.repeatedPasswordGroup.querySelector('.form-control-feedback')
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
PasswordValidator.prototype.bindEvents = function () {
|
|
60
|
+
this.passwordField.addEventListener('focus', this.resetPasswordFeedback.bind(this))
|
|
61
|
+
this.passwordField.addEventListener('keyup', this.instantFeedbackForPassword.bind(this))
|
|
62
|
+
this.repeatedPasswordField.addEventListener('keyup', this.validateRepeatedPassword.bind(this))
|
|
63
|
+
this.passwordField.addEventListener('blur', this.validatePassword.bind(this))
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Events Listeners
|
|
68
|
+
*/
|
|
69
|
+
|
|
70
|
+
PasswordValidator.prototype.resetPasswordFeedback = function () {
|
|
71
|
+
this.errors = []
|
|
72
|
+
this.resetValidation(this.passwordGroup)
|
|
73
|
+
this.resetFeedbackIcon(this.passwordFeedback)
|
|
74
|
+
if (!this.disablePasswordChecks) {
|
|
75
|
+
this.displayPasswordErrors()
|
|
76
|
+
this.instantFeedbackForPassword()
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Validate password on the fly to provide the user a visual strength meter
|
|
82
|
+
*/
|
|
83
|
+
PasswordValidator.prototype.instantFeedbackForPassword = function () {
|
|
84
|
+
const passwordStrength = this.getPasswordStrength(this.passwordField.value)
|
|
85
|
+
const strengthLevel = this.getStrengthLevel(passwordStrength)
|
|
86
|
+
|
|
87
|
+
if (this.currentStrengthLevel === strengthLevel) {
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
this.currentStrengthLevel = strengthLevel
|
|
92
|
+
|
|
93
|
+
this.updateStrengthMeter()
|
|
94
|
+
|
|
95
|
+
if (this.repeatedPasswordField.value !== '') {
|
|
96
|
+
this.updateRepeatedPasswordFeedback()
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Validate password and display the error(s) message(s)
|
|
102
|
+
*/
|
|
103
|
+
PasswordValidator.prototype.validatePassword = function () {
|
|
104
|
+
this.errors = []
|
|
105
|
+
const password = this.passwordField.value
|
|
106
|
+
|
|
107
|
+
if (!this.disablePasswordChecks) {
|
|
108
|
+
const passwordStrength = this.getPasswordStrength(password)
|
|
109
|
+
this.currentStrengthLevel = this.getStrengthLevel(passwordStrength)
|
|
110
|
+
|
|
111
|
+
if (passwordStrength.errors) {
|
|
112
|
+
this.addPasswordError(passwordStrength.errors)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
this.checkLeakedPassword(password).then(this.handleLeakedPasswordResponse.bind(this))
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
this.setPasswordFeedback()
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Validate the repeated password upon typing
|
|
123
|
+
*/
|
|
124
|
+
PasswordValidator.prototype.validateRepeatedPassword = function () {
|
|
125
|
+
this.updateRepeatedPasswordFeedback()
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* User Feedback manipulators
|
|
130
|
+
*/
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Update the strength meter based on OWASP feedback
|
|
134
|
+
*/
|
|
135
|
+
PasswordValidator.prototype.updateStrengthMeter = function () {
|
|
136
|
+
this.resetStrengthMeter()
|
|
137
|
+
|
|
138
|
+
this.passwordStrengthMeter.classList.add.apply(
|
|
139
|
+
this.passwordStrengthMeter.classList,
|
|
140
|
+
this.tokenize(this.getStrengthLevelProgressClass())
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
PasswordValidator.prototype.setPasswordFeedback = function () {
|
|
145
|
+
const feedback = this.getFeedbackFromLevel()
|
|
146
|
+
this.updateStrengthMeter()
|
|
147
|
+
this.displayPasswordErrors()
|
|
148
|
+
this.setFeedbackForField(feedback, this.passwordField)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Update the repeated password feedback icon and color
|
|
153
|
+
*/
|
|
154
|
+
PasswordValidator.prototype.updateRepeatedPasswordFeedback = function () {
|
|
155
|
+
const feedback = this.checkPasswordFieldsEquality() ? FEEDBACK_SUCCESS : FEEDBACK_ERROR
|
|
156
|
+
this.setFeedbackForField(feedback, this.repeatedPasswordField)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Display the given feedback on the field
|
|
161
|
+
* @param {string} feedback success|error|warning
|
|
162
|
+
* @param {HTMLElement} field
|
|
163
|
+
*/
|
|
164
|
+
PasswordValidator.prototype.setFeedbackForField = function (feedback, field) {
|
|
165
|
+
const formGroup = this.getFormGroupElementForField(field)
|
|
166
|
+
const visualFeedback = this.getFeedbackElementForField(field)
|
|
167
|
+
|
|
168
|
+
this.resetValidation(formGroup)
|
|
169
|
+
this.resetFeedbackIcon(visualFeedback)
|
|
170
|
+
|
|
171
|
+
visualFeedback.classList.remove('hidden')
|
|
172
|
+
|
|
173
|
+
visualFeedback.classList
|
|
174
|
+
.add
|
|
175
|
+
.apply(
|
|
176
|
+
visualFeedback.classList,
|
|
177
|
+
this.tokenize(this.getFeedbackIconClass(feedback))
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
formGroup.classList
|
|
181
|
+
.add
|
|
182
|
+
.apply(
|
|
183
|
+
formGroup.classList,
|
|
184
|
+
this.tokenize(this.getValidationClass(feedback))
|
|
185
|
+
)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Password Strength Helpers
|
|
190
|
+
*/
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Get OWASP feedback on the given password. Returns false if the password is empty
|
|
194
|
+
* @param password
|
|
195
|
+
* @returns {object|false}
|
|
196
|
+
*/
|
|
197
|
+
PasswordValidator.prototype.getPasswordStrength = function (password) {
|
|
198
|
+
if (password === '') {
|
|
199
|
+
return false
|
|
200
|
+
}
|
|
201
|
+
return owaspPasswordStrengthTest.test(password)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Get the password strength level based on password strength feedback object given by OWASP
|
|
206
|
+
* @param passwordStrength
|
|
207
|
+
* @returns {number}
|
|
208
|
+
*/
|
|
209
|
+
PasswordValidator.prototype.getStrengthLevel = function (passwordStrength) {
|
|
210
|
+
if (passwordStrength === false) {
|
|
211
|
+
return 0
|
|
212
|
+
}
|
|
213
|
+
if (passwordStrength.requiredTestErrors.length !== 0) {
|
|
214
|
+
return 1
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (passwordStrength.strong === false) {
|
|
218
|
+
return 2
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (passwordStrength.isPassphrase === false || passwordStrength.optionalTestErrors.length !== 0) {
|
|
222
|
+
return 3
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return 4
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
PasswordValidator.prototype.LEVEL_TO_FEEDBACK_MAP = [
|
|
229
|
+
FEEDBACK_ERROR,
|
|
230
|
+
FEEDBACK_ERROR,
|
|
231
|
+
FEEDBACK_WARNING,
|
|
232
|
+
FEEDBACK_SUCCESS,
|
|
233
|
+
FEEDBACK_SUCCESS
|
|
234
|
+
]
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* @returns {string}
|
|
238
|
+
*/
|
|
239
|
+
PasswordValidator.prototype.getFeedbackFromLevel = function () {
|
|
240
|
+
return this.LEVEL_TO_FEEDBACK_MAP[this.currentStrengthLevel]
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
PasswordValidator.prototype.LEVEL_TO_PROGRESS_MAP = [
|
|
244
|
+
STRENGTH_PROGRESS_0,
|
|
245
|
+
STRENGTH_PROGRESS_1,
|
|
246
|
+
STRENGTH_PROGRESS_2,
|
|
247
|
+
STRENGTH_PROGRESS_3,
|
|
248
|
+
STRENGTH_PROGRESS_4
|
|
249
|
+
]
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Get the CSS class for the meter based on the current level
|
|
253
|
+
*/
|
|
254
|
+
PasswordValidator.prototype.getStrengthLevelProgressClass = function () {
|
|
255
|
+
return this.LEVEL_TO_PROGRESS_MAP[this.currentStrengthLevel]
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
PasswordValidator.prototype.addPasswordError = function (error) {
|
|
259
|
+
this.errors.push(...(Array.isArray(error) ? error : [error]))
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
PasswordValidator.prototype.displayPasswordErrors = function () {
|
|
263
|
+
// Erase the error list content
|
|
264
|
+
while (this.passwordHelpText.firstChild) {
|
|
265
|
+
this.passwordHelpText.removeChild(this.passwordHelpText.firstChild)
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Add the errors in the stack to the DOM
|
|
269
|
+
this.errors.map((error) => {
|
|
270
|
+
const text = document.createTextNode(error)
|
|
271
|
+
const paragraph = document.createElement('p')
|
|
272
|
+
paragraph.appendChild(text)
|
|
273
|
+
this.passwordHelpText.appendChild(paragraph)
|
|
274
|
+
})
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
PasswordValidator.prototype.FEEDBACK_TO_ICON_MAP = []
|
|
278
|
+
PasswordValidator.prototype.FEEDBACK_TO_ICON_MAP[FEEDBACK_SUCCESS] = ICON_SUCCESS
|
|
279
|
+
PasswordValidator.prototype.FEEDBACK_TO_ICON_MAP[FEEDBACK_WARNING] = ICON_WARNING
|
|
280
|
+
PasswordValidator.prototype.FEEDBACK_TO_ICON_MAP[FEEDBACK_ERROR] = ICON_ERROR
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* @param success|error|warning feedback
|
|
284
|
+
*/
|
|
285
|
+
PasswordValidator.prototype.getFeedbackIconClass = function (feedback) {
|
|
286
|
+
return this.FEEDBACK_TO_ICON_MAP[feedback]
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
PasswordValidator.prototype.FEEDBACK_TO_VALIDATION_MAP = []
|
|
290
|
+
PasswordValidator.prototype.FEEDBACK_TO_VALIDATION_MAP[FEEDBACK_SUCCESS] = VALIDATION_SUCCESS
|
|
291
|
+
PasswordValidator.prototype.FEEDBACK_TO_VALIDATION_MAP[FEEDBACK_WARNING] = VALIDATION_WARNING
|
|
292
|
+
PasswordValidator.prototype.FEEDBACK_TO_VALIDATION_MAP[FEEDBACK_ERROR] = VALIDATION_ERROR
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* @param success|error|warning feedback
|
|
296
|
+
*/
|
|
297
|
+
PasswordValidator.prototype.getValidationClass = function (feedback) {
|
|
298
|
+
return this.FEEDBACK_TO_VALIDATION_MAP[feedback]
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Validators
|
|
303
|
+
*/
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Check if both password fields are equal
|
|
307
|
+
* @returns {boolean}
|
|
308
|
+
*/
|
|
309
|
+
PasswordValidator.prototype.checkPasswordFieldsEquality = function () {
|
|
310
|
+
return this.passwordField.value === this.repeatedPasswordField.value
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Check if the password is leaked
|
|
315
|
+
* @param password
|
|
316
|
+
*/
|
|
317
|
+
PasswordValidator.prototype.checkLeakedPassword = function (password) {
|
|
318
|
+
const url = 'https://api.pwnedpasswords.com/range/'
|
|
319
|
+
|
|
320
|
+
return new Promise(function (resolve, reject) {
|
|
321
|
+
this.sha1(password).then((digest) => {
|
|
322
|
+
const preFix = digest.slice(0, 5)
|
|
323
|
+
let suffix = digest.slice(5, digest.length)
|
|
324
|
+
suffix = suffix.toUpperCase()
|
|
325
|
+
|
|
326
|
+
return fetch(url + preFix)
|
|
327
|
+
.then(function (response) {
|
|
328
|
+
return response.text()
|
|
329
|
+
})
|
|
330
|
+
.then(function (data) {
|
|
331
|
+
resolve(data.indexOf(suffix) > -1)
|
|
332
|
+
})
|
|
333
|
+
.catch(function (err) {
|
|
334
|
+
reject(err)
|
|
335
|
+
})
|
|
336
|
+
})
|
|
337
|
+
}.bind(this))
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
PasswordValidator.prototype.handleLeakedPasswordResponse = function (hasPasswordLeaked) {
|
|
341
|
+
if (hasPasswordLeaked === true) {
|
|
342
|
+
this.currentStrengthLevel--
|
|
343
|
+
this.addPasswordError('This password was exposed in a data breach. Please use a more secure alternative one!')
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
this.setPasswordFeedback()
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* CSS Classes reseters
|
|
351
|
+
*/
|
|
352
|
+
|
|
353
|
+
PasswordValidator.prototype.resetValidation = function (el) {
|
|
354
|
+
const tokenizedClasses = this.tokenize(
|
|
355
|
+
VALIDATION_ERROR,
|
|
356
|
+
VALIDATION_WARNING,
|
|
357
|
+
VALIDATION_SUCCESS
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
el.classList.remove.apply(
|
|
361
|
+
el.classList,
|
|
362
|
+
tokenizedClasses
|
|
363
|
+
)
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
PasswordValidator.prototype.resetFeedbackIcon = function (el) {
|
|
367
|
+
const tokenizedClasses = this.tokenize(
|
|
368
|
+
ICON_ERROR,
|
|
369
|
+
ICON_WARNING,
|
|
370
|
+
ICON_SUCCESS
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
el.classList.remove.apply(
|
|
374
|
+
el.classList,
|
|
375
|
+
tokenizedClasses
|
|
376
|
+
)
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
PasswordValidator.prototype.resetStrengthMeter = function () {
|
|
380
|
+
const tokenizedClasses = this.tokenize(
|
|
381
|
+
STRENGTH_PROGRESS_1,
|
|
382
|
+
STRENGTH_PROGRESS_2,
|
|
383
|
+
STRENGTH_PROGRESS_3,
|
|
384
|
+
STRENGTH_PROGRESS_4
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
this.passwordStrengthMeter.classList.remove.apply(
|
|
388
|
+
this.passwordStrengthMeter.classList,
|
|
389
|
+
tokenizedClasses
|
|
390
|
+
)
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Helpers
|
|
395
|
+
*/
|
|
396
|
+
|
|
397
|
+
PasswordValidator.prototype.getFormGroupElementForField = function (field) {
|
|
398
|
+
if (field === this.passwordField) {
|
|
399
|
+
return this.passwordGroup
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (field === this.repeatedPasswordField) {
|
|
403
|
+
return this.repeatedPasswordGroup
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
PasswordValidator.prototype.getFeedbackElementForField = function (field) {
|
|
408
|
+
if (field === this.passwordField) {
|
|
409
|
+
return this.passwordFeedback
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
if (field === this.repeatedPasswordField) {
|
|
413
|
+
return this.repeatedPasswordFeedback
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Returns an array of strings ready to be applied on classList.add or classList.remove
|
|
419
|
+
* @returns {string[]}
|
|
420
|
+
*/
|
|
421
|
+
PasswordValidator.prototype.tokenize = function () {
|
|
422
|
+
const tokenArray = []
|
|
423
|
+
for (const i in arguments) {
|
|
424
|
+
tokenArray.push(arguments[i])
|
|
425
|
+
}
|
|
426
|
+
return tokenArray.join(' ').split(' ')
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
PasswordValidator.prototype.sha1 = function (str) {
|
|
430
|
+
const buffer = new TextEncoder('utf-8').encode(str)
|
|
431
|
+
|
|
432
|
+
return crypto.subtle.digest('SHA-1', buffer).then((hash) => {
|
|
433
|
+
return this.hex(hash)
|
|
434
|
+
})
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
PasswordValidator.prototype.hex = function (buffer) {
|
|
438
|
+
const hexCodes = []
|
|
439
|
+
const view = new DataView(buffer)
|
|
440
|
+
for (let i = 0; i < view.byteLength; i += 4) {
|
|
441
|
+
const value = view.getUint32(i)
|
|
442
|
+
const stringValue = value.toString(16)
|
|
443
|
+
const padding = '00000000'
|
|
444
|
+
const paddedValue = (padding + stringValue).slice(-padding.length)
|
|
445
|
+
hexCodes.push(paddedValue)
|
|
446
|
+
}
|
|
447
|
+
return hexCodes.join('')
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
new PasswordValidator(
|
|
451
|
+
document.getElementById('password'),
|
|
452
|
+
document.getElementById('repeat_password')
|
|
453
|
+
)
|
|
454
|
+
})()
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
auth: 'oidc',
|
|
5
|
+
localAuth: {
|
|
6
|
+
tls: true,
|
|
7
|
+
password: true
|
|
8
|
+
},
|
|
9
|
+
configPath: './config',
|
|
10
|
+
dbPath: './.db',
|
|
11
|
+
port: 8443,
|
|
12
|
+
serverUri: 'https://localhost:8443',
|
|
13
|
+
webid: true,
|
|
14
|
+
strictOrigin: true,
|
|
15
|
+
trustedOrigins: [],
|
|
16
|
+
dataBrowserPath: 'default'
|
|
17
|
+
|
|
18
|
+
// For use in Enterprises to configure a HTTP proxy for all outbound HTTP requests from the SOLID server (we use
|
|
19
|
+
// https://www.npmjs.com/package/global-tunnel-ng).
|
|
20
|
+
// "httpProxy": {
|
|
21
|
+
// "tunnel": "neither",
|
|
22
|
+
// "host": "proxy.example.com",
|
|
23
|
+
// "port": 12345
|
|
24
|
+
// }
|
|
25
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"root": "./data",
|
|
3
|
+
"port": "8443",
|
|
4
|
+
"serverUri": "https://localhost:8443",
|
|
5
|
+
"webid": true,
|
|
6
|
+
"mount": "/",
|
|
7
|
+
"configPath": "./config",
|
|
8
|
+
"dbPath": "./.db",
|
|
9
|
+
"sslKey": "./privkey.pem",
|
|
10
|
+
"sslCert": "./fullchain.pem",
|
|
11
|
+
"multiuser": true,
|
|
12
|
+
"corsProxy": "/proxy",
|
|
13
|
+
"server": {
|
|
14
|
+
"name": "",
|
|
15
|
+
"description": "",
|
|
16
|
+
"logo": ""
|
|
17
|
+
},
|
|
18
|
+
"enforceToc": true,
|
|
19
|
+
"disablePasswordChecks": false,
|
|
20
|
+
"tocUri": "https://your-toc",
|
|
21
|
+
"supportEmail": "Your support email address"
|
|
22
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Returns a partial Email object (minus the `to` and `from` properties),
|
|
5
|
+
* suitable for sending with Nodemailer.
|
|
6
|
+
*
|
|
7
|
+
* Used to send a Delete Account email, upon user request
|
|
8
|
+
*
|
|
9
|
+
* @param data {Object}
|
|
10
|
+
*
|
|
11
|
+
* @param data.deleteUrl {string}
|
|
12
|
+
* @param data.webId {string}
|
|
13
|
+
*
|
|
14
|
+
* @return {Object}
|
|
15
|
+
*/
|
|
16
|
+
function render (data) {
|
|
17
|
+
return {
|
|
18
|
+
subject: 'Delete Solid-account request',
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Text version
|
|
22
|
+
*/
|
|
23
|
+
text: `Hi,
|
|
24
|
+
|
|
25
|
+
We received a request to delete your Solid account, ${data.webId}
|
|
26
|
+
|
|
27
|
+
To delete your account, click on the following link:
|
|
28
|
+
|
|
29
|
+
${data.deleteUrl}
|
|
30
|
+
|
|
31
|
+
If you did not mean to delete your account, ignore this email.`,
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* HTML version
|
|
35
|
+
*/
|
|
36
|
+
html: `<p>Hi,</p>
|
|
37
|
+
|
|
38
|
+
<p>We received a request to delete your Solid account, ${data.webId}</p>
|
|
39
|
+
|
|
40
|
+
<p>To delete your account, click on the following link:</p>
|
|
41
|
+
|
|
42
|
+
<p><a href="${data.deleteUrl}">${data.deleteUrl}</a></p>
|
|
43
|
+
|
|
44
|
+
<p>If you did not mean to delete your account, ignore this email.</p>
|
|
45
|
+
`
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports.render = render
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module.exports.render = render
|
|
2
|
+
|
|
3
|
+
function render (data) {
|
|
4
|
+
return {
|
|
5
|
+
subject: `Invalid username for account ${data.accountUri}`,
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Text version
|
|
9
|
+
*/
|
|
10
|
+
text: `Hi,
|
|
11
|
+
|
|
12
|
+
We're sorry to inform you that the username for account ${data.accountUri} is not allowed after changes to username policy.
|
|
13
|
+
|
|
14
|
+
This account has been set to be deleted at ${data.dateOfRemoval}.
|
|
15
|
+
|
|
16
|
+
${data.supportEmail ? `Please contact ${data.supportEmail} if you want to move your account.` : ''}`,
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* HTML version
|
|
20
|
+
*/
|
|
21
|
+
html: `<p>Hi,</p>
|
|
22
|
+
|
|
23
|
+
<p>We're sorry to inform you that the username for account ${data.accountUri} is not allowed after changes to username policy.</p>
|
|
24
|
+
|
|
25
|
+
<p>This account has been set to be deleted at ${data.dateOfRemoval}.</p>
|
|
26
|
+
|
|
27
|
+
${data.supportEmail ? `<p>Please contact ${data.supportEmail} if you want to move your account.</p>` : ''}
|
|
28
|
+
`
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Returns a partial Email object (minus the `to` and `from` properties),
|
|
5
|
+
* suitable for sending with Nodemailer.
|
|
6
|
+
*
|
|
7
|
+
* Used to send a Reset Password email, upon user request
|
|
8
|
+
*
|
|
9
|
+
* @param data {Object}
|
|
10
|
+
*
|
|
11
|
+
* @param data.resetUrl {string}
|
|
12
|
+
* @param data.webId {string}
|
|
13
|
+
*
|
|
14
|
+
* @return {Object}
|
|
15
|
+
*/
|
|
16
|
+
function render (data) {
|
|
17
|
+
return {
|
|
18
|
+
subject: 'Account password reset',
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Text version
|
|
22
|
+
*/
|
|
23
|
+
text: `Hi,
|
|
24
|
+
|
|
25
|
+
We received a request to reset your password for your Solid account, ${data.webId}
|
|
26
|
+
|
|
27
|
+
To reset your password, click on the following link:
|
|
28
|
+
|
|
29
|
+
${data.resetUrl}
|
|
30
|
+
|
|
31
|
+
If you did not mean to reset your password, ignore this email, your password will not change.`,
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* HTML version
|
|
35
|
+
*/
|
|
36
|
+
html: `<p>Hi,</p>
|
|
37
|
+
|
|
38
|
+
<p>We received a request to reset your password for your Solid account, ${data.webId}</p>
|
|
39
|
+
|
|
40
|
+
<p>To reset your password, click on the following link:</p>
|
|
41
|
+
|
|
42
|
+
<p><a href="${data.resetUrl}">${data.resetUrl}</a></p>
|
|
43
|
+
|
|
44
|
+
<p>If you did not mean to reset your password, ignore this email, your password will not change.</p>
|
|
45
|
+
`
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports.render = render
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Returns a partial Email object (minus the `to` and `from` properties),
|
|
5
|
+
* suitable for sending with Nodemailer.
|
|
6
|
+
*
|
|
7
|
+
* Used to send a Welcome email after a new user account has been created.
|
|
8
|
+
*
|
|
9
|
+
* @param data {Object}
|
|
10
|
+
*
|
|
11
|
+
* @param data.webid {string}
|
|
12
|
+
*
|
|
13
|
+
* @return {Object}
|
|
14
|
+
*/
|
|
15
|
+
function render (data) {
|
|
16
|
+
return {
|
|
17
|
+
subject: 'Welcome to Solid',
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Text version of the Welcome email
|
|
21
|
+
*/
|
|
22
|
+
text: `Welcome to Solid!
|
|
23
|
+
|
|
24
|
+
Your account has been created.
|
|
25
|
+
|
|
26
|
+
Your Web Id: ${data.webid}`,
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* HTML version of the Welcome email
|
|
30
|
+
*/
|
|
31
|
+
html: `<p>Welcome to Solid!</p>
|
|
32
|
+
|
|
33
|
+
<p>Your account has been created.</p>
|
|
34
|
+
|
|
35
|
+
<p>Your Web Id: ${data.webid}</p>`
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports.render = render
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Root ACL resource for the user account
|
|
2
|
+
@prefix acl: <http://www.w3.org/ns/auth/acl#>.
|
|
3
|
+
@prefix foaf: <http://xmlns.com/foaf/0.1/>.
|
|
4
|
+
|
|
5
|
+
# The homepage is readable by the public
|
|
6
|
+
<#public>
|
|
7
|
+
a acl:Authorization;
|
|
8
|
+
acl:agentClass foaf:Agent;
|
|
9
|
+
acl:accessTo </>;
|
|
10
|
+
acl:mode acl:Read.
|
|
11
|
+
|
|
12
|
+
# The owner has full access to every resource in their pod.
|
|
13
|
+
# Other agents have no access rights,
|
|
14
|
+
# unless specifically authorized in other .acl resources.
|
|
15
|
+
<#owner>
|
|
16
|
+
a acl:Authorization;
|
|
17
|
+
acl:agent <{{webId}}>;
|
|
18
|
+
# Optional owner email, to be used for account recovery:
|
|
19
|
+
{{#if email}}acl:agent <mailto:{{{email}}}>;{{/if}}
|
|
20
|
+
# Set the access to the root storage folder itself
|
|
21
|
+
acl:accessTo </>;
|
|
22
|
+
# All resources will inherit this authorization, by default
|
|
23
|
+
acl:default </>;
|
|
24
|
+
# The owner has all of the access modes allowed
|
|
25
|
+
acl:mode
|
|
26
|
+
acl:Read, acl:Write, acl:Control.
|