| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149 |
- import { expect } from 'chai'
- import Async from 'async'
- import User from './helpers/User.mjs'
- import Subscription from './helpers/Subscription.mjs'
- import settings from '@overleaf/settings'
- import CollaboratorsEmailHandler from '../../../app/src/Features/Collaborators/CollaboratorsEmailHandler.mjs'
- import CollaboratorsInviteHelper from '../../../app/src/Features/Collaborators/CollaboratorsInviteHelper.mjs'
- import Features from '../../../app/src/infrastructure/Features.mjs'
- import sinon from 'sinon'
- let generateTokenSpy
- const createInvite = (sendingUser, projectId, email, callback) => {
- sendingUser.getCsrfToken(err => {
- if (err) {
- return callback(err)
- }
- sendingUser.request.post(
- {
- uri: `/project/${projectId}/invite`,
- json: {
- email,
- privileges: 'readAndWrite',
- },
- },
- (err, response, body) => {
- if (err) {
- return callback(err)
- }
- expect(response.statusCode).to.equal(200)
- expect(body.error).to.not.exist
- expect(body.invite).to.exist
- callback(null, body.invite)
- }
- )
- })
- }
- const createProject = (owner, projectName, callback) => {
- owner.createProject(projectName, (err, projectId) => {
- if (err) {
- throw err
- }
- const fakeProject = {
- _id: projectId,
- name: projectName,
- owner_ref: owner,
- }
- callback(err, projectId, fakeProject)
- })
- }
- const createProjectAndInvite = (owner, projectName, email, callback) => {
- createProject(owner, projectName, (err, projectId, project) => {
- if (err) {
- return callback(err)
- }
- createInvite(owner, projectId, email, (err, invite) => {
- if (err) {
- return callback(err)
- }
- // attach the token to the invite
- invite.token = generateTokenSpy.getCall(0).returnValue
- const link = CollaboratorsEmailHandler._buildInviteUrl(project, invite)
- callback(null, project, invite, link)
- })
- })
- }
- const revokeInvite = (sendingUser, projectId, inviteId, callback) => {
- sendingUser.getCsrfToken(err => {
- if (err) {
- return callback(err)
- }
- sendingUser.request.delete(
- {
- uri: `/project/${projectId}/invite/${inviteId}`,
- },
- err => {
- if (err) {
- return callback(err)
- }
- callback()
- }
- )
- })
- }
- // Actions
- const tryFollowInviteLink = (user, link, callback) => {
- user.request.get(
- {
- uri: link,
- baseUrl: null,
- },
- callback
- )
- }
- const tryAcceptInvite = (user, invite, projectId, callback) => {
- user.getCsrfToken(err => {
- if (err) {
- return callback(err)
- }
- user.request.post(
- {
- uri: `/project/${projectId}/invite/token/${invite.token}/accept`,
- json: {
- token: invite.token,
- },
- },
- callback
- )
- })
- }
- const tryFollowLoginLink = (user, loginLink, callback) => {
- user.getCsrfToken(error => {
- if (error != null) {
- return callback(error)
- }
- user.request.get(loginLink, callback)
- })
- }
- const tryLoginUser = (user, callback) => {
- user.getCsrfToken(error => {
- if (error != null) {
- return callback(error)
- }
- user.request.post(
- {
- url: '/login',
- json: {
- email: user.email,
- password: user.password,
- 'g-recaptcha-response': 'valid',
- },
- },
- callback
- )
- })
- }
- const tryGetInviteList = (user, projectId, callback) => {
- user.getCsrfToken(error => {
- if (error != null) {
- return callback(error)
- }
- user.request.get(
- {
- url: `/project/${projectId}/invites`,
- json: true,
- },
- callback
- )
- })
- }
- const updateSharingLink = (
- sendingUser,
- projectId,
- privileges,
- opts,
- callback
- ) => {
- if (typeof opts === 'function') {
- callback = opts
- opts = {}
- }
- sendingUser.getCsrfToken(err => {
- if (err) {
- return callback(err)
- }
- sendingUser.request.post(
- {
- uri: `/project/${projectId}/sharing-link`,
- json: {
- privileges,
- ...opts,
- },
- },
- (err, response, body) => {
- if (err) {
- return callback(err)
- }
- expect(response.statusCode).to.equal(200)
- expect(body).to.include.keys('_id', 'token', 'privileges')
- callback(null, body)
- }
- )
- })
- }
- const getSharingLink = (sendingUser, projectId, callback) => {
- sendingUser.getCsrfToken(err => {
- if (err) {
- return callback(err)
- }
- sendingUser.request.get(
- {
- uri: `/project/${projectId}/sharing-link`,
- json: true,
- },
- (err, response, body) => {
- if (err) {
- return callback(err)
- }
- callback(null, response, body)
- }
- )
- })
- }
- const validateSharingLink = (user, projectId, token, callback) => {
- user.getCsrfToken(err => {
- if (err) {
- return callback(err)
- }
- user.request.post(
- {
- uri: `/project/${projectId}/share/validate`,
- json: { token },
- },
- (err, response, body) => {
- if (err) {
- return callback(err)
- }
- callback(null, response, body)
- }
- )
- })
- }
- const acceptSharingLink = (user, projectId, token, callback) => {
- user.getCsrfToken(err => {
- if (err) {
- return callback(err)
- }
- user.request.post(
- {
- uri: `/project/${projectId}/share`,
- json: {
- token,
- },
- },
- callback
- )
- })
- }
- const tryJoinProject = (user, projectId, callback) => {
- return user.getCsrfToken(error => {
- if (error != null) {
- return callback(error)
- }
- user.request.post(
- {
- url: `/project/${projectId}/join`,
- auth: {
- user: settings.apis.web.user,
- pass: settings.apis.web.pass,
- sendImmediately: true,
- },
- json: { userId: user._id },
- jar: false,
- },
- callback
- )
- })
- }
- // Expectations
- const expectProjectAccess = (user, projectId, callback) => {
- // should have access to project
- user.openProject(projectId, err => {
- expect(err).not.to.exist
- callback()
- })
- }
- const expectNoProjectAccess = (user, projectId, callback) => {
- // should not have access to project page
- user.openProject(projectId, err => {
- expect(err).to.be.instanceof(Error)
- callback()
- })
- }
- const expectInvitePage = (user, link, callback) => {
- // view invite
- tryFollowInviteLink(user, link, (err, response, body) => {
- expect(err).not.to.exist
- expect(response.statusCode).to.equal(200)
- expect(body).to.match(/<title[^>]*>Project Invite - .*<\/title>/)
- callback()
- })
- }
- const expectInvalidInvitePage = (user, link, callback) => {
- // view invalid invite
- tryFollowInviteLink(user, link, (err, response, body) => {
- expect(err).not.to.exist
- expect(response.statusCode).to.equal(404)
- expect(body).to.match(/<title[^>]*>Invalid Invite - .*<\/title>/)
- callback()
- })
- }
- const expectInviteRedirectToRegister = (user, link, callback) => {
- // view invite, redirect to `/register`
- tryFollowInviteLink(user, link, (err, response) => {
- expect(err).not.to.exist
- expect(response.statusCode).to.equal(302)
- expect(response.headers.location).to.match(/^\/register.*$/)
- user.getSession((err, session) => {
- if (err) return callback(err)
- expect(session.sharedProjectData).deep.equals({
- project_name: PROJECT_NAME,
- user_first_name: OWNER_NAME,
- })
- callback()
- })
- })
- }
- const expectLoginPage = (user, callback) => {
- tryFollowLoginLink(user, '/login', (err, response, body) => {
- expect(err).not.to.exist
- expect(response.statusCode).to.equal(200)
- expect(body).to.match(
- /<title[^>]*>(Login|Log in to Overleaf) - .*<\/title>/
- )
- callback()
- })
- }
- const expectLoginRedirectToInvite = (user, link, callback) => {
- tryLoginUser(user, (err, response) => {
- expect(err).not.to.exist
- expect(response.statusCode).to.equal(200)
- callback()
- })
- }
- const expectRegistrationRedirectToInvite = (user, link, callback) => {
- user.register((err, _user, response) => {
- expect(err).not.to.exist
- expect(response.statusCode).to.equal(200)
- if (response.body.redir === '/registration/try-premium') {
- user.request.get('/registration/onboarding', (err, response) => {
- if (err) return callback(err)
- expect(response.statusCode).to.equal(200)
- callback()
- })
- } else {
- expect(response.body.redir).to.equal(link)
- callback()
- }
- })
- }
- const expectInviteRedirectToProject = (user, link, projectId, callback) => {
- // view invite, redirect straight to project
- tryFollowInviteLink(user, link, (err, response) => {
- expect(err).not.to.exist
- expect(response.statusCode).to.equal(302)
- expect(response.headers.location).to.equal(`/project/${projectId}`)
- callback()
- })
- }
- const expectAcceptInviteAndRedirect = (user, invite, projectId, callback) => {
- // should accept the invite and redirect to project
- tryAcceptInvite(user, invite, projectId, (err, response) => {
- expect(err).not.to.exist
- expect(response.statusCode).to.equal(302)
- expect(response.headers.location).to.equal(`/project/${projectId}`)
- callback()
- })
- }
- const expectInviteListCount = (user, projectId, count, callback) => {
- tryGetInviteList(user, projectId, (err, response, body) => {
- expect(err).not.to.exist
- expect(response.statusCode).to.equal(200)
- expect(body).to.have.all.keys(['invites'])
- expect(body.invites.length).to.equal(count)
- callback()
- })
- }
- const expectInvitesInJoinProjectCount = (user, projectId, count, callback) => {
- tryJoinProject(user, projectId, (err, response, body) => {
- expect(err).not.to.exist
- expect(response.statusCode).to.equal(200)
- expect(body.project).to.contain.keys(['invites'])
- expect(body.project.invites.length).to.equal(count)
- callback()
- })
- }
- const PROJECT_NAME = 'project name for sharing test'
- const OWNER_NAME = 'sending user name'
- describe('ProjectInviteTests', function () {
- beforeEach(function (done) {
- this.sendingUser = new User()
- this.user = new User()
- this.site_admin = new User({ email: `admin+${Math.random()}@example.com` })
- this.email = `smoketestuser+${Math.random()}@example.com`
- generateTokenSpy = sinon.spy(CollaboratorsInviteHelper, 'generateToken')
- Async.series(
- [
- cb => this.sendingUser.ensureUserExists(cb),
- cb => this.sendingUser.upgradeFeatures({ collaborators: 10 }, cb),
- cb => this.sendingUser.login(cb),
- cb =>
- this.sendingUser.mongoUpdate(
- {
- $set: { first_name: OWNER_NAME },
- },
- cb
- ),
- ],
- done
- )
- })
- afterEach(function () {
- generateTokenSpy.restore()
- })
- describe('creating invites', function () {
- describe('creating two invites', function () {
- beforeEach(function (done) {
- createProject(
- this.sendingUser,
- PROJECT_NAME,
- (err, projectId, project) => {
- expect(err).not.to.exist
- this.projectId = projectId
- this.fakeProject = project
- done()
- }
- )
- })
- it('should fail if email is not a string', function (done) {
- this.sendingUser.getCsrfToken(err => {
- if (err) {
- return done(err)
- }
- this.sendingUser.request.post(
- {
- uri: `/project/${this.projectId}/invite`,
- json: {
- email: {},
- privileges: 'readAndWrite',
- },
- },
- (err, response, body) => {
- if (err) {
- return done(err)
- }
- expect(response.statusCode).to.equal(400)
- done()
- }
- )
- })
- })
- it('should fail on invalid privileges', function (done) {
- this.sendingUser.getCsrfToken(err => {
- if (err) {
- return done(err)
- }
- this.sendingUser.request.post(
- {
- uri: `/project/${this.projectId}/invite`,
- json: {
- email: this.email,
- privileges: 'invalid-privilege',
- },
- },
- (err, response, body) => {
- if (err) {
- return done(err)
- }
- expect(response.statusCode).to.equal(400)
- done()
- }
- )
- })
- })
- it('should allow the project owner to create and remove invites', function (done) {
- Async.series(
- [
- cb => expectProjectAccess(this.sendingUser, this.projectId, cb),
- cb =>
- expectInviteListCount(this.sendingUser, this.projectId, 0, cb),
- // create invite, check invite list count
- cb => {
- createInvite(
- this.sendingUser,
- this.projectId,
- this.email,
- (err, invite) => {
- if (err) {
- return cb(err)
- }
- this.invite = invite
- cb()
- }
- )
- },
- cb =>
- expectInviteListCount(this.sendingUser, this.projectId, 1, cb),
- cb =>
- revokeInvite(
- this.sendingUser,
- this.projectId,
- this.invite._id,
- cb
- ),
- cb =>
- expectInviteListCount(this.sendingUser, this.projectId, 0, cb),
- // and a second time
- cb => {
- createInvite(
- this.sendingUser,
- this.projectId,
- this.email,
- (err, invite) => {
- if (err) {
- return cb(err)
- }
- this.invite = invite
- cb()
- }
- )
- },
- cb =>
- expectInviteListCount(this.sendingUser, this.projectId, 1, cb),
- // check the joinProject view
- cb =>
- expectInvitesInJoinProjectCount(
- this.sendingUser,
- this.projectId,
- 1,
- cb
- ),
- // revoke invite
- cb =>
- revokeInvite(
- this.sendingUser,
- this.projectId,
- this.invite._id,
- cb
- ),
- cb =>
- expectInviteListCount(this.sendingUser, this.projectId, 0, cb),
- cb =>
- expectInvitesInJoinProjectCount(
- this.sendingUser,
- this.projectId,
- 0,
- cb
- ),
- ],
- done
- )
- })
- it('should allow the project owner to create many invites at once', function (done) {
- Async.series(
- [
- cb => expectProjectAccess(this.sendingUser, this.projectId, cb),
- cb =>
- expectInviteListCount(this.sendingUser, this.projectId, 0, cb),
- // create first invite
- cb => {
- createInvite(
- this.sendingUser,
- this.projectId,
- this.email,
- (err, invite) => {
- if (err) {
- return cb(err)
- }
- this.inviteOne = invite
- cb()
- }
- )
- },
- cb =>
- expectInviteListCount(this.sendingUser, this.projectId, 1, cb),
- // and a second
- cb => {
- createInvite(
- this.sendingUser,
- this.projectId,
- this.email,
- (err, invite) => {
- if (err) {
- return cb(err)
- }
- this.inviteTwo = invite
- cb()
- }
- )
- },
- // should have two
- cb =>
- expectInviteListCount(this.sendingUser, this.projectId, 2, cb),
- cb =>
- expectInvitesInJoinProjectCount(
- this.sendingUser,
- this.projectId,
- 2,
- cb
- ),
- // revoke first
- cb =>
- revokeInvite(
- this.sendingUser,
- this.projectId,
- this.inviteOne._id,
- cb
- ),
- cb =>
- expectInviteListCount(this.sendingUser, this.projectId, 1, cb),
- // revoke second
- cb =>
- revokeInvite(
- this.sendingUser,
- this.projectId,
- this.inviteTwo._id,
- cb
- ),
- cb =>
- expectInviteListCount(this.sendingUser, this.projectId, 0, cb),
- ],
- done
- )
- })
- })
- })
- describe('clicking the invite link', function () {
- beforeEach(function (done) {
- createProjectAndInvite(
- this.sendingUser,
- PROJECT_NAME,
- this.email,
- (err, project, invite, link) => {
- expect(err).not.to.exist
- this.projectId = project._id
- this.fakeProject = project
- this.invite = invite
- this.link = link
- done()
- }
- )
- })
- describe('user is logged in already', function () {
- beforeEach(function (done) {
- this.user.login(done)
- })
- describe('user is already a member of the project', function () {
- beforeEach(function (done) {
- Async.series(
- [
- cb => expectInvitePage(this.user, this.link, cb),
- cb =>
- expectAcceptInviteAndRedirect(
- this.user,
- this.invite,
- this.projectId,
- cb
- ),
- cb => expectProjectAccess(this.user, this.projectId, cb),
- ],
- done
- )
- })
- describe('when user clicks on the invite a second time', function () {
- it('should just redirect to the project page', function (done) {
- Async.series(
- [
- cb => expectProjectAccess(this.user, this.projectId, cb),
- cb =>
- expectInviteRedirectToProject(
- this.user,
- this.link,
- this.projectId,
- cb
- ),
- cb => expectProjectAccess(this.user, this.projectId, cb),
- ],
- done
- )
- })
- describe('when the user recieves another invite to the same project', function () {
- it('should redirect to the project page', function (done) {
- Async.series(
- [
- cb => {
- createInvite(
- this.sendingUser,
- this.projectId,
- this.email,
- (err, invite) => {
- if (err) {
- throw err
- }
- this.secondInvite = invite
- const token = generateTokenSpy.getCall(1).returnValue
- this.secondLink =
- CollaboratorsEmailHandler._buildInviteUrl(
- this.fakeProject,
- { ...invite, token }
- )
- cb()
- }
- )
- },
- cb =>
- expectInviteRedirectToProject(
- this.user,
- this.secondLink,
- this.projectId,
- cb
- ),
- cb => expectProjectAccess(this.user, this.projectId, cb),
- cb =>
- revokeInvite(
- this.sendingUser,
- this.projectId,
- this.secondInvite._id,
- cb
- ),
- ],
- done
- )
- })
- })
- })
- })
- describe('user is not a member of the project', function () {
- it('should not grant access if the user does not accept the invite', function (done) {
- Async.series(
- [
- cb => expectInvitePage(this.user, this.link, cb),
- cb => expectNoProjectAccess(this.user, this.projectId, cb),
- ],
- done
- )
- })
- it('should render the invalid-invite page if the token is invalid', function (done) {
- Async.series(
- [
- cb => {
- const link = this.link.replace(
- this.invite.token,
- 'not_a_real_token'
- )
- expectInvalidInvitePage(this.user, link, cb)
- },
- cb => expectNoProjectAccess(this.user, this.projectId, cb),
- cb => expectNoProjectAccess(this.user, this.projectId, cb),
- ],
- done
- )
- })
- it('should allow the user to accept the invite and access the project', function (done) {
- Async.series(
- [
- cb => expectInvitePage(this.user, this.link, cb),
- cb =>
- expectAcceptInviteAndRedirect(
- this.user,
- this.invite,
- this.projectId,
- cb
- ),
- cb => expectProjectAccess(this.user, this.projectId, cb),
- ],
- done
- )
- })
- })
- })
- describe('user is not logged in initially', function () {
- describe('registration prompt workflow with valid token', function () {
- before(function () {
- if (!Features.hasFeature('registration')) {
- this.skip()
- }
- })
- it('should redirect to the register page', function (done) {
- expectInviteRedirectToRegister(this.user, this.link, done)
- })
- it('should allow user to accept the invite if the user registers a new account', function (done) {
- Async.series(
- [
- cb => expectInviteRedirectToRegister(this.user, this.link, cb),
- cb =>
- expectRegistrationRedirectToInvite(this.user, this.link, cb),
- cb => expectInvitePage(this.user, this.link, cb),
- cb =>
- expectAcceptInviteAndRedirect(
- this.user,
- this.invite,
- this.projectId,
- cb
- ),
- cb => expectProjectAccess(this.user, this.projectId, cb),
- ],
- done
- )
- })
- })
- describe('registration prompt workflow with non-valid token', function () {
- before(function () {
- if (!Features.hasFeature('registration')) {
- this.skip()
- }
- })
- it('should redirect to the register page', function (done) {
- Async.series(
- [
- cb => expectInviteRedirectToRegister(this.user, this.link, cb),
- cb => expectNoProjectAccess(this.user, this.projectId, cb),
- ],
- done
- )
- })
- it('should display invalid-invite right away', function (done) {
- const badLink = this.link.replace(
- this.invite.token,
- 'not_a_real_token'
- )
- Async.series(
- [
- cb => expectInvalidInvitePage(this.user, badLink, cb),
- cb => expectNoProjectAccess(this.user, this.projectId, cb),
- ],
- done
- )
- })
- })
- describe('login workflow with valid token', function () {
- beforeEach(function (done) {
- this.user.ensureUserExists(done)
- })
- it('should redirect to the register page', function (done) {
- Async.series(
- [
- cb => expectInviteRedirectToRegister(this.user, this.link, cb),
- cb => expectNoProjectAccess(this.user, this.projectId, cb),
- ],
- done
- )
- })
- it('should allow the user to login to view the invite', function (done) {
- Async.series(
- [
- cb => expectInviteRedirectToRegister(this.user, this.link, cb),
- cb => expectLoginPage(this.user, cb),
- cb => expectLoginRedirectToInvite(this.user, this.link, cb),
- cb => expectInvitePage(this.user, this.link, cb),
- cb => expectNoProjectAccess(this.user, this.projectId, cb),
- ],
- done
- )
- })
- it('should allow user to accept the invite if the user logs in', function (done) {
- Async.series(
- [
- cb => expectInviteRedirectToRegister(this.user, this.link, cb),
- cb => expectLoginPage(this.user, cb),
- cb => expectLoginRedirectToInvite(this.user, this.link, cb),
- cb => expectInvitePage(this.user, this.link, cb),
- cb =>
- expectAcceptInviteAndRedirect(
- this.user,
- this.invite,
- this.projectId,
- cb
- ),
- cb => expectProjectAccess(this.user, this.projectId, cb),
- ],
- done
- )
- })
- })
- describe('login workflow with non-valid token', function () {
- it('should redirect to the register page', function (done) {
- Async.series(
- [
- cb => expectInviteRedirectToRegister(this.user, this.link, cb),
- cb => expectNoProjectAccess(this.user, this.projectId, cb),
- ],
- done
- )
- })
- it('should show the invalid-invite page right away', function (done) {
- const badLink = this.link.replace(
- this.invite.token,
- 'not_a_real_token'
- )
- Async.series(
- [
- cb => expectInvalidInvitePage(this.user, badLink, cb),
- cb => expectNoProjectAccess(this.user, this.projectId, cb),
- ],
- done
- )
- })
- })
- })
- })
- describe('sharing link routes', function () {
- beforeEach(function (done) {
- this.projectName = `sharing-link-test-${Math.random()}`
- createProject(this.sendingUser, this.projectName, (err, projectId) => {
- if (err) {
- return done(err)
- }
- this.projectId = projectId
- done()
- })
- })
- it('should create and fetch sharing link without listing reusable invite in invite list', function (done) {
- Async.series(
- [
- cb => expectInviteListCount(this.sendingUser, this.projectId, 0, cb),
- cb => {
- updateSharingLink(
- this.sendingUser,
- this.projectId,
- 'readOnly',
- (err, link) => {
- if (err) {
- return cb(err)
- }
- this.sharingLink = link
- expect(link.privileges).to.equal('readOnly')
- cb()
- }
- )
- },
- cb => expectInviteListCount(this.sendingUser, this.projectId, 0, cb),
- cb => {
- getSharingLink(
- this.sendingUser,
- this.projectId,
- (err, response, body) => {
- if (err) {
- return cb(err)
- }
- expect(response.statusCode).to.equal(200)
- expect(body.privileges).to.equal('readOnly')
- expect(body.token).to.equal(this.sharingLink.token)
- cb()
- }
- )
- },
- ],
- done
- )
- })
- it('should validate and accept sharing link via /share flow', function (done) {
- Async.series(
- [
- cb => this.user.login(cb),
- cb => {
- updateSharingLink(
- this.sendingUser,
- this.projectId,
- 'readOnly',
- (err, link) => {
- if (err) {
- return cb(err)
- }
- this.sharingLink = link
- cb()
- }
- )
- },
- cb => expectNoProjectAccess(this.user, this.projectId, cb),
- cb => {
- validateSharingLink(
- this.user,
- this.projectId,
- this.sharingLink.token,
- (err, response, body) => {
- if (err) {
- return cb(err)
- }
- expect(response.statusCode).to.equal(200)
- expect(body.valid).to.equal(true)
- cb()
- }
- )
- },
- cb => {
- acceptSharingLink(
- this.user,
- this.projectId,
- this.sharingLink.token,
- (err, response) => {
- if (err) {
- return cb(err)
- }
- expect(response.statusCode).to.equal(302)
- expect(response.headers.location).to.equal(
- `/project/${this.projectId}`
- )
- cb()
- }
- )
- },
- cb => expectProjectAccess(this.user, this.projectId, cb),
- ],
- done
- )
- })
- it('should reject invalid sharing link token in validation', function (done) {
- Async.series(
- [
- cb => this.user.login(cb),
- cb => {
- validateSharingLink(
- this.user,
- this.projectId,
- 'not-a-valid-token',
- (err, response, body) => {
- if (err) {
- return cb(err)
- }
- expect(response.statusCode).to.equal(200)
- expect(body.valid).to.equal(false)
- cb()
- }
- )
- },
- ],
- done
- )
- })
- it('should reject validation for user not in required subscription group', function (done) {
- if (!Features.hasFeature('saas')) {
- this.skip()
- }
- const subscription = new Subscription({
- adminId: this.sendingUser._id,
- memberIds: [this.sendingUser._id],
- groupPlan: true,
- planCode: 'group_professional',
- paymentProvider: { state: 'active' },
- })
- let sharingLink
- Async.series(
- [
- cb => this.user.login(cb),
- cb => subscription.ensureExists(cb),
- cb => {
- updateSharingLink(
- this.sendingUser,
- this.projectId,
- 'readOnly',
- { subscriptionId: subscription._id.toString() },
- (err, link) => {
- if (err) {
- return cb(err)
- }
- sharingLink = link
- cb()
- }
- )
- },
- cb => {
- validateSharingLink(
- this.user,
- this.projectId,
- sharingLink.token,
- (err, response, body) => {
- if (err) {
- return cb(err)
- }
- expect(response.statusCode).to.equal(200)
- expect(body.valid).to.equal(false)
- cb()
- }
- )
- },
- cb => subscription.addMember(this.user._id, cb),
- cb => {
- validateSharingLink(
- this.user,
- this.projectId,
- sharingLink.token,
- (err, response, body) => {
- if (err) {
- return cb(err)
- }
- expect(response.statusCode).to.equal(200)
- expect(body.valid).to.equal(true)
- cb()
- }
- )
- },
- ],
- done
- )
- })
- })
- })
|