From a8b917cf52f001faf102bf75a40b3354ae6be4be Mon Sep 17 00:00:00 2001 From: Arne Hassel Date: Sat, 2 Mar 2019 21:44:02 +0100 Subject: [PATCH 1/3] Redo #1122 on top of latest release branch --- lib/acl-checker.js | 23 +++++++++- test/integration/authentication-oidc-test.js | 44 +++++++++++++++++++ ...idc-with-strict-origins-turned-off-test.js | 43 ++++++++++++++++++ .../accounts-scenario/alice/profile/card$.ttl | 4 ++ .../alice/profile/card$.ttl | 4 ++ 5 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 test/resources/accounts-scenario/alice/profile/card$.ttl create mode 100644 test/resources/accounts-strict-origin-off/alice/profile/card$.ttl diff --git a/lib/acl-checker.js b/lib/acl-checker.js index 44c0ae883..d91404036 100644 --- a/lib/acl-checker.js +++ b/lib/acl-checker.js @@ -57,7 +57,8 @@ class ACLChecker { const modes = [ACL(mode)] const agentOrigin = this.agentOrigin const trustedOrigins = this.trustedOrigins - const accessDenied = aclCheck.accessDenied(acl.graph, resource, directory, aclFile, agent, modes, agentOrigin, trustedOrigins) + const originTrustedModes = agent && agentOrigin ? await this.getOriginTrustedModes(agent, agentOrigin) : [] + const accessDenied = aclCheck.accessDenied(acl.graph, resource, directory, aclFile, agent, modes, agentOrigin, trustedOrigins, originTrustedModes) if (accessDenied && user) { this.messagesCached[cacheKey].push(HTTPError(403, accessDenied)) @@ -68,6 +69,26 @@ class ACLChecker { return this.aclCached[cacheKey] } + async getOriginTrustedModes (agent, agentOrigin) { + let agentStore + try { + this.requests[agent.uri] = this.requests[agent.uri] || this.fetch(agent.uri) + agentStore = await this.requests[agent.uri] + } catch (err) { + if (err && (err.code === 'ENOENT' || err.status === 404)) { + // If we're unable to resolve the agent, we conclude that origin has no trusted modes + this.requests[agent.uri] = Promise.resolve(null) + return Promise.resolve([]) + } + debug(err) + throw err + } + if (agentStore) { + return aclCheck.getTrustedModesForOrigin(agentStore, agent, agentOrigin) + } + return Promise.resolve([]) + } + async getError (user, mode) { const cacheKey = `${mode}-${user}` this.aclCached[cacheKey] = this.aclCached[cacheKey] || this.can(user, mode) diff --git a/test/integration/authentication-oidc-test.js b/test/integration/authentication-oidc-test.js index 53827f5f7..56a567dc9 100644 --- a/test/integration/authentication-oidc-test.js +++ b/test/integration/authentication-oidc-test.js @@ -35,6 +35,8 @@ describe('Authentication API (OIDC)', () => { let bobDbPath = path.join(__dirname, '../resources/accounts-scenario/bob/db') + const trustedAppUri = 'https://trusted.app' + const serverConfig = { sslKey: path.join(__dirname, '../keys/key.pem'), sslCert: path.join(__dirname, '../keys/cert.pem'), @@ -437,6 +439,48 @@ describe('Authentication API (OIDC)', () => { expect(response).to.have.property('status', 401) }) }) + + describe('with trusted app and no cookie', () => { + before(done => { + alice.get('/private-for-alice.txt') + .set('Origin', trustedAppUri) + .end((err, res) => { + response = res + done(err) + }) + }) + + it('should return a 401', () => expect(response).to.have.property('status', 401)) + }) + + describe('with trusted app and malicious cookie', () => { + before(done => { + var malcookie = cookie.replace(/connect\.sid=(\S+)/, 'connect.sid=l33th4x0rzp0wn4g3;') + alice.get('/private-for-alice.txt') + .set('Cookie', malcookie) + .set('Origin', trustedAppUri) + .end((err, res) => { + response = res + done(err) + }) + }) + + it('should return a 401', () => expect(response).to.have.property('status', 401)) + }) + + describe('with trusted app and correct cookie', () => { + before(done => { + alice.get('/private-for-alice.txt') + .set('Cookie', cookie) + .set('Origin', trustedAppUri) + .end((err, res) => { + response = res + done(err) + }) + }) + + it('should return a 200', () => expect(response).to.have.property('status', 200)) + }) }) }) diff --git a/test/integration/authentication-oidc-with-strict-origins-turned-off-test.js b/test/integration/authentication-oidc-with-strict-origins-turned-off-test.js index f61ca8ab6..7cf97db3a 100644 --- a/test/integration/authentication-oidc-with-strict-origins-turned-off-test.js +++ b/test/integration/authentication-oidc-with-strict-origins-turned-off-test.js @@ -35,6 +35,8 @@ describe('Authentication API (OIDC) - With strict origins turned off', () => { const bobServerUri = `https://localhost:${bobServerPort}` let bobDbPath = path.join(__dirname, '../resources/accounts-strict-origin-off/bob/db') + const trustedAppUri = 'https://trusted.app' + const serverConfig = { sslKey: path.join(__dirname, '../keys/key.pem'), sslCert: path.join(__dirname, '../keys/cert.pem'), @@ -194,6 +196,18 @@ describe('Authentication API (OIDC) - With strict origins turned off', () => { }) }) + it('should return a 401', () => expect(response).to.have.property('status', 401)) + }) + describe('and trusted app', () => { + before(done => { + alice.get('/private-for-alice.txt') + .set('Origin', trustedAppUri) + .end((err, res) => { + response = res + done(err) + }) + }) + it('should return a 401', () => expect(response).to.have.property('status', 401)) }) }) @@ -251,6 +265,21 @@ describe('Authentication API (OIDC) - With strict origins turned off', () => { // Even if origin checking is disabled, then this should return a 401 because cookies should not be trusted cross-origin it('should return a 401', () => expect(response).to.have.property('status', 401)) }) + + describe('and trusted app', () => { + // Trusted apps are not supported when strictOrigin check is turned off + before(done => { + alice.get('/private-for-alice.txt') + .set('Cookie', cookie) + .set('Origin', trustedAppUri) + .end((err, res) => { + response = res + done(err) + }) + }) + + it('should return a 401', () => expect(response).to.have.property('status', 401)) + }) }) describe('with malicious cookie', () => { @@ -310,6 +339,20 @@ describe('Authentication API (OIDC) - With strict origins turned off', () => { it('should return a 401', () => expect(response).to.have.property('status', 401)) }) + + describe('and trusted app', () => { + before(done => { + alice.get('/private-for-alice.txt') + .set('Cookie', malcookie) + .set('Origin', trustedAppUri) + .end((err, res) => { + response = res + done(err) + }) + }) + + it('should return a 401', () => expect(response).to.have.property('status', 401)) + }) }) }) }) diff --git a/test/resources/accounts-scenario/alice/profile/card$.ttl b/test/resources/accounts-scenario/alice/profile/card$.ttl new file mode 100644 index 000000000..a014d960e --- /dev/null +++ b/test/resources/accounts-scenario/alice/profile/card$.ttl @@ -0,0 +1,4 @@ +@prefix acl: . + +<#me> acl:trustedApp [ acl:origin ; + acl:mode acl:Read, acl:Write, acl:Append, acl:Control]. diff --git a/test/resources/accounts-strict-origin-off/alice/profile/card$.ttl b/test/resources/accounts-strict-origin-off/alice/profile/card$.ttl new file mode 100644 index 000000000..a014d960e --- /dev/null +++ b/test/resources/accounts-strict-origin-off/alice/profile/card$.ttl @@ -0,0 +1,4 @@ +@prefix acl: . + +<#me> acl:trustedApp [ acl:origin ; + acl:mode acl:Read, acl:Write, acl:Append, acl:Control]. From e4065e34467147636c5323278f6005bcc44d0de5 Mon Sep 17 00:00:00 2001 From: Michiel de Jong Date: Wed, 13 Mar 2019 17:15:21 +0100 Subject: [PATCH 2/3] My changes on top of #1122 adopt acl-check #19 fetch the aclDoc before trying to read from it fix profile fetcher lint fix acl file fetch arg new behavior s mid --- lib/acl-checker.js | 31 ++++++-------------- test/integration/authentication-oidc-test.js | 8 ++--- 2 files changed, 13 insertions(+), 26 deletions(-) diff --git a/lib/acl-checker.js b/lib/acl-checker.js index d91404036..632f2ff28 100644 --- a/lib/acl-checker.js +++ b/lib/acl-checker.js @@ -57,9 +57,16 @@ class ACLChecker { const modes = [ACL(mode)] const agentOrigin = this.agentOrigin const trustedOrigins = this.trustedOrigins - const originTrustedModes = agent && agentOrigin ? await this.getOriginTrustedModes(agent, agentOrigin) : [] + 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) { + 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) { @@ -69,26 +76,6 @@ class ACLChecker { return this.aclCached[cacheKey] } - async getOriginTrustedModes (agent, agentOrigin) { - let agentStore - try { - this.requests[agent.uri] = this.requests[agent.uri] || this.fetch(agent.uri) - agentStore = await this.requests[agent.uri] - } catch (err) { - if (err && (err.code === 'ENOENT' || err.status === 404)) { - // If we're unable to resolve the agent, we conclude that origin has no trusted modes - this.requests[agent.uri] = Promise.resolve(null) - return Promise.resolve([]) - } - debug(err) - throw err - } - if (agentStore) { - return aclCheck.getTrustedModesForOrigin(agentStore, agent, agentOrigin) - } - return Promise.resolve([]) - } - async getError (user, mode) { const cacheKey = `${mode}-${user}` this.aclCached[cacheKey] = this.aclCached[cacheKey] || this.can(user, mode) diff --git a/test/integration/authentication-oidc-test.js b/test/integration/authentication-oidc-test.js index 56a567dc9..c6ebc9c0c 100644 --- a/test/integration/authentication-oidc-test.js +++ b/test/integration/authentication-oidc-test.js @@ -3,10 +3,10 @@ const path = require('path') const fs = require('fs-extra') const { UserStore } = require('@solid/oidc-auth-manager') const UserAccount = require('../../lib/models/user-account') -const SolidAuthOIDC = require('@solid/solid-auth-oidc') +// const SolidAuthOIDC = require('@solid/solid-auth-oidc') -const fetch = require('node-fetch') -const localStorage = require('localstorage-memory') +// const fetch = require('node-fetch') +// const localStorage = require('localstorage-memory') const URL = require('whatwg-url').URL global.URL = URL global.URLSearchParams = require('whatwg-url').URLSearchParams @@ -20,7 +20,7 @@ chai.use(require('dirty-chai')) // In this test we always assume that we are Alice describe('Authentication API (OIDC)', () => { - let alice, bob + let alice, bob // eslint-disable-line no-unused-vars let aliceServerUri = 'https://localhost:7000' let aliceWebId = 'https://localhost:7000/profile/card#me' From 82de1f814f04ef57f29603ffcbbe1af2aa25304b Mon Sep 17 00:00:00 2001 From: Michiel de Jong Date: Fri, 15 Mar 2019 13:21:04 +0100 Subject: [PATCH 3/3] Update CHANGELOG for v5 release --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 541ca77fb..d250c0fc2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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