Export: improved API routes

- use an explicit "boards" domain: /api/boards/:boardId
- pass authToken as a request parameter: /api/boards/:boardId?authToken=:token
- in the future, same route can be used with authToken set in the Authenticate: header easily
This commit is contained in:
Xavier Priour 2015-12-17 23:57:28 +01:00
parent a45a899137
commit 115ea533f6
3 changed files with 22 additions and 16 deletions

View file

@ -18,9 +18,8 @@ Template.boardMenuPopup.events({
Template.boardMenuPopup.helpers({ Template.boardMenuPopup.helpers({
exportUrl() { exportUrl() {
const boardId = Session.get('currentBoard'); const boardId = Session.get('currentBoard');
const userId = Meteor.userId();
const loginToken = Accounts._storedLoginToken(); const loginToken = Accounts._storedLoginToken();
return Meteor.absoluteUrl(`api/b/${boardId}/${userId}/${loginToken}`); return Meteor.absoluteUrl(`api/boards/${boardId}?authToken=${loginToken}`);
}, },
exportFilename() { exportFilename() {
const boardId = Session.get('currentBoard'); const boardId = Session.get('currentBoard');

View file

@ -88,7 +88,7 @@ Boards.helpers({
return true; return true;
} else { } else {
// otherwise you have to be logged-in and active member // otherwise you have to be logged-in and active member
return this.isActiveMember(user._id); return user && this.isActiveMember(user._id);
} }
}, },

View file

@ -1,29 +1,36 @@
/* global JsonRoutes */ /* global JsonRoutes */
if(Meteor.isServer) { if(Meteor.isServer) {
// todo XXX once we have a real API in place, move that route there // todo XXX once we have a real API in place, move that route there
// todo XXX also share the route definition between the client and the server
// so that we could use something like ApiRoutes.path('boards/export', boardId)
// on the client instead of copy/pasting the route path manually between the client and the server.
/* /*
* This route is used to export the board FROM THE APPLICATION. * This route is used to export the board FROM THE APPLICATION.
* We want to identify the logged-in user without asking for password again, * If user is already logged-in, pass loginToken as param "authToken":
* but the server-side API routing has no notion of "current user". * '/api/boards/:boardId?authToken=:token'
* So we have to pass login information (id + token) to authenticate.
* *
* See https://blog.kayla.com.au/server-side-route-authentication-in-meteor/ * See https://blog.kayla.com.au/server-side-route-authentication-in-meteor/
* for detailed explanations * for detailed explanations
*/ */
JsonRoutes.add('get', '/api/b/:boardId/:userId/:loginToken', function (req, res) { JsonRoutes.add('get', '/api/boards/:boardId', function (req, res) {
const { userId, loginToken, boardId } = req.params; const boardId = req.params.boardId;
let user = null;
// todo XXX for real API, first look for token in Authentication: header
// then fallback to parameter
const loginToken = req.query.authToken;
if (loginToken) {
const hashToken = Accounts._hashLoginToken(loginToken); const hashToken = Accounts._hashLoginToken(loginToken);
const user = Meteor.users.findOne({ user = Meteor.users.findOne({
_id: userId,
'services.resume.loginTokens.hashedToken': hashToken, 'services.resume.loginTokens.hashedToken': hashToken,
}); });
}
const exporter = new Exporter(boardId); const exporter = new Exporter(boardId);
if(user && exporter.canExport(user)) { if(exporter.canExport(user)) {
JsonRoutes.sendResult(res, 200, exporter.build()); JsonRoutes.sendResult(res, 200, exporter.build());
} else { } else {
// we could send an explicit error message, but on the other // we could send an explicit error message, but on the other hand the only way to
// hand the only way to get there is by hacking the UI so... // get there is by hacking the UI so let's keep it raw.
JsonRoutes.sendResult(res, 403); JsonRoutes.sendResult(res, 403);
} }
}); });