diff options
author | Xavier Priour <xavier.priour@bubblyware.com> | 2015-12-16 21:54:35 +0100 |
---|---|---|
committer | Xavier Priour <xavier.priour@bubblyware.com> | 2015-12-16 21:58:43 +0100 |
commit | d08e1cc45b7f894f360f3a8a89e235ccc47b8f96 (patch) | |
tree | c6e38bc7e4f25a7185d787191a34959ce673b1a4 | |
parent | efe7c21d579a0cffe682741d2daf832062001a3a (diff) | |
download | wekan-d08e1cc45b7f894f360f3a8a89e235ccc47b8f96.tar.gz wekan-d08e1cc45b7f894f360f3a8a89e235ccc47b8f96.tar.bz2 wekan-d08e1cc45b7f894f360f3a8a89e235ccc47b8f96.zip |
Export Wekan now server-based with proper auth
-rw-r--r-- | .meteor/packages | 2 | ||||
-rw-r--r-- | .meteor/versions | 2 | ||||
-rw-r--r-- | client/components/boards/boardHeader.jade | 2 | ||||
-rw-r--r-- | client/components/boards/boardHeader.js | 26 | ||||
-rw-r--r-- | models/boards.js | 6 | ||||
-rw-r--r-- | models/export.js | 30 |
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); + } } |