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,148 @@
|
|
|
1
|
+
const fs = require('fs-extra')
|
|
2
|
+
const Handlebars = require('handlebars')
|
|
3
|
+
const path = require('path')
|
|
4
|
+
|
|
5
|
+
const { getAccountManager, loadConfig, loadUsernames } = require('./cli-utils')
|
|
6
|
+
const { isValidUsername } = require('../../lib/common/user-utils')
|
|
7
|
+
const blacklistService = require('../../lib/services/blacklist-service')
|
|
8
|
+
const { initConfigDir, initTemplateDirs } = require('../../lib/server-config')
|
|
9
|
+
const { fromServerConfig } = require('../../lib/models/oidc-manager')
|
|
10
|
+
|
|
11
|
+
const EmailService = require('../../lib/services/email-service')
|
|
12
|
+
const SolidHost = require('../../lib/models/solid-host')
|
|
13
|
+
|
|
14
|
+
module.exports = function (program) {
|
|
15
|
+
program
|
|
16
|
+
.command('invalidusernames')
|
|
17
|
+
.option('--notify', 'Will notify users with usernames that are invalid')
|
|
18
|
+
.option('--delete', 'Will delete users with usernames that are invalid')
|
|
19
|
+
.description('Manage usernames that are invalid')
|
|
20
|
+
.action(async (options) => {
|
|
21
|
+
const config = loadConfig(program, options)
|
|
22
|
+
if (!config.multiuser) {
|
|
23
|
+
return console.error('You are running a single user server, no need to check for invalid usernames')
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const invalidUsernames = getInvalidUsernames(config)
|
|
27
|
+
const host = SolidHost.from({ port: config.port, serverUri: config.serverUri })
|
|
28
|
+
const accountManager = getAccountManager(config, { host })
|
|
29
|
+
|
|
30
|
+
if (options.notify) {
|
|
31
|
+
return notifyUsers(invalidUsernames, accountManager, config)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (options.delete) {
|
|
35
|
+
return deleteUsers(invalidUsernames, accountManager, config, host)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
listUsernames(invalidUsernames)
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function backupIndexFile (username, accountManager, invalidUsernameTemplate, dateOfRemoval, supportEmail) {
|
|
43
|
+
const userDirectory = accountManager.accountDirFor(username)
|
|
44
|
+
const currentIndex = path.join(userDirectory, 'index.html')
|
|
45
|
+
const currentIndexExists = fs.existsSync(currentIndex)
|
|
46
|
+
const backupIndex = path.join(userDirectory, 'index.backup.html')
|
|
47
|
+
const backupIndexExists = fs.existsSync(backupIndex)
|
|
48
|
+
if (currentIndexExists && !backupIndexExists) {
|
|
49
|
+
fs.renameSync(currentIndex, backupIndex)
|
|
50
|
+
createNewIndexAcl(userDirectory)
|
|
51
|
+
createNewIndex(username, invalidUsernameTemplate, dateOfRemoval, supportEmail, currentIndex)
|
|
52
|
+
console.info(`index.html updated for user ${username}`)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function createNewIndex (username, invalidUsernameTemplate, dateOfRemoval, supportEmail, currentIndex) {
|
|
57
|
+
const newIndexSource = invalidUsernameTemplate({
|
|
58
|
+
username,
|
|
59
|
+
dateOfRemoval,
|
|
60
|
+
supportEmail
|
|
61
|
+
})
|
|
62
|
+
fs.writeFileSync(currentIndex, newIndexSource, 'utf-8')
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function createNewIndexAcl (userDirectory) {
|
|
66
|
+
const currentIndexAcl = path.join(userDirectory, 'index.html.acl')
|
|
67
|
+
const backupIndexAcl = path.join(userDirectory, 'index.backup.html.acl')
|
|
68
|
+
const currentIndexSource = fs.readFileSync(currentIndexAcl, 'utf-8')
|
|
69
|
+
const backupIndexSource = currentIndexSource.replace(/index.html/g, 'index.backup.html')
|
|
70
|
+
fs.writeFileSync(backupIndexAcl, backupIndexSource, 'utf-8')
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function deleteUsers (usernames, accountManager, config, host) {
|
|
74
|
+
const oidcManager = fromServerConfig({
|
|
75
|
+
...config,
|
|
76
|
+
host
|
|
77
|
+
})
|
|
78
|
+
const deletingUsers = usernames
|
|
79
|
+
.map(async username => {
|
|
80
|
+
try {
|
|
81
|
+
const user = accountManager.userAccountFrom({ username })
|
|
82
|
+
await oidcManager.users.deleteUser(user)
|
|
83
|
+
} catch (error) {
|
|
84
|
+
if (error.message !== 'No email given') {
|
|
85
|
+
// 'No email given' is an expected error that we want to ignore
|
|
86
|
+
throw error
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const userDirectory = accountManager.accountDirFor(username)
|
|
90
|
+
await fs.remove(userDirectory)
|
|
91
|
+
})
|
|
92
|
+
await Promise.all(deletingUsers)
|
|
93
|
+
console.info(`Deleted ${deletingUsers.length} users succeeded`)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function getInvalidUsernames (config) {
|
|
97
|
+
const usernames = loadUsernames(config)
|
|
98
|
+
return usernames.filter(username => !isValidUsername(username) || !blacklistService.validate(username))
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function listUsernames (usernames) {
|
|
102
|
+
if (usernames.length === 0) {
|
|
103
|
+
return console.info('No invalid usernames was found')
|
|
104
|
+
}
|
|
105
|
+
console.info(`${usernames.length} invalid usernames were found:${usernames.map(username => `\n- ${username}`)}`)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function notifyUsers (usernames, accountManager, config) {
|
|
109
|
+
const twoWeeksFromNow = Date.now() + 14 * 24 * 60 * 60 * 1000
|
|
110
|
+
const dateOfRemoval = (new Date(twoWeeksFromNow)).toLocaleDateString()
|
|
111
|
+
const { supportEmail } = config
|
|
112
|
+
|
|
113
|
+
updateIndexFiles(usernames, accountManager, dateOfRemoval, supportEmail)
|
|
114
|
+
await sendEmails(config, usernames, accountManager, dateOfRemoval, supportEmail)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async function sendEmails (config, usernames, accountManager, dateOfRemoval, supportEmail) {
|
|
118
|
+
if (config.email && config.email.host) {
|
|
119
|
+
const configPath = initConfigDir(config)
|
|
120
|
+
const templates = initTemplateDirs(configPath)
|
|
121
|
+
const users = await Promise.all(await usernames.map(async username => {
|
|
122
|
+
const emailAddress = await accountManager.loadAccountRecoveryEmail({ username })
|
|
123
|
+
const accountUri = accountManager.accountUriFor(username)
|
|
124
|
+
return { username, emailAddress, accountUri }
|
|
125
|
+
}))
|
|
126
|
+
const emailService = new EmailService(templates.email, config.email)
|
|
127
|
+
const sendingEmails = users
|
|
128
|
+
.filter(user => !!user.emailAddress)
|
|
129
|
+
.map(user => emailService.sendWithTemplate('invalid-username', {
|
|
130
|
+
to: user.emailAddress,
|
|
131
|
+
accountUri: user.accountUri,
|
|
132
|
+
dateOfRemoval,
|
|
133
|
+
supportEmail
|
|
134
|
+
}))
|
|
135
|
+
const emailsSent = await Promise.all(sendingEmails)
|
|
136
|
+
console.info(`${emailsSent.length} emails sent to users with invalid usernames`)
|
|
137
|
+
return
|
|
138
|
+
}
|
|
139
|
+
console.info('You have not configured an email service.')
|
|
140
|
+
console.info('Please set it up to send users email about their accounts')
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function updateIndexFiles (usernames, accountManager, dateOfRemoval, supportEmail) {
|
|
144
|
+
const invalidUsernameFilePath = path.join(process.cwd(), 'default-views', 'account', 'invalid-username.hbs')
|
|
145
|
+
const source = fs.readFileSync(invalidUsernameFilePath, 'utf-8')
|
|
146
|
+
const invalidUsernameTemplate = Handlebars.compile(source)
|
|
147
|
+
usernames.forEach(username => backupIndexFile(username, accountManager, invalidUsernameTemplate, dateOfRemoval, supportEmail))
|
|
148
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const Path = require('path')
|
|
3
|
+
const promisify = require('util').promisify
|
|
4
|
+
const readdir = promisify(fs.readdir)
|
|
5
|
+
const lstat = promisify(fs.lstat)
|
|
6
|
+
const rename = promisify(fs.rename)
|
|
7
|
+
|
|
8
|
+
/* Converts the old (pre-5.0.0) extensionless files to $-based files _with_ extensions
|
|
9
|
+
* to make them work in the new resource mapper (post-5.0.0).
|
|
10
|
+
* By default, all extensionless files (that used to be interpreted as Turtle) will now receive a '$.ttl' suffix. */
|
|
11
|
+
/* https://www.w3.org/DesignIssues/HTTPFilenameMapping.html */
|
|
12
|
+
|
|
13
|
+
module.exports = function (program) {
|
|
14
|
+
program
|
|
15
|
+
.command('migrate-legacy-resources')
|
|
16
|
+
.option('-p, --path <path>', 'Path to the data folder, defaults to \'data/\'')
|
|
17
|
+
.option('-s, --suffix <path>', 'The suffix to add to extensionless files, defaults to \'$.ttl\'')
|
|
18
|
+
.option('-v, --verbose', 'Path to the data folder')
|
|
19
|
+
.description('Migrate the data folder from node-solid-server 4 to node-solid-server 5')
|
|
20
|
+
.action(async (opts) => {
|
|
21
|
+
const verbose = opts.verbose
|
|
22
|
+
const suffix = opts.suffix || '$.ttl'
|
|
23
|
+
let paths = opts.path ? [opts.path] : ['data', 'config/templates']
|
|
24
|
+
paths = paths.map(path => path.startsWith(Path.sep) ? path : Path.join(process.cwd(), path))
|
|
25
|
+
try {
|
|
26
|
+
for (const path of paths) {
|
|
27
|
+
if (verbose) {
|
|
28
|
+
console.log(`Migrating files in ${path}`)
|
|
29
|
+
}
|
|
30
|
+
await migrate(path, suffix, verbose)
|
|
31
|
+
}
|
|
32
|
+
} catch (err) {
|
|
33
|
+
console.error(err)
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function migrate (path, suffix, verbose) {
|
|
39
|
+
const files = await readdir(path)
|
|
40
|
+
for (const file of files) {
|
|
41
|
+
const fullFilePath = Path.join(path, file)
|
|
42
|
+
const stat = await lstat(fullFilePath)
|
|
43
|
+
if (stat.isFile()) {
|
|
44
|
+
if (shouldMigrateFile(file)) {
|
|
45
|
+
const newFullFilePath = getNewFileName(fullFilePath, suffix)
|
|
46
|
+
if (verbose) {
|
|
47
|
+
console.log(`${fullFilePath}\n => ${newFullFilePath}`)
|
|
48
|
+
}
|
|
49
|
+
await rename(fullFilePath, newFullFilePath)
|
|
50
|
+
}
|
|
51
|
+
} else {
|
|
52
|
+
if (shouldMigrateFolder(file)) {
|
|
53
|
+
await migrate(fullFilePath, suffix, verbose)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function getNewFileName (fullFilePath, suffix) {
|
|
60
|
+
return fullFilePath + suffix
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function shouldMigrateFile (filename) {
|
|
64
|
+
return filename.indexOf('.') < 0
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function shouldMigrateFolder (foldername) {
|
|
68
|
+
return foldername[0] !== '.'
|
|
69
|
+
}
|
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const validUrl = require('valid-url')
|
|
4
|
+
const { URL } = require('url')
|
|
5
|
+
const { isEmail } = require('validator')
|
|
6
|
+
|
|
7
|
+
module.exports = [
|
|
8
|
+
// {
|
|
9
|
+
// abbr: 'v',
|
|
10
|
+
// flag: true,
|
|
11
|
+
// help: 'Print the logs to console\n'
|
|
12
|
+
// },
|
|
13
|
+
{
|
|
14
|
+
name: 'root',
|
|
15
|
+
help: "Root folder to serve (default: './data')",
|
|
16
|
+
question: 'Path to the folder you want to serve. Default is',
|
|
17
|
+
default: './data',
|
|
18
|
+
prompt: true,
|
|
19
|
+
filter: (value) => path.resolve(value)
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: 'port',
|
|
23
|
+
help: 'SSL port to use',
|
|
24
|
+
question: 'SSL port to run on. Default is',
|
|
25
|
+
default: '8443',
|
|
26
|
+
prompt: true
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: 'server-uri',
|
|
30
|
+
question: 'Solid server uri (with protocol, hostname and port)',
|
|
31
|
+
help: "Solid server uri (default: 'https://localhost:8443')",
|
|
32
|
+
default: 'https://localhost:8443',
|
|
33
|
+
validate: validUri,
|
|
34
|
+
prompt: true
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: 'webid',
|
|
38
|
+
help: 'Enable WebID authentication and access control (uses HTTPS)',
|
|
39
|
+
flag: true,
|
|
40
|
+
default: true,
|
|
41
|
+
question: 'Enable WebID authentication',
|
|
42
|
+
prompt: true
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: 'mount',
|
|
46
|
+
help: "Serve on a specific URL path (default: '/')",
|
|
47
|
+
question: 'Serve Solid on URL path',
|
|
48
|
+
default: '/',
|
|
49
|
+
prompt: true
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: 'config-path',
|
|
53
|
+
question: 'Path to the config directory (for example: ./config)',
|
|
54
|
+
default: './config',
|
|
55
|
+
prompt: true
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: 'config-file',
|
|
59
|
+
question: 'Path to the config file (for example: ./config.json)',
|
|
60
|
+
default: './config.json',
|
|
61
|
+
prompt: true
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: 'db-path',
|
|
65
|
+
question: 'Path to the server metadata db directory (for users/apps etc)',
|
|
66
|
+
default: './.db',
|
|
67
|
+
prompt: true
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: 'auth',
|
|
71
|
+
help: 'Pick an authentication strategy for WebID: `tls` or `oidc`',
|
|
72
|
+
question: 'Select authentication strategy',
|
|
73
|
+
type: 'list',
|
|
74
|
+
choices: [
|
|
75
|
+
'WebID-OpenID Connect'
|
|
76
|
+
],
|
|
77
|
+
prompt: false,
|
|
78
|
+
default: 'WebID-OpenID Connect',
|
|
79
|
+
filter: (value) => {
|
|
80
|
+
if (value === 'WebID-OpenID Connect') return 'oidc'
|
|
81
|
+
},
|
|
82
|
+
when: (answers) => {
|
|
83
|
+
return answers.webid
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: 'use-owner',
|
|
88
|
+
question: 'Do you already have a WebID?',
|
|
89
|
+
type: 'confirm',
|
|
90
|
+
default: false,
|
|
91
|
+
hide: true
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: 'owner',
|
|
95
|
+
help: 'Set the owner of the storage (overwrites the root ACL file)',
|
|
96
|
+
question: 'Your webid (to overwrite the root ACL with)',
|
|
97
|
+
prompt: false,
|
|
98
|
+
validate: function (value) {
|
|
99
|
+
if (value === '' || !value.startsWith('http')) {
|
|
100
|
+
return 'Enter a valid Webid'
|
|
101
|
+
}
|
|
102
|
+
return true
|
|
103
|
+
},
|
|
104
|
+
when: function (answers) {
|
|
105
|
+
return answers['use-owner']
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
name: 'ssl-key',
|
|
110
|
+
help: 'Path to the SSL private key in PEM format',
|
|
111
|
+
validate: validPath,
|
|
112
|
+
prompt: true
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
name: 'ssl-cert',
|
|
116
|
+
help: 'Path to the SSL certificate key in PEM format',
|
|
117
|
+
validate: validPath,
|
|
118
|
+
prompt: true
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
name: 'no-reject-unauthorized',
|
|
122
|
+
help: 'Accept self-signed certificates',
|
|
123
|
+
flag: true,
|
|
124
|
+
default: false,
|
|
125
|
+
prompt: false
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
name: 'multiuser',
|
|
129
|
+
help: 'Enable multi-user mode',
|
|
130
|
+
question: 'Enable multi-user mode',
|
|
131
|
+
flag: true,
|
|
132
|
+
default: false,
|
|
133
|
+
prompt: true
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: 'idp',
|
|
137
|
+
help: 'Obsolete; use --multiuser',
|
|
138
|
+
prompt: false
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
name: 'no-live',
|
|
142
|
+
help: 'Disable live support through WebSockets',
|
|
143
|
+
flag: true,
|
|
144
|
+
default: false
|
|
145
|
+
},
|
|
146
|
+
// {
|
|
147
|
+
// full: 'default-app',
|
|
148
|
+
// help: 'URI to use as a default app for resources (default: https://linkeddata.github.io/warp/#/list/)'
|
|
149
|
+
// },
|
|
150
|
+
{
|
|
151
|
+
name: 'use-cors-proxy',
|
|
152
|
+
help: 'Do you want to have a CORS proxy endpoint?',
|
|
153
|
+
flag: true,
|
|
154
|
+
default: false,
|
|
155
|
+
hide: true
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
name: 'proxy',
|
|
159
|
+
help: 'Obsolete; use --corsProxy',
|
|
160
|
+
prompt: false
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
name: 'cors-proxy',
|
|
164
|
+
help: 'Serve the CORS proxy on this path',
|
|
165
|
+
when: function (answers) {
|
|
166
|
+
return answers['use-cors-proxy']
|
|
167
|
+
},
|
|
168
|
+
default: '/proxy',
|
|
169
|
+
prompt: true
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
name: 'auth-proxy',
|
|
173
|
+
help: 'Object with path/server pairs to reverse proxy',
|
|
174
|
+
default: {},
|
|
175
|
+
prompt: false,
|
|
176
|
+
hide: true
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
name: 'suppress-data-browser',
|
|
180
|
+
help: 'Suppress provision of a data browser',
|
|
181
|
+
flag: true,
|
|
182
|
+
prompt: false,
|
|
183
|
+
default: false,
|
|
184
|
+
hide: false
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
name: 'data-browser-path',
|
|
188
|
+
help: 'An HTML file which is sent to allow users to browse the data (eg using mashlib.js)',
|
|
189
|
+
question: 'Path of data viewer page (defaults to using mashlib)',
|
|
190
|
+
validate: validPath,
|
|
191
|
+
default: 'default',
|
|
192
|
+
prompt: false
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
name: 'suffix-acl',
|
|
196
|
+
full: 'suffix-acl',
|
|
197
|
+
help: "Suffix for acl files (default: '.acl')",
|
|
198
|
+
default: '.acl',
|
|
199
|
+
prompt: false
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
name: 'suffix-meta',
|
|
203
|
+
full: 'suffix-meta',
|
|
204
|
+
help: "Suffix for metadata files (default: '.meta')",
|
|
205
|
+
default: '.meta',
|
|
206
|
+
prompt: false
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
name: 'secret',
|
|
210
|
+
help: 'Secret used to sign the session ID cookie (e.g. "your secret phrase")',
|
|
211
|
+
question: 'Session secret for cookie',
|
|
212
|
+
default: 'random',
|
|
213
|
+
prompt: false,
|
|
214
|
+
filter: function (value) {
|
|
215
|
+
if (value === '' || value === 'random') {
|
|
216
|
+
return
|
|
217
|
+
}
|
|
218
|
+
return value
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
// {
|
|
222
|
+
// full: 'no-error-pages',
|
|
223
|
+
// flag: true,
|
|
224
|
+
// help: 'Disable custom error pages (use Node.js default pages instead)'
|
|
225
|
+
// },
|
|
226
|
+
{
|
|
227
|
+
name: 'error-pages',
|
|
228
|
+
help: 'Folder from which to look for custom error pages files (files must be named <error-code>.html -- eg. 500.html)',
|
|
229
|
+
validate: validPath,
|
|
230
|
+
prompt: false
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
name: 'force-user',
|
|
234
|
+
help: 'Force a WebID to always be logged in (useful when offline)'
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
name: 'strict-origin',
|
|
238
|
+
help: 'Enforce same origin policy in the ACL',
|
|
239
|
+
flag: true,
|
|
240
|
+
default: false,
|
|
241
|
+
prompt: false
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
name: 'use-email',
|
|
245
|
+
help: 'Do you want to set up an email service?',
|
|
246
|
+
flag: true,
|
|
247
|
+
prompt: true,
|
|
248
|
+
default: false
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
name: 'email-host',
|
|
252
|
+
help: 'Host of your email service',
|
|
253
|
+
prompt: true,
|
|
254
|
+
default: 'smtp.gmail.com',
|
|
255
|
+
when: (answers) => {
|
|
256
|
+
return answers['use-email']
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
name: 'email-port',
|
|
261
|
+
help: 'Port of your email service',
|
|
262
|
+
prompt: true,
|
|
263
|
+
default: '465',
|
|
264
|
+
when: (answers) => {
|
|
265
|
+
return answers['use-email']
|
|
266
|
+
}
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
name: 'email-auth-user',
|
|
270
|
+
help: 'User of your email service',
|
|
271
|
+
prompt: true,
|
|
272
|
+
when: (answers) => {
|
|
273
|
+
return answers['use-email']
|
|
274
|
+
},
|
|
275
|
+
validate: (value) => {
|
|
276
|
+
if (!value) {
|
|
277
|
+
return 'You must enter this information'
|
|
278
|
+
}
|
|
279
|
+
return true
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
name: 'email-auth-pass',
|
|
284
|
+
help: 'Password of your email service',
|
|
285
|
+
type: 'password',
|
|
286
|
+
prompt: true,
|
|
287
|
+
when: (answers) => {
|
|
288
|
+
return answers['use-email']
|
|
289
|
+
}
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
name: 'use-api-apps',
|
|
293
|
+
help: 'Do you want to load your default apps on /api/apps?',
|
|
294
|
+
flag: true,
|
|
295
|
+
prompt: false,
|
|
296
|
+
default: true
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
name: 'api-apps',
|
|
300
|
+
help: 'Path to the folder to mount on /api/apps',
|
|
301
|
+
prompt: true,
|
|
302
|
+
validate: validPath,
|
|
303
|
+
when: (answers) => {
|
|
304
|
+
return answers['use-api-apps']
|
|
305
|
+
}
|
|
306
|
+
},
|
|
307
|
+
{ // copied from name: 'owner'
|
|
308
|
+
name: 'redirect-http-from',
|
|
309
|
+
help: 'HTTP port or \',\'-separated ports to redirect to the solid server port (e.g. "80,8080").',
|
|
310
|
+
prompt: false,
|
|
311
|
+
validate: function (value) {
|
|
312
|
+
if (!value.match(/^[0-9]+(,[0-9]+)*$/)) {
|
|
313
|
+
return 'direct-port(s) must be a comma-separated list of integers.'
|
|
314
|
+
}
|
|
315
|
+
const list = value.split(/,/).map(v => parseInt(v))
|
|
316
|
+
const bad = list.find(v => { return v < 1 || v > 65535 })
|
|
317
|
+
if (bad.length) {
|
|
318
|
+
return 'redirect-http-from port(s) ' + bad + ' out of range'
|
|
319
|
+
}
|
|
320
|
+
return true
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
// This property is packaged into an object for the server property in config.json
|
|
325
|
+
name: 'server-info-name', // All properties with prefix server-info- will be removed from the config
|
|
326
|
+
help: 'A name for your server (not required, but will be presented on your server\'s frontpage)',
|
|
327
|
+
prompt: true,
|
|
328
|
+
default: answers => new URL(answers['server-uri']).hostname
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
// This property is packaged into an object for the server property in config.json
|
|
332
|
+
name: 'server-info-description', // All properties with prefix server-info- will be removed from the config
|
|
333
|
+
help: 'A description of your server (not required)',
|
|
334
|
+
prompt: true
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
// This property is packaged into an object for the server property in config.json
|
|
338
|
+
name: 'server-info-logo', // All properties with prefix server-info- will be removed from the config
|
|
339
|
+
help: 'A logo that represents you, your brand, or your server (not required)',
|
|
340
|
+
prompt: true
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
name: 'enforce-toc',
|
|
344
|
+
help: 'Do you want to enforce Terms & Conditions for your service?',
|
|
345
|
+
flag: true,
|
|
346
|
+
prompt: true,
|
|
347
|
+
default: false,
|
|
348
|
+
when: answers => answers.multiuser
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
name: 'toc-uri',
|
|
352
|
+
help: 'URI to your Terms & Conditions',
|
|
353
|
+
prompt: true,
|
|
354
|
+
validate: validUri,
|
|
355
|
+
when: answers => answers['enforce-toc']
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
name: 'disable-password-checks',
|
|
359
|
+
help: 'Do you want to disable password strength checking?',
|
|
360
|
+
flag: true,
|
|
361
|
+
prompt: true,
|
|
362
|
+
default: false,
|
|
363
|
+
when: answers => answers.multiuser
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
name: 'support-email',
|
|
367
|
+
help: 'The support email you provide for your users (not required)',
|
|
368
|
+
prompt: true,
|
|
369
|
+
validate: (value) => {
|
|
370
|
+
if (value && !isEmail(value)) {
|
|
371
|
+
return 'Must be a valid email'
|
|
372
|
+
}
|
|
373
|
+
return true
|
|
374
|
+
},
|
|
375
|
+
when: answers => answers.multiuser
|
|
376
|
+
}
|
|
377
|
+
]
|
|
378
|
+
|
|
379
|
+
function validPath (value) {
|
|
380
|
+
if (value === 'default') {
|
|
381
|
+
return Promise.resolve(true)
|
|
382
|
+
}
|
|
383
|
+
if (!value) {
|
|
384
|
+
return Promise.resolve('You must enter a valid path')
|
|
385
|
+
}
|
|
386
|
+
return new Promise((resolve) => {
|
|
387
|
+
fs.stat(value, function (err) {
|
|
388
|
+
if (err) return resolve('Nothing found at this path')
|
|
389
|
+
return resolve(true)
|
|
390
|
+
})
|
|
391
|
+
})
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function validUri (value) {
|
|
395
|
+
if (!validUrl.isUri(value)) {
|
|
396
|
+
return 'Enter a valid uri (with protocol)'
|
|
397
|
+
}
|
|
398
|
+
return true
|
|
399
|
+
}
|