Export Wekan now server-based with proper auth

This commit is contained in:
Xavier Priour 2015-12-16 21:54:35 +01:00
parent efe7c21d57
commit d08e1cc45b
6 changed files with 42 additions and 26 deletions

View file

@ -73,4 +73,4 @@ perak:markdown
seriousm:emoji-continued
templates:tabs
verron:autosize
pfafman:filesaver
simple:json-routes

View file

@ -111,7 +111,6 @@ peerlibrary:blaze-components@0.15.1
peerlibrary:computed-field@0.3.1
peerlibrary:reactive-field@0.1.0
perak:markdown@1.0.5
pfafman:filesaver@0.2.2
promise@0.5.1
raix:eventemitter@0.1.3
raix:handlebar-helpers@0.2.5
@ -126,6 +125,7 @@ seriousm:emoji-continued@1.4.0
service-configuration@1.0.5
session@1.1.1
sha@1.0.4
simple:json-routes@1.0.4
softwarerero:accounts-t9n@1.1.7
spacebars@1.0.7
spacebars-compiler@1.0.7

View file

@ -56,7 +56,7 @@ template(name="boardMenuPopup")
if currentUser.isBoardAdmin
hr
ul.pop-over-list
li: a.js-export-board {{_ 'export-board'}}
li: a(href="{{exportUrl}}", download="{{exportFilename}}") {{_ 'export-board'}}
li: a.js-archive-board {{_ 'archive-board'}}
template(name="boardVisibilityList")

View file

@ -1,4 +1,3 @@
/* global saveAs */
Template.boardMenuPopup.events({
'click .js-rename-board': Popup.open('boardChangeTitle'),
'click .js-open-archives'() {
@ -14,25 +13,18 @@ Template.boardMenuPopup.events({
// confirm that the board was successfully archived.
FlowRouter.go('home');
}),
'click .js-export-board'() {
const boardId = Session.get('currentBoard');
Meteor.call('exportBoard', boardId, (error, response) => {
if(error) {
// the only error we can anticipate is accessing a non-authorized board
// and this should have been caugh by UI before.
// So no treatment here for the time being.
} else {
const dataToSave = new Blob([JSON.stringify(response)], {type: 'application/json;charset=utf-8'});
const filename = `wekan-export-board-${boardId}.json`;
saveAs(dataToSave, filename);
}
});
},
});
Template.boardMenuPopup.helpers({
urlExport() {
return Meteor.absoluteUrl(`api/b/${Session.get('currentBoard')}`);
exportUrl() {
const boardId = Session.get('currentBoard');
const userId = Meteor.userId();
const loginToken = Accounts._storedLoginToken();
return Meteor.absoluteUrl(`api/b/${boardId}/${userId}/${loginToken}`);
},
exportFilename() {
const boardId = Session.get('currentBoard');
return `wekan-export-board-${boardId}.json`;
},
});

View file

@ -80,15 +80,15 @@ Boards.attachSchema(new SimpleSchema({
Boards.helpers({
/**
* Is current logged-in user authorized to view this board?
* Is supplied user authorized to view this board?
*/
isVisibleByUser() {
isVisibleBy(user) {
if(this.isPublic()) {
// public boards are visible to everyone
return true;
} else {
// otherwise you have to be logged-in and active member
return this.isActiveMember(Meteor.userId());
return this.isActiveMember(user._id);
}
},

View file

@ -1,11 +1,30 @@
/* global JsonRoutes */
if(Meteor.isServer) {
JsonRoutes.add('get', '/api/b/:boardId/:userId/:loginToken', function (req, res) {
const { userId, loginToken, boardId } = req.params;
const hashToken = Accounts._hashLoginToken(loginToken);
const user = Meteor.users.findOne({
_id: userId,
'services.resume.loginTokens.hashedToken': hashToken,
});
const exporter = new Exporter(boardId);
if(user && exporter.canExport(user)) {
JsonRoutes.sendResult(res, 200, exporter.build());
} else {
// we could send an explicit error message, but on the other
// hand the only way to get there is by hacking the UI so...
JsonRoutes.sendResult(res, 403);
}
});
}
Meteor.methods({
exportBoard(boardId) {
check(boardId, String);
const board = Boards.findOne(boardId);
if(board.isVisibleByUser()) {
const exporter = new Exporter(boardId);
const exporter = new Exporter(boardId);
if(exporter.canExport(Meteor.user())) {
return exporter.build();
} else {
throw new Meteor.Error('error-board-notAMember');
@ -56,4 +75,9 @@ class Exporter {
result.users = Users.find(byUserIds, userFields).fetch();
return result;
}
canExport(user) {
const board = Boards.findOne(this._boardId);
return board && board.isVisibleBy(user);
}
}