From b5bed1837ed15be27d2b73cd099111015330cf0e Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Fri, 6 Oct 2017 15:58:03 +0100 Subject: [PATCH] Start acceptance tests for token-based access --- .../acceptance/coffee/TokenAccessTests.coffee | 172 ++++++++++++++++++ .../acceptance/coffee/helpers/User.coffee | 21 +++ 2 files changed, 193 insertions(+) create mode 100644 services/web/test/acceptance/coffee/TokenAccessTests.coffee diff --git a/services/web/test/acceptance/coffee/TokenAccessTests.coffee b/services/web/test/acceptance/coffee/TokenAccessTests.coffee new file mode 100644 index 0000000000..74b8c2b7b8 --- /dev/null +++ b/services/web/test/acceptance/coffee/TokenAccessTests.coffee @@ -0,0 +1,172 @@ +expect = require("chai").expect +async = require("async") +User = require "./helpers/User" +request = require "./helpers/request" +settings = require "settings-sharelatex" + +try_read_access = (user, project_id, test, callback) -> + async.series [ + (cb) -> + user.request.get "/project/#{project_id}", (error, response, body) -> + return cb(error) if error? + test(response, body) + cb() + (cb) -> + user.request.get "/project/#{project_id}/download/zip", (error, response, body) -> + return cb(error) if error? + test(response, body) + cb() + ], callback + +try_read_only_token_access = (user, token, test, callback) -> + async.series [ + (cb) -> + user.request.get "/read/#{token}", (error, response, body) -> + return cb(error) if error? + test(response, body) + cb() + ], callback + +try_read_and_write_token_access = (user, token, test, callback) -> + async.series [ + (cb) -> + user.request.get "/read/#{token}", (error, response, body) -> + return cb(error) if error? + test(response, body) + cb() + ], callback + +try_content_access = (user, project_id, test, callback) -> + # The real-time service calls this end point to determine the user's + # permissions. + if user.id? + user_id = user.id + else + user_id = "anonymous-user" + request.post { + url: "/project/#{project_id}/join" + qs: {user_id} + auth: + user: settings.apis.web.user + pass: settings.apis.web.pass + sendImmediately: true + json: true + jar: false + }, (error, response, body) -> + return callback(error) if error? + test(response, body) + callback() + +expect_content_write_access = (user, project_id, callback) -> + try_content_access(user, project_id, (response, body) -> + expect(body.privilegeLevel).to.be.oneOf ["readAndWrite"] + , callback) + +expect_content_read_access = (user, project_id, callback) -> + try_content_access(user, project_id, (response, body) -> + expect(body.privilegeLevel).to.be.oneOf ["readOnly"] + , callback) + +expect_read_only_access = (user, project_id, token, callback) -> + async.series [ + (cb) -> + try_read_only_token_access(user, token, (response, body) -> + expect(response.statusCode).to.be.oneOf [200, 204] + , cb) + (cb) -> + try_content_access(user, project_id, (response, body) -> + expect(body.privilegeLevel).to.be.oneOf ["readOnly"] + , cb) + ], callback + +expect_read_and_write_access = (user, project_id, token, callback) -> + async.series [ + (cb) -> + try_read_and_write_token_access(user, token, (response, body) -> + expect(response.statusCode).to.be.oneOf [200, 204] + , cb) + (cb) -> + try_content_access(user, project_id, (response, body) -> + expect(body.privilegeLevel).to.be.oneOf ["readAndWrite"] + , cb) + ], callback + + + + + +describe 'TokenAccess', -> + before (done) -> + @timeout(90000) + @owner = new User() + @other1 = new User() + @other2 = new User() + @anon = new User() + async.parallel [ + (cb) => @owner.login cb + (cb) => @other1.login cb + (cb) => @other2.login cb + (cb) => @anon.getCsrfToken cb + ], done + + describe 'read-only token', -> + before (done) -> + @owner.createProject 'token-ro-test#{Math.random()}', (err, project_id) => + return done(err) if err? + @project_id = project_id + @owner.makeTokenBased @project_id, (err) => + return done(err) if err? + @owner.getProject @project_id, (err, project) => + return done(err) if err? + @tokens = project.tokens + console.log ">> ", @project_id, @tokens, project.publicAccesLevel + done() + + it 'should deny access before the token is used', (done) -> + try_read_access(@other1, @project_id, (response, body) => + expect(response.statusCode).to.equal 302 + expect(body).to.match /.*\/restricted.*/ + , done) + + it 'should allow the user to access project via read-only token url', (done) -> + try_read_only_token_access(@other1, @tokens.readOnly, (response, body) => + expect(response.statusCode).to.equal 200 + , done) + + it 'should allow the user to join the project with read-only access', (done) -> + try_content_access(@other1, @project_id, (response, body) => + expect(body.privilegeLevel).to.equal 'readOnly' + , done) + + describe 'made private again', -> + before (done) -> + @owner.makePrivate @project_id, () -> setTimeout(done, 1000) + + it 'should deny access to project', (done) -> + try_read_access(@other1, @project_id, (response, body) => + expect(response.statusCode).to.equal 302 + expect(body).to.match /.*\/restricted.*/ + , done) + + it 'should not allow the user to access read-only token', (done) -> + try_read_only_token_access(@other1, @tokens.readOnly, (response, body) => + expect(response.statusCode).to.equal 404 + , done) + + it 'should not allow the user to join the project', (done) -> + try_content_access(@other1, @project_id, (response, body) => + expect(body.privilegeLevel).to.equal false + , done) + + + # describe 'anonymous read-only token', -> + # beforeEach -> + + # describe 'made private again', -> + # beforeEach -> + + # describe 'read-and-write token', -> + # beforeEach -> + + # describe 'made private again', -> + # beforeEach -> diff --git a/services/web/test/acceptance/coffee/helpers/User.coffee b/services/web/test/acceptance/coffee/helpers/User.coffee index 21378aa844..3ee7ea52c2 100644 --- a/services/web/test/acceptance/coffee/helpers/User.coffee +++ b/services/web/test/acceptance/coffee/helpers/User.coffee @@ -93,6 +93,9 @@ class User callback(err) db.users.remove {_id: ObjectId(user_id)}, callback + getProject: (project_id, callback = (error, project)->) -> + db.projects.findOne {_id: ObjectId(project_id.toString())}, callback + createProject: (name, callback = (error, project_id) ->) -> @request.post { url: "/project/new", @@ -138,6 +141,24 @@ class User return callback(error) if error? callback(null) + makePrivate: (project_id, callback = (error) ->) -> + @request.post { + url: "/project/#{project_id}/settings/admin", + json: + publicAccessLevel: 'private' + }, (error, response, body) -> + return callback(error) if error? + callback(null) + + makeTokenBased: (project_id, callback = (error) ->) -> + @request.post { + url: "/project/#{project_id}/settings/admin", + json: + publicAccessLevel: 'tokenBased' + }, (error, response, body) -> + return callback(error) if error? + callback(null) + getCsrfToken: (callback = (error) ->) -> @request.get { url: "/register"