Skip to content

Commit

Permalink
feat(server): allow the use of tokens to get a document
Browse files Browse the repository at this point in the history
  • Loading branch information
targos committed Jul 26, 2016
1 parent ed3efd9 commit c1bf0f1
Show file tree
Hide file tree
Showing 8 changed files with 77 additions and 19 deletions.
3 changes: 2 additions & 1 deletion .babelrc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"plugins": [
"transform-async-to-generator",
"transform-es2015-spread"
"transform-es2015-spread",
"transform-es2015-parameters"
]
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
"babel-cli": "^6.7.5",
"babel-eslint": "^6.0.2",
"babel-plugin-transform-async-to-generator": "^6.7.4",
"babel-plugin-transform-es2015-parameters": "^6.11.4",
"babel-plugin-transform-es2015-spread": "^6.6.5",
"eslint": "^3.1.1",
"eslint-config-cheminfo": "^1.1.1",
Expand Down
28 changes: 20 additions & 8 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -321,9 +321,8 @@ class Couch {
return cumRows.filter((r, idx) => idx < options.limit);
}

async getEntriesByUserAndRights(user, rights, options) {
async getEntriesByUserAndRights(user, rights, options = {}) {
debug(`getEntriesByUserAndRights (${user}, ${rights})`);
options = options || {};
const limit = options.limit;
const skip = options.skip;

Expand All @@ -347,7 +346,7 @@ class Couch {
return await Promise.all(allowedDocs.map(doc => nanoPromise.getDocument(this._db, doc.id)));
}

async getEntryByIdAndRights(id, user, rights, options) {
async getEntryByIdAndRights(id, user, rights, options = {}) {
debug(`getEntryByIdAndRights (${id}, ${user}, ${rights})`);
await this.open();

Expand All @@ -361,8 +360,7 @@ class Couch {
hisEntry = owners[0];
}

const ok = await validateRights(this._db, hisEntry.value, user, rights);
if (ok[0]) {
if (await validateTokenOrRights(this._db, hisEntry.id, hisEntry.value, rights, user, options.token)) {
debug.trace(`user ${user} has access`);
return await nanoPromise.getDocument(this._db, hisEntry.id, options);
}
Expand All @@ -371,7 +369,7 @@ class Couch {
throw new CouchError('user has no access', 'unauthorized');
}

async getEntryByUuidAndRights(uuid, user, rights, options) {
async getEntryByUuidAndRights(uuid, user, rights, options = {}) {
debug(`getEntryByUuidAndRights (${uuid}, ${user}, ${rights})`);
await this.open();

Expand All @@ -386,8 +384,7 @@ class Couch {
}

debug.trace('check rights');
const ok = await validateRights(this._db, doc.$owners, user, rights);
if (ok[0]) {
if (await validateTokenOrRights(this._db, uuid, doc.$owners, rights, user, options.token)) {
debug.trace(`user ${user} has access`);
if (!options) {
return doc;
Expand Down Expand Up @@ -983,6 +980,21 @@ function validateRights(db, ownerArrays, user, rights) {
//return Promise.all(checks).then(result => result.some(value => value === true));
}

async function validateTokenOrRights(db, uuid, owners, rights, user, token) {
if (!Array.isArray(rights)) {
rights = [rights];
}
if (token && token.$kind === 'entry' && token.uuid === uuid) {
for (var i = 0; i < rights.length; i++) {
if (token.rights.indexOf(rights[i]) !== -1) {
return true;
}
}
}
const ok = await validateRights(db, owners, user, rights);
return ok[0];
}

async function getGroup(db, name) {
debug.trace('get group');
const groups = await nanoPromise.queryView(db, 'groupByName', {key: name, reduce: false, include_docs: true});
Expand Down
7 changes: 7 additions & 0 deletions src/server/middleware/couch.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ exports.setupCouch = function*(next) {
yield next;
};

exports.tokenLookup = function* (next) {
if (this.query.token) {
this.query.token = yield this.state.couch.getToken(this.query.token);
}
yield next;
};

exports.getDocumentByUuid = function*() {
try {
const doc = yield this.state.couch.getEntryByUuid(this.params.uuid, this.state.userEmail, this.query);
Expand Down
1 change: 1 addition & 0 deletions src/server/routes/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const couch = require('../middleware/couch');
const util = require('../middleware/util');

router.use(couch.setupCouch);
router.use(couch.tokenLookup);

const parseJson1mb = util.parseBody({jsonLimit: '1mb'});
const parseJson100mb = util.parseBody({jsonLimit: '100mb'});
Expand Down
20 changes: 14 additions & 6 deletions src/util/nanoPromise.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,10 @@ exports.createDatabase = function (nano, database) {
});
};

exports.getDocument = function (db, docID, options) {
exports.getDocument = function (db, docID, options = {}) {
return new Promise((resolve, reject) => {
debug.trace(`getDocument ${docID}`);
cleanOptions(options);
db.get(docID, options, function (err, result) {
if (err) {
if (err.statusCode === 404 && (err.reason === 'missing' || err.reason === 'deleted')) {
Expand All @@ -82,14 +83,13 @@ exports.insertDocument = function (db, doc) {
});
};

exports.queryView = function (db, view, params, options) {
params = params || {};
options = options || {};
exports.queryView = function (db, view, params = {}, options = {}) {
if (!hasOwnProperty.call(params, 'reduce')) {
params.reduce = false;
}
return new Promise((resolve, reject) => {
debug.trace(`queryView ${view}`);
cleanOptions(params);
db.view(constants.DESIGN_DOC_NAME, view, params, function (err, body) {
if (err) return reject(err);
if (options.onlyValue) {
Expand Down Expand Up @@ -149,9 +149,10 @@ exports.attachFiles = function (db, doc, files) {
});
};

exports.getAttachment = function (db, doc, name, asStream, options) {
exports.getAttachment = function (db, doc, name, asStream, options = {}) {
return new Promise((resolve, reject) => {
debug.trace(`get attachment ${doc}/${name}`);
cleanOptions(options);
if (asStream) {
const stream = db.attachment.get(doc, name, options);
resolve(stream);
Expand All @@ -164,12 +165,19 @@ exports.getAttachment = function (db, doc, name, asStream, options) {
});
};

exports.request = function (nano, options) {
exports.request = function (nano, options = {}) {
return new Promise((resolve, reject) => {
debug.trace('request');
cleanOptions(options);
nano.request(options, function (err, result) {
if (err) return reject(err);
resolve(result);
});
});
};

function cleanOptions(options) {
if (options.token) {
delete options.token;
}
}
11 changes: 11 additions & 0 deletions test/data/noRights.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ function populate(db) {
$type: 'entry',
$owners: ['b@b.com'],
$id: 'B',
_id: 'B',
$creationDate: 0,
$modificationDate: 0,
$content: {}
Expand Down Expand Up @@ -117,6 +118,16 @@ function populate(db) {
$modificationDate: 0
}));

prom.push(insertDocument(db, {
$type: 'token',
$kind: 'entry',
$owner: 'x@x.com',
$id: 'mytoken',
$creationDate: 0,
uuid: 'A',
rights: ['read']
}));

return Promise.all(prom);
}

Expand Down
25 changes: 21 additions & 4 deletions test/rest-api/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,23 @@ describe('basic rest-api as anonymous (noRights)', function () {
before(noRights);

it('get an entry', function () {
return couch.getEntryById('A', 'b@b.com').then(entry => {
return request.get(`/db/test/entry/${entry._id}`)
.expect(401);
});
return request.get('/db/test/entry/A')
.expect(401);
});

it('get an entry with token', function () {
return request.get('/db/test/entry/A?token=mytoken')
.expect(200);
});

it('get an entry with token (wrong uuid)', function () {
return request.get('/db/test/entry/B?token=mytoken')
.expect(401);
});

it('not allowed to create a token', function () {
return request.post('/db/test/entry/A/_token')
.expect(401);
});

it('get all entries', function () {
Expand Down Expand Up @@ -128,6 +141,10 @@ describe('basic rest-api as b@b.com', function () {
});
});

it('create token', function () {
return request.post('/db/test/entry/C/_token').expect(201);
});

it('delete document', function () {
return request.del('/db/test/entry/C')
.expect(200)
Expand Down

0 comments on commit c1bf0f1

Please sign in to comment.