Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
a8b24ef
trustedOrigins is empty by default
megoth Feb 7, 2019
8f5df1b
fix: package.json & package-lock.json to reduce vulnerabilities
snyk-bot Feb 15, 2019
92ca524
Restricting access to API endpoints to top domain
megoth Jan 28, 2019
f02987d
Fixed so that capability discovery points to valid APIs
megoth Feb 15, 2019
821776f
Laxing a bit on the endpoints you can visit on subdomains
megoth Feb 15, 2019
3d082f1
Merge pull request #1102 from solid/snyk-fix-xlobc6
kjetilk Feb 19, 2019
b1562ac
Removes acl files from list of files to match when mapping url to file
megoth Feb 19, 2019
218b36a
Removing hardcoded string of .acl-suffix
megoth Feb 20, 2019
acc2401
Display server version at startup when not running from git repository
gklyne Feb 22, 2019
01d2a35
Add proper-lockfile dependency.
RubenVerborgh Feb 23, 2019
90dcfe6
Lock file during patching.
RubenVerborgh Feb 23, 2019
d3e6506
Now excluding both .acl- and .meta-files when finding default file
megoth Feb 28, 2019
9876274
Add proper-lockfile dependency.
RubenVerborgh Feb 23, 2019
74e5464
Lock file during patching.
RubenVerborgh Feb 23, 2019
a4aeb9e
Making proper use of strictOrigins
megoth Feb 28, 2019
c8a23a9
Fixed some faulty tests
megoth Mar 2, 2019
01853af
Added RubenVerborgh explanation
megoth Mar 2, 2019
f5d2816
Revert "Lock file during patching."
megoth Mar 2, 2019
017ca05
Did a faulty rebase, trying to revert commits
megoth Mar 2, 2019
7c0d436
Revert "Add proper-lockfile dependency."
megoth Mar 2, 2019
06c5b19
Trying to make tests less dependent on order
megoth Mar 3, 2019
1998a5b
Removing the need to run tests in order
megoth Mar 3, 2019
7feecf9
Rename variables for consistency.
RubenVerborgh Mar 3, 2019
4e6eb10
Add lock on readResource.
RubenVerborgh Mar 3, 2019
3193a32
Add lock on put.
RubenVerborgh Mar 3, 2019
fd3b2b1
Add lock on get.
RubenVerborgh Mar 3, 2019
e9c8db3
Add lock on deleteResource.
RubenVerborgh Mar 3, 2019
dafc228
repairs redirect due to case typo correction
Mar 4, 2019
35839d5
Extract printing debug info from ldp to config module
dmitrizagidulin Mar 4, 2019
d7411eb
Expand SolidHost config constructor, plus tests
dmitrizagidulin Mar 4, 2019
0ece321
Some optimizations
megoth Mar 6, 2019
7df7f6f
Merge pull request #1126 from solid/extract-print-debug
kjetilk Mar 7, 2019
eccaec1
Merge pull request #1125 from ecobytes/fix/1124
kjetilk Mar 7, 2019
5cbfb79
Merge pull request #1112 from gklyne/release/v5.0.0
kjetilk Mar 7, 2019
31f113a
Trying to optimize and improve readability through better abstraction
megoth Mar 6, 2019
08684fb
Trying to fix regex
megoth Mar 7, 2019
b486abe
Trying to revert authentication-oidc-test manually
megoth Mar 7, 2019
de3b0e0
Fixing alignment
megoth Mar 7, 2019
4fd4f90
Revert "Trying to make tests less dependent on order"
megoth Mar 7, 2019
7b75a0b
Making changes minimal for acl-tls-test.js
megoth Mar 7, 2019
e4cf1e2
Reverting changes to solid-host
megoth Mar 7, 2019
567b042
Merge pull request #1109 from solid/fix/exclude-acl-in-url-mapping
kjetilk Mar 7, 2019
6c95b56
Merge pull request #1116 from solid/feature/lock-patch
kjetilk Mar 8, 2019
3dac0b2
Merge pull request #1091 from solid/feature/no-default-trusted-origins
kjetilk Mar 8, 2019
872014f
Auto-fix vulnerable deps
kjetilk Mar 8, 2019
bbff254
Release version 4.4.2 of the npm package.
kjetilk Mar 8, 2019
0acc026
Merge master into 5.0.0 branch
kjetilk Mar 8, 2019
70ddc02
Release version 5.0.0-beta.8 of the npm package.
kjetilk Mar 8, 2019
6e063ea
Always pass an object as options.
RubenVerborgh Mar 8, 2019
2f9996a
Only allow index.html as index file.
RubenVerborgh Mar 8, 2019
492860a
Merge pull request #1130 from solid/fix/streaming-start-end
kjetilk Mar 8, 2019
e62fa81
Merge pull request #1132 from solid/fix/only-index-html
kjetilk Mar 8, 2019
eb6b62b
Merge pull request #1118 from solid/fix/faulty-403
kjetilk Mar 8, 2019
332e7ae
Update package-lock.json.
kjetilk Mar 8, 2019
61d6455
Revert "Making proper use of strictOrigins"
kjetilk Mar 8, 2019
a4f0cbb
Merge pull request #1133 from solid/revert-1118-fix/faulty-403
kjetilk Mar 8, 2019
46b860f
Release version 5.0.0-beta.9 of the npm package.
kjetilk Mar 10, 2019
5980d75
Redo #1118
michielbdejong Mar 12, 2019
0339003
Merge pull request #1140 from michielbdejong/redo-1118
kjetilk Mar 15, 2019
a8b917c
Redo #1122 on top of latest release branch
megoth Mar 2, 2019
c328a3e
My changes on top of #1122
michielbdejong Mar 13, 2019
4927b97
Update CHANGELOG for v5 release
michielbdejong Mar 15, 2019
6982ec1
Use mashlib 0.7.18 and acl-check 0.2.0
michielbdejong Mar 15, 2019
ef9e8c9
fix commented-out requires in test
michielbdejong Mar 15, 2019
526dea6
Update package-lock.json.
kjetilk Mar 15, 2019
f02094e
Release version 5.0.0-beta.9 of the npm package.
kjetilk Mar 15, 2019
dca8d65
Release version 5.0.0-beta.10 of the npm package.
kjetilk Mar 15, 2019
c8efa90
package-lock.json
michielbdejong Mar 15, 2019
a5d3ad5
refer to https://github.com/solid/acl-check/issues/23
michielbdejong Mar 15, 2019
3d9b2bc
Merge pull request #1144 from solid/redo-1122
kjetilk Mar 15, 2019
21ebe47
Merge pull request #1127 from solid/expand-server-host
kjetilk Mar 15, 2019
bafa5f6
Release version 5.0.0-beta.11 of the npm package.
kjetilk Mar 15, 2019
63d2b81
Restricting access to API endpoints to top domain
megoth Jan 28, 2019
1bc05b1
Fixed so that capability discovery points to valid APIs
megoth Feb 15, 2019
b16b137
Laxing a bit on the endpoints you can visit on subdomains
megoth Feb 15, 2019
927acf6
Merge branch 'feature/prevent-api-spoofing' of github.com:solid/node-…
kjetilk Mar 15, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,17 @@
- The browser-reported `Origin` header will now be checked by
default, and the ACL system can be used to restrict access
to applications for added security.
- Users can add `trustedApp` entries to their profile using a new databrowser pane.
You will see an 'A' icon added while you view a Person's profile URL
with the data browser (might have to hit refresh in your browser and make sure you
are viewing a WebId URL like https://localhost:8443/profile/card#me).
- Logging is now verbose by default so the `-v` option has been
removed and a `--quiet` option has been added to mute the log.
- To be bug compliant with 4.x releases, if a rule for public readable
root / does not exist, it will check in /index.html.acl (see issue #1063)
- Command line options are now kebab-cased rather than camelCased,
config options may be both.
- Resource with no extension now have '$.ttl' appended in the filename (see upgrades notes below).
- Many smaller fixes.

#### 5.0.0 Upgrade Notes
Expand Down
7 changes: 6 additions & 1 deletion bin/lib/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,15 @@ function getVersion () {
// Obtain version from git
const options = { cwd: __dirname, encoding: 'utf8' }
const { stdout } = spawnSync('git', ['describe', '--tags'], options)
return stdout.trim()
const version = stdout.trim()
if (version === '') {
throw new Error('No git version here')
}
return version
} catch (e) {
// Obtain version from package.json
const { version } = require(path.join(__dirname, '../../package.json'))
return version
}
}

2 changes: 1 addition & 1 deletion config/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ module.exports = {
'serverUri': 'https://localhost:8443',
'webid': true,
'strictOrigin': true,
'trustedOrigins': ['https://apps.solid.invalid'],
'trustedOrigins': [],
'dataBrowserPath': 'default'

// For use in Enterprises to configure a HTTP proxy for all outbound HTTP requests from the SOLID server (we use
Expand Down
29 changes: 17 additions & 12 deletions lib/acl-checker.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,10 @@ class ACLChecker {
constructor (resource, options = {}) {
this.resource = resource
this.resourceUrl = new URL(resource)
this.agentOrigin = options.agentOrigin
this.agentOrigin = options.strictOrigin && options.agentOrigin ? rdf.sym(options.agentOrigin) : null
this.fetch = options.fetch
this.fetchGraph = options.fetchGraph
this.strictOrigin = options.strictOrigin
this.trustedOrigins = options.trustedOrigins
this.trustedOrigins = options.strictOrigin && options.trustedOrigins ? options.trustedOrigins.map(trustedOrigin => rdf.sym(trustedOrigin)) : null
this.suffix = options.suffix || DEFAULT_ACL_SUFFIX
this.aclCached = {}
this.messagesCached = {}
Expand Down Expand Up @@ -56,17 +55,23 @@ class ACLChecker {
const aclFile = rdf.sym(acl.acl)
const agent = user ? rdf.sym(user) : null
const modes = [ACL(mode)]
const agentOrigin = this.agentOrigin ? rdf.sym(this.agentOrigin) : null
const trustedOrigins = this.trustedOrigins ? this.trustedOrigins.map(trustedOrigin => rdf.sym(trustedOrigin)) : null
const accessDenied = aclCheck.accessDenied(acl.graph, resource, directory, aclFile, agent, modes, agentOrigin, trustedOrigins)
if (accessDenied && this.agentOrigin && this.resourceUrl.origin !== this.agentOrigin) {
this.messagesCached[cacheKey].push(HTTPError(403, accessDenied))
} else if (accessDenied && user) {
const agentOrigin = this.agentOrigin
const trustedOrigins = this.trustedOrigins
let originTrustedModes = []
try {
this.fetch(aclFile.doc().value)
originTrustedModes = await aclCheck.getTrustedModesForOrigin(acl.graph, resource, directory, aclFile, agentOrigin, (uriNode) => {
return this.fetch(uriNode.doc().value, acl.graph)
})
} catch (e) {
// FIXME: https://github.com/solid/acl-check/issues/23
// console.error(e.message)
}
const accessDenied = aclCheck.accessDenied(acl.graph, resource, directory, aclFile, agent, modes, agentOrigin, trustedOrigins, originTrustedModes)
if (accessDenied && user) {
this.messagesCached[cacheKey].push(HTTPError(403, accessDenied))
} else if (accessDenied && !user) {
this.messagesCached[cacheKey].push(HTTPError(401, 'Unauthenticated'))
} else if (accessDenied) {
this.messagesCached[cacheKey].push(HTTPError(401, accessDenied))
this.messagesCached[cacheKey].push(HTTPError(401, 'Unauthenticated'))
}
this.aclCached[cacheKey] = Promise.resolve(!accessDenied)
return this.aclCached[cacheKey]
Expand Down
16 changes: 9 additions & 7 deletions lib/api/accounts/user-accounts.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ const express = require('express')
const bodyParser = require('body-parser').urlencoded({ extended: false })
const debug = require('../../debug').accounts

const restrictToTopDomain = require('../../handlers/restrict-to-top-domain')

const CreateAccountRequest = require('../../requests/create-account-request')
const AddCertificateRequest = require('../../requests/add-cert-request')
const DeleteAccountRequest = require('../../requests/delete-account-request')
Expand Down Expand Up @@ -65,16 +67,16 @@ function middleware (accountManager) {

router.get('/', checkAccountExists(accountManager))

router.post('/api/accounts/new', bodyParser, CreateAccountRequest.post)
router.get(['/register', '/api/accounts/new'], CreateAccountRequest.get)
router.post('/api/accounts/new', restrictToTopDomain, bodyParser, CreateAccountRequest.post)
router.get(['/register', '/api/accounts/new'], restrictToTopDomain, CreateAccountRequest.get)

router.post('/api/accounts/cert', bodyParser, newCertificate(accountManager))
router.post('/api/accounts/cert', restrictToTopDomain, bodyParser, newCertificate(accountManager))

router.get('/account/delete', DeleteAccountRequest.get)
router.post('/account/delete', bodyParser, DeleteAccountRequest.post)
router.get('/account/delete', restrictToTopDomain, DeleteAccountRequest.get)
router.post('/account/delete', restrictToTopDomain, bodyParser, DeleteAccountRequest.post)

router.get('/account/delete/confirm', DeleteAccountConfirmRequest.get)
router.post('/account/delete/confirm', bodyParser, DeleteAccountConfirmRequest.post)
router.get('/account/delete/confirm', restrictToTopDomain, DeleteAccountConfirmRequest.get)
router.post('/account/delete/confirm', restrictToTopDomain, bodyParser, DeleteAccountConfirmRequest.post)

return router
}
Expand Down
10 changes: 6 additions & 4 deletions lib/api/authn/webid-oidc.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ const bodyParser = require('body-parser').urlencoded({ extended: false })
const OidcManager = require('../../models/oidc-manager')
const { LoginRequest } = require('../../requests/login-request')

const restrictToTopDomain = require('../../handlers/restrict-to-top-domain')

const PasswordResetEmailRequest = require('../../requests/password-reset-email-request')
const PasswordChangeRequest = require('../../requests/password-change-request')

Expand Down Expand Up @@ -71,11 +73,11 @@ function middleware (oidc) {

router.post('/login/tls', bodyParser, LoginRequest.loginTls)

router.get('/account/password/reset', PasswordResetEmailRequest.get)
router.post('/account/password/reset', bodyParser, PasswordResetEmailRequest.post)
router.get('/account/password/reset', restrictToTopDomain, PasswordResetEmailRequest.get)
router.post('/account/password/reset', restrictToTopDomain, bodyParser, PasswordResetEmailRequest.post)

router.get('/account/password/change', PasswordChangeRequest.get)
router.post('/account/password/change', bodyParser, PasswordChangeRequest.post)
router.get('/account/password/change', restrictToTopDomain, PasswordChangeRequest.get)
router.post('/account/password/change', restrictToTopDomain, bodyParser, PasswordChangeRequest.post)

router.get('/.well-known/solid/logout/', (req, res) => res.redirect('/logout'))

Expand Down
46 changes: 23 additions & 23 deletions lib/capability-discovery.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,7 @@
* @module capability-discovery
*/
const express = require('express')

const serviceConfigDefaults = {
'api': {
'accounts': {
// 'changePassword': '/api/account/changePassword',
// 'delete': '/api/accounts/delete',

// Create new user (see IdentityProvider.post() in identity-provider.js)
'new': '/api/accounts/new',
'recover': '/api/accounts/recover',
'signin': '/login',
'signout': '/logout',
'validateToken': '/api/accounts/validateToken'
}
}
}
const { URL } = require('url')

module.exports = capabilityDiscovery

Expand All @@ -31,7 +16,7 @@ function capabilityDiscovery () {
const router = express.Router('/')

// Advertise the server capability discover endpoint
router.get('/.well-known/solid', serviceCapabilityDocument(serviceConfigDefaults))
router.get('/.well-known/solid', serviceCapabilityDocument())
return router
}

Expand All @@ -43,12 +28,27 @@ function capabilityDiscovery () {
* @param res
* @param next
*/
function serviceCapabilityDocument (serviceConfig) {
function serviceCapabilityDocument () {
return (req, res) => {
// Add the server root url
serviceConfig.root = req.app.locals.ldp.resourceMapper.resolveUrl(req.hostname, req.path)
// Add the 'apps' urls section
serviceConfig.apps = req.app.locals.appUrls
res.json(serviceConfig)
const ldp = req.app.locals.ldp
res.json({
// Add the server root url
root: req.app.locals.ldp.resourceMapper.resolveUrl(req.hostname, req.path),
// Add the 'apps' urls section
apps: req.app.locals.appUrls,
api: {
accounts: {
// 'changePassword': '/api/account/changePassword',
// 'delete': '/api/accounts/delete',

// Create new user (see IdentityProvider.post() in identity-provider.js)
new: new URL('/api/accounts/new', ldp.serverUri),
recover: new URL('/api/accounts/recover', ldp.serverUri),
signin: req.app.locals.ldp.resourceMapper.resolveUrl(req.hostname, '/login'),
signout: req.app.locals.ldp.resourceMapper.resolveUrl(req.hostname, '/logout'),
validateToken: new URL('/api/accounts/validateToken', ldp.serverUri)
}
}
})
}
}
27 changes: 23 additions & 4 deletions lib/create-app.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,24 @@ function createApp (argv = {}) {
// Override default configs (defaults) with passed-in params (argv)
argv = Object.assign({}, defaults, argv)

argv.host = SolidHost.from({ port: argv.port, serverUri: argv.serverUri })
argv.host = SolidHost.from(argv)

argv.resourceMapper = new ResourceMapper({
rootUrl: argv.serverUri,
rootPath: path.resolve(argv.root || process.cwd()),
includeHost: argv.multiuser,
defaultContentType: argv.defaultContentType
defaultContentType: argv.defaultContentType,
fileSuffixes: [
argv.suffixAcl || '.acl',
argv.suffixMeta || '.meta'
]
})

const configPath = config.initConfigDir(argv)
argv.templates = config.initTemplateDirs(configPath)

config.printDebugInfo(argv)

const ldp = new LDP(argv)

const app = express()
Expand Down Expand Up @@ -206,14 +212,27 @@ function initWebId (argv, app, ldp) {
// without permission by including the credentials set by the Solid server.
app.use((req, res, next) => {
const origin = req.get('origin')
const trustedOrigins = argv.trustedOrigins
const trustedOrigins = ldp.getTrustedOrigins(req)
const userId = req.session.userId
// Exception: allow logout requests from all third-party apps
// such that OIDC client can log out via cookie auth
// TODO: remove this exception when OIDC clients
// use Bearer token to authenticate instead of cookie
// (https://github.com/solid/node-solid-server/pull/835#issuecomment-426429003)
if (!argv.host.allowsSessionFor(userId, origin, trustedOrigins) && !isLogoutRequest(req)) {
//
// Authentication cookies are an optimization:
// instead of going through the process of
// fully validating authentication on every request,
// we go through this process once,
// and store its successful result in a cookie
// that will be reused upon the next request.
// However, that cookie can then be sent by any server,
// even servers that have not gone through the proper authentication mechanism.
// However, if trusted origins are enabled,
// then any origin is allowed to take the shortcut route,
// since malicious origins will be banned at the ACL checking phase.
// https://github.com/solid/node-solid-server/issues/1117
if (!argv.strictOrigin && !argv.host.allowsSessionFor(userId, origin, trustedOrigins) && !isLogoutRequest(req)) {
debug.authentication(`Rejecting session for ${userId} from ${origin}`)
// Destroy session data
delete req.session.userId
Expand Down
14 changes: 7 additions & 7 deletions lib/create-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,20 +80,20 @@ function createServer (argv, app) {
}

// Look for port or list of ports to redirect to argv.port
if ('redirectHTTPFrom' in argv) {
const redirectHTTPFroms = argv.redirectHTTPFrom.constructor === Array
? argv.redirectHTTPFrom
: [argv.redirectHTTPFrom]
if ('redirectHttpFrom' in argv) {
const redirectHttpFroms = argv.redirectHttpFrom.constructor === Array
? argv.redirectHttpFrom
: [argv.redirectHttpFrom]
const portStr = argv.port === 443 ? '' : ':' + argv.port
redirectHTTPFroms.forEach(redirectHTTPFrom => {
debug.settings('will redirect from port ' + redirectHTTPFrom + ' to port ' + argv.port)
redirectHttpFroms.forEach(redirectHttpFrom => {
debug.settings('will redirect from port ' + redirectHttpFrom + ' to port ' + argv.port)
let redirectingServer = express()
redirectingServer.get('*', function (req, res) {
let host = req.headers.host.split(':') // ignore port
debug.server(host, '=> https://' + host + portStr + req.url)
res.redirect('https://' + host + portStr + req.url)
})
redirectingServer.listen(redirectHTTPFrom)
redirectingServer.listen(redirectHttpFrom)
})
}

Expand Down
18 changes: 11 additions & 7 deletions lib/handlers/patch.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const $rdf = require('rdflib')
const crypto = require('crypto')
const overQuota = require('../utils').overQuota
const getContentType = require('../utils').getContentType
const { lock } = require('proper-lockfile')

// Patch parsers by request body content type
const PATCH_PARSERS = {
Expand All @@ -23,6 +24,7 @@ const DEFAULT_FOR_NEW_CONTENT_TYPE = 'text/turtle'
async function patchHandler (req, res, next) {
debug(`PATCH -- ${req.originalUrl}`)
res.header('MS-Author-Via', 'SPARQL')
let releaseLock
try {
// Obtain details of the target resource
const ldp = req.app.locals.ldp
Expand Down Expand Up @@ -50,22 +52,24 @@ async function patchHandler (req, res, next) {
throw error(415, `Unsupported patch content type: ${patch.contentType}`)
}

// Parse the target graph and the patch document,
// and verify permission for performing this specific patch
const [graph, patchObject] = await Promise.all([
readGraph(resource),
parsePatch(url, patch.uri, patch.text)
.then(patchObject => checkPermission(req, patchObject))
])
// Parse the patch document and verify permissions
const patchObject = await parsePatch(url, patch.uri, patch.text)
await checkPermission(req, patchObject)

// Patch the graph and write it back to the file
releaseLock = await lock(path, { retries: 10, realpath: false })
const graph = await readGraph(resource)
await applyPatch(patchObject, graph, url)
const result = await writeGraph(graph, resource, ldp.resourceMapper.rootPath, ldp.serverUri)

// Send the result to the client
res.send(result)
} catch (err) {
return next(err)
} finally {
if (releaseLock) {
await releaseLock()
}
}
return next()
}
Expand Down
13 changes: 13 additions & 0 deletions lib/handlers/restrict-to-top-domain.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const HTTPError = require('../http-error')

module.exports = function (req, res, next) {
const locals = req.app.locals
const ldp = locals.ldp
const serverUri = locals.host.serverUri
const hostname = ldp.resourceMapper.resolveUrl(req.hostname)
if (hostname === serverUri) {
return next()
}
const isLoggedIn = !!(req.session && req.session.userId)
return next(new HTTPError(isLoggedIn ? 403 : 401, 'Not allowed to access top-level APIs on accounts'))
}
Loading