summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorXavier Priour <xavier.priour@bubblyware.com>2015-12-16 21:54:35 +0100
committerXavier Priour <xavier.priour@bubblyware.com>2015-12-16 21:58:43 +0100
commitd08e1cc45b7f894f360f3a8a89e235ccc47b8f96 (patch)
treec6e38bc7e4f25a7185d787191a34959ce673b1a4
parentefe7c21d579a0cffe682741d2daf832062001a3a (diff)
downloadwekan-d08e1cc45b7f894f360f3a8a89e235ccc47b8f96.tar.gz
wekan-d08e1cc45b7f894f360f3a8a89e235ccc47b8f96.tar.bz2
wekan-d08e1cc45b7f894f360f3a8a89e235ccc47b8f96.zip
Export Wekan now server-based with proper auth
-rw-r--r--.meteor/packages2
-rw-r--r--.meteor/versions2
-rw-r--r--client/components/boards/boardHeader.jade2
-rw-r--r--client/components/boards/boardHeader.js26
-rw-r--r--models/boards.js6
-rw-r--r--models/export.js30
6 files changed, 42 insertions, 26 deletions
diff --git a/.meteor/packages b/.meteor/packages
index c9d81082..ad6ddf0b 100644
--- a/.meteor/packages
+++ b/.meteor/packages
@@ -73,4 +73,4 @@ perak:markdown
seriousm:emoji-continued
templates:tabs
verron:autosize
-pfafman:filesaver
+simple:json-routes
diff --git a/.meteor/versions b/.meteor/versions
index 6c0486b6..65c43d86 100644
--- a/.meteor/versions
+++ b/.meteor/versions
@@ -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
diff --git a/client/components/boards/boardHeader.jade b/client/components/boards/boardHeader.jade
index eb7ca984..a743311b 100644
--- a/client/components/boards/boardHeader.jade
+++ b/client/components/boards/boardHeader.jade
@@ -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")
diff --git a/client/components/boards/boardHeader.js b/client/components/boards/boardHeader.js
index b5bb0dbb..0c9b5794 100644
--- a/client/components/boards/boardHeader.js
+++ b/client/components/boards/boardHeader.js
@@ -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`;
},
});
diff --git a/models/boards.js b/models/boards.js
index cdf83ce0..d5363f4e 100644
--- a/models/boards.js
+++ b/models/boards.js
@@ -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);
}
},
diff --git a/models/export.js b/models/export.js
index aab81c64..8d1be64e 100644
--- a/models/export.js
+++ b/models/export.js
@@ -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);
+ }
}