Add SharingMiddleware for permission checks and small fixes

This commit is contained in:
Julius Haertl
2016-08-24 01:22:45 +02:00
parent 33e99b9e7c
commit 2deffacd98
21 changed files with 452 additions and 115 deletions

View File

@@ -1,43 +1,25 @@
<?php <?php
/** /**
* ownCloud - deck * @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
* *
* @author Julius Härtl <jus@bitgrid.net> * @author Julius Härtl <jus@bitgrid.net>
* @copyright Julius Härtl 2016 *
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/ */
namespace OCA\Deck\AppInfo; $app = new \OCA\Deck\AppInfo\Application();
$app->registerNavigationEntry();
use OCP\AppFramework\App;
require_once __DIR__ . '/autoload.php';
$app = new App('deck');
$container = $app->getContainer();
$container->query('OCP\INavigationManager')->add(function () use ($container) {
$urlGenerator = $container->query('OCP\IURLGenerator');
$l10n = $container->query('OCP\IL10N');
return [
// the string under which your app will be referenced in owncloud
'id' => 'deck',
// sorting weight for the navigation. The higher the number, the higher
// will it be listed in the navigation
'order' => 10,
// the route that will be shown on startup
'href' => $urlGenerator->linkToRoute('deck.page.index'),
// the icon that will be shown in the navigation
// this file needs to exist in img/
'icon' => $urlGenerator->imagePath('deck', 'app.svg'),
// the title of your application. This will be used in the
// navigation or on the settings page of your app
'name' => $l10n->t('Deck'),
];
});

View File

@@ -56,9 +56,6 @@ return [
['name' => 'card#unarchive', 'url' => '/cards/{cardId}/unarchive', 'verb' => 'PUT'], ['name' => 'card#unarchive', 'url' => '/cards/{cardId}/unarchive', 'verb' => 'PUT'],
['name' => 'card#assignLabel', 'url' => '/cards/{cardId}/label/{labelId}', 'verb' => 'POST'], ['name' => 'card#assignLabel', 'url' => '/cards/{cardId}/label/{labelId}', 'verb' => 'POST'],
['name' => 'card#removeLabel', 'url' => '/cards/{cardId}/label/{labelId}', 'verb' => 'DELETE'], ['name' => 'card#removeLabel', 'url' => '/cards/{cardId}/label/{labelId}', 'verb' => 'DELETE'],
// TODO: card - assign user
['name' => 'card#assignUser', 'url' => '/cards/{cardId}/user/{labelId}', 'verb' => 'POST'],
['name' => 'card#removeUser', 'url' => '/cards/{cardId}/user/{labelId}', 'verb' => 'DELETE'],
// labels // labels
['name' => 'label#create', 'url' => '/labels', 'verb' => 'POST'], ['name' => 'label#create', 'url' => '/labels', 'verb' => 'POST'],
@@ -66,9 +63,6 @@ return [
['name' => 'label#delete', 'url' => '/labels/{labelId}', 'verb' => 'DELETE'], ['name' => 'label#delete', 'url' => '/labels/{labelId}', 'verb' => 'DELETE'],
// TODO: Implement public board sharing // TODO: Implement public board sharing
['name' => 'public#index', 'url' => '/public/board/:hash', 'verb' => 'GET'],
['name' => 'public#board', 'url' => '/public/board/ajax/:hash', 'verb' => 'GET'],
// TODO: API for external access // TODO: API for external access
] ]

View File

@@ -862,3 +862,9 @@ button:hover {
.icon-archive { .icon-archive {
background-image: url('../img/archive.svg'); background-image: url('../img/archive.svg');
} }
.icon-archive-white {
background-image: url('../img/archive-white.svg');
}
.icon-details {
background-image: url('../img/details-white.svg');
}

View File

@@ -28,22 +28,38 @@ use OCA\Deck\Middleware\SharingMiddleware;
class Application extends App { class Application extends App {
public function __construct(array $urlParams = array()) { public function __construct(array $urlParams = array()) {
parent::__construct('deck', $urlParams); parent::__construct('deck', $urlParams);
$container = $this->getContainer(); $container = $this->getContainer();
$server = $container->getServer(); $server = $container->getServer();
$container->registerService('SharingMiddleware', function ($c) use ($server) { // This is currently unused
$container->registerService('SharingMiddleware', function ($container) use ($server) {
return new SharingMiddleware( return new SharingMiddleware(
$server->getUserManager(), $container,
$server->getGroupManager(), $server->getRequest(),
$server->getDatabaseConnection(), $server->getUserSession(),
$server->getUserSession()->getUser()->getUID() // FIXME: ERROR when logged out!!! $container->query('ControllerMethodReflector')
); );
}); });
$container->registerMiddleware('SharingMiddleware'); $container->registerMiddleware('SharingMiddleware');
}
public function registerNavigationEntry() {
$container = $this->getContainer();
$container->query('OCP\INavigationManager')->add(function () use ($container) {
$urlGenerator = $container->query('OCP\IURLGenerator');
$l10n = $container->query('OCP\IL10N');
return [
'id' => 'deck',
'order' => 10,
'href' => $urlGenerator->linkToRoute('deck.page.index'),
'icon' => $urlGenerator->imagePath('deck', 'app.svg'),
'name' => $l10n->t('Deck'),
];
});
} }
} }

View File

@@ -63,16 +63,14 @@ class BoardController extends Controller {
* @NoAdminRequired * @NoAdminRequired
*/ */
public function index() { public function index() {
return $this->boardService->findAll($this->userInfo); return $this->boardService->findAll($this->userInfo);
} }
/** /**
* @NoAdminRequired * @NoAdminRequired
* @RequireReadPermission
*/ */
public function read($boardId) { public function read($boardId) {
// FIXME: Remove as this is just for testing if loading animation works out nicely
//usleep(2000);
return $this->boardService->find($this->userId, $boardId); return $this->boardService->find($this->userId, $boardId);
} }
@@ -85,6 +83,7 @@ class BoardController extends Controller {
/** /**
* @NoAdminRequired * @NoAdminRequired
* @RequireManagePermission
*/ */
public function update($id, $title, $color) { public function update($id, $title, $color) {
return $this->boardService->update($id, $title, $this->userId, $color); return $this->boardService->update($id, $title, $this->userId, $color);
@@ -92,21 +91,40 @@ class BoardController extends Controller {
/** /**
* @NoAdminRequired * @NoAdminRequired
* @RequireManagePermission
*/ */
public function delete($boardId) { public function delete($boardId) {
return $this->boardService->delete($this->userId, $boardId); return $this->boardService->delete($this->userId, $boardId);
} }
/**
* @NoAdminRequired
* @RequireReadPermission
*/
public function labels($boardId) { public function labels($boardId) {
return $this->boardService->labels($this->boardId); return $this->boardService->labels($this->boardId);
} }
/**
* @NoAdminRequired
* @RequireManagePermission
*/
public function addAcl($boardId, $type, $participant, $write, $invite, $manage) { public function addAcl($boardId, $type, $participant, $write, $invite, $manage) {
return $this->boardService->addAcl($boardId, $type, $participant, $write, $invite, $manage); return $this->boardService->addAcl($boardId, $type, $participant, $write, $invite, $manage);
} }
/**
* @NoAdminRequired
* @RequireManagePermission
*/
public function updateAcl($id, $permissionWrite, $permissionInvite, $permissionManage) { public function updateAcl($id, $permissionWrite, $permissionInvite, $permissionManage) {
return $this->boardService->updateAcl($id, $permissionWrite, $permissionInvite, $permissionManage); return $this->boardService->updateAcl($id, $permissionWrite, $permissionInvite, $permissionManage);
} }
/**
* @NoAdminRequired
* @RequireManagePermission
*/
public function deleteAcl($id) { public function deleteAcl($id) {
return $this->boardService->deleteAcl($id); return $this->boardService->deleteAcl($id);
} }

View File

@@ -41,42 +41,42 @@ class CardController extends Controller {
} }
/** /**
* @NoAdminRequired * @NoAdminRequired
*/ * @RequireReadPermission
public function index($cardId) {
return $this->cardService->findAll($boardId);
}
/**
* @NoAdminRequired
*/ */
public function read($cardId) { public function read($cardId) {
return $this->cardService->find($this->userId, $cardId); return $this->cardService->find($this->userId, $cardId);
} }
/** /**
* @NoAdminRequired * @NoAdminRequired
* @RequireEditPermission
*/ */
public function reorder($cardId, $stackId, $order) { public function reorder($cardId, $stackId, $order) {
return $this->cardService->reorder($cardId, $stackId, $order); return $this->cardService->reorder($cardId, $stackId, $order);
} }
/** /**
* @NoAdminRequired * @NoAdminRequired
* @RequireEditPermission
*/ */
public function rename($cardId, $title) { public function rename($cardId, $title) {
return $this->cardService->rename($cardId, $title); return $this->cardService->rename($cardId, $title);
} }
/** /**
* @NoAdminRequired * @NoAdminRequired
* @RequireEditPermission
*/ */
public function create($title, $stackId, $type, $order=999) { public function create($title, $stackId, $type, $order=999) {
return $this->cardService->create($title, $stackId, $type, $order, $this->userId); return $this->cardService->create($title, $stackId, $type, $order, $this->userId);
} }
/** /**
* @NoAdminRequired * @NoAdminRequired
* @RequireEditPermission
*/ */
public function update($id, $title, $stackId, $type, $order, $description) { public function update($id, $title, $stackId, $type, $order, $description) {
return $this->cardService->update($id, $title, $stackId, $type, $order, $description, $this->userId); return $this->cardService->update($id, $title, $stackId, $type, $order, $description, $this->userId);
} }
/** /**
* @NoAdminRequired * @NoAdminRequired
* @RequireEditPermission
*/ */
public function delete($cardId) { public function delete($cardId) {
return $this->cardService->delete($this->userId, $cardId); return $this->cardService->delete($this->userId, $cardId);
@@ -84,24 +84,28 @@ class CardController extends Controller {
/** /**
* @NoAdminRequired * @NoAdminRequired
* @RequireEditPermission
*/ */
public function archive($cardId) { public function archive($cardId) {
return $this->cardService->archive($cardId); return $this->cardService->archive($cardId);
} }
/** /**
* @NoAdminRequired * @NoAdminRequired
* @RequireEditPermission
*/ */
public function unarchive($cardId) { public function unarchive($cardId) {
return $this->cardService->unarchive($cardId); return $this->cardService->unarchive($cardId);
} }
/** /**
* @NoAdminRequired * @NoAdminRequired
* @RequireEditPermission
*/ */
public function assignLabel($cardId, $labelId) { public function assignLabel($cardId, $labelId) {
return $this->cardService->assignLabel($this->userId, $cardId, $labelId); return $this->cardService->assignLabel($this->userId, $cardId, $labelId);
} }
/** /**
* @NoAdminRequired * @NoAdminRequired
* @RequireEditPermission
*/ */
public function removeLabel($cardId, $labelId) { public function removeLabel($cardId, $labelId) {
return $this->cardService->removeLabel($this->userId, $cardId, $labelId); return $this->cardService->removeLabel($this->userId, $cardId, $labelId);

View File

@@ -44,18 +44,21 @@ class LabelController extends Controller {
/** /**
* @NoAdminRequired * @NoAdminRequired
* @RequireManagePermission
*/ */
public function create($title, $color, $boardId) { public function create($title, $color, $boardId) {
return $this->labelService->create($title, $this->userId, $color, $boardId); return $this->labelService->create($title, $this->userId, $color, $boardId);
} }
/** /**
* @NoAdminRequired * @NoAdminRequired
* @RequireManagePermission
*/ */
public function update($id, $title, $color) { public function update($id, $title, $color) {
return $this->labelService->update($id, $title, $this->userId, $color); return $this->labelService->update($id, $title, $this->userId, $color);
} }
/** /**
* @NoAdminRequired * @NoAdminRequired
* @RequireManagePermission
*/ */
public function delete($labelId) { public function delete($labelId) {
return $this->labelService->delete($this->userId, $labelId); return $this->labelService->delete($this->userId, $labelId);

View File

@@ -47,8 +47,6 @@ class ShareController extends Controller {
} }
/** /**
* FIXME: REMOVE, just for testing
* @NoCSRFRequired
* @NoAdminRequired * @NoAdminRequired
*/ */
public function searchUser($search) { public function searchUser($search) {

View File

@@ -43,36 +43,42 @@ class StackController extends Controller {
} }
/** /**
* @NoAdminRequired * @NoAdminRequired
* @RequireReadPermission
*/ */
public function index($boardId) { public function index($boardId) {
return $this->stackService->findAll($boardId); return $this->stackService->findAll($boardId);
} }
/** /**
* @NoAdminRequired * @NoAdminRequired
* @RequireReadPermission
*/ */
public function archived($boardId) { public function archived($boardId) {
return $this->stackService->findAllArchived($boardId); return $this->stackService->findAllArchived($boardId);
} }
/** /**
* @NoAdminRequired * @NoAdminRequired
* @RequireReadPermission
*/ */
public function read($boardId) { public function read($boardId) {
return $this->stackService->find($this->userId, $boardId); return $this->stackService->find($this->userId, $boardId);
} }
/** /**
* @NoAdminRequired * @NoAdminRequired
* @RequireManagePermission
*/ */
public function create($title, $boardId, $order=999) { public function create($title, $boardId, $order=999) {
return $this->stackService->create($title, $boardId, $order); return $this->stackService->create($title, $boardId, $order);
} }
/** /**
* @NoAdminRequired * @NoAdminRequired
* @RequireManagePermission
*/ */
public function update($id, $title, $boardId, $order) { public function update($id, $title, $boardId, $order) {
return $this->stackService->update($id, $title, $boardId, $order); return $this->stackService->update($id, $title, $boardId, $order);
} }
/** /**
* @NoAdminRequired * @NoAdminRequired
* @RequireManagePermission
*/ */
public function delete($stackId) { public function delete($stackId) {
return $this->stackService->delete($this->userId, $stackId); return $this->stackService->delete($this->userId, $stackId);

View File

@@ -23,30 +23,48 @@
namespace OCA\Deck\Db; namespace OCA\Deck\Db;
use OCA\Deck\NoPermissionException;
use OCP\AppFramework\Db\Entity; use OCP\AppFramework\Db\Entity;
use OCP\IDb; use OCP\IDb;
use OCP\AppFramework\Db\Mapper; use OCP\AppFramework\Db\Mapper;
class AclMapper extends DeckMapper { class AclMapper extends DeckMapper implements IPermissionMapper {
public function __construct(IDb $db) { public function __construct(IDb $db) {
parent::__construct($db, 'deck_board_acl', '\OCA\Deck\Db\Acl'); parent::__construct($db, 'deck_board_acl', '\OCA\Deck\Db\Acl');
} }
public function findAll($boardId, $limit=null, $offset=null) { public function findAll($boardId, $limit=null, $offset=null) {
$sql = 'SELECT id, board_id, type, participant, permission_write, permission_invite, permission_manage, 0 as owner FROM `*PREFIX*deck_board_acl` WHERE `board_id` = ? UNION SELECT 0, id, \'user\', owner, 1, 1, 1, 1 FROM `*PREFIX*deck_boards` WHERE `id` = ? '; $sql = 'SELECT id, board_id, type, participant, permission_write, permission_invite, permission_manage, 0 as owner FROM `*PREFIX*deck_board_acl` WHERE `board_id` = ? ' .
'UNION SELECT 0, id, \'user\', owner, 1, 1, 1, 1 FROM `*PREFIX*deck_boards` WHERE `id` = ? ';
return $this->findEntities($sql, [$boardId, $boardId], $limit, $offset); return $this->findEntities($sql, [$boardId, $boardId], $limit, $offset);
} }
public function findAllShared($boardId) {
$sql = 'SELECT id, board_id, type, participant, permission_write, permission_invite, permission_manage FROM `*PREFIX*deck_board_acl` WHERE `board_id` = ? ';
return $this->findEntities($sql, [$boardId]);
}
public function findAllForCard($cardId, $userId) { public function findAllForCard($cardId, $userId) {
$findBoardId = "(SELECT board_id from oc_deck_stacks WHERE id IN (SELECT stack_id from oc_deck_cards WHERE id = 15))"; $findBoardId = "(SELECT board_id from oc_deck_stacks WHERE id IN (SELECT stack_id from oc_deck_cards WHERE id = 15))";
$sql = "SELECT 0, id, 'user', owner, 1, 1, 1, 1 as owner FROM `oc_deck_boards` WHERE `id` IN (SELECT board_id from oc_deck_stacks WHERE id IN (SELECT stack_id from oc_deck_cards WHERE id = 15)) $sql = "SELECT 0, id, 'user', owner, 1, 1, 1, 1 as owner FROM `oc_deck_boards` " .
"WHERE `id` IN (SELECT board_id from oc_deck_stacks WHERE id IN (SELECT stack_id from oc_deck_cards WHERE id = 15))
UNION UNION
SELECT id, board_id, type, participant, permission_write, permission_invite, permission_manage, 0 FROM oc_deck_board_acl SELECT id, board_id, type, participant, permission_write, permission_invite, permission_manage, 0 FROM oc_deck_board_acl
WHERE participant = 'admin' AND board_id IN (SELECT board_id from oc_deck_stacks WHERE id IN (SELECT stack_id from oc_deck_cards WHERE id = 15));"; WHERE participant = 'admin' AND board_id IN (SELECT board_id from oc_deck_stacks WHERE id IN (SELECT stack_id from oc_deck_cards WHERE id = 15));";
} }
public function isOwner($userId, $aclId) {
$sql = 'SELECT * FROM `*PREFIX*deck_boards` WHERE `id` IN (SELECT board_id FROM `*PREFIX*deck_board_acl` WHERE id = ?)';
$stmt = $this->execute($sql, [$aclId]);
$row = $stmt->fetch();
return ($row['owner'] === $userId);
}
public function findBoardId($aclId) {
$entity = $this->find($aclId);
return $entity->getBoardId();
}
} }

View File

@@ -28,7 +28,7 @@ use OCP\AppFramework\Db\Mapper;
use Symfony\Component\Config\Definition\Exception\Exception; use Symfony\Component\Config\Definition\Exception\Exception;
class BoardMapper extends Mapper { class BoardMapper extends Mapper implements IPermissionMapper {
private $labelMapper; private $labelMapper;
private $_relationMappers = array(); private $_relationMappers = array();
@@ -136,4 +136,14 @@ class BoardMapper extends Mapper {
} }
public function isOwner($userId, $boardId) {
$board = $this->find($boardId);
return ($board->getOwner() === $userId);
}
public function findBoardId($id) {
return $id;
}
} }

View File

@@ -29,7 +29,7 @@ use OCP\AppFramework\Db\Mapper;
class CardMapper extends Mapper { class CardMapper extends Mapper implements IPermissionMapper {
private $labelMapper; private $labelMapper;
@@ -79,7 +79,6 @@ class CardMapper extends Mapper {
return $entities; return $entities;
} }
// TODO: test
public function findAllArchived($stackId, $limit=null, $offset=null) { public function findAllArchived($stackId, $limit=null, $offset=null) {
$sql = 'SELECT * FROM `*PREFIX*deck_cards` WHERE `stack_id`=? AND archived ORDER BY `last_modified`'; $sql = 'SELECT * FROM `*PREFIX*deck_cards` WHERE `stack_id`=? AND archived ORDER BY `last_modified`';
$entities = $this->findEntities($sql, [$stackId], $limit, $offset); $entities = $this->findEntities($sql, [$stackId], $limit, $offset);
@@ -107,5 +106,19 @@ class CardMapper extends Mapper {
$stmt->execute(); $stmt->execute();
} }
public function isOwner($userId, $cardId) {
$sql = 'SELECT * FROM `*PREFIX*deck_boards` WHERE `id` IN (SELECT board_id FROM `*PREFIX*deck_stacks` WHERE id IN (SELECT stack_id FROM `*PREFIX*deck_cards` WHERE id = ?))';
$stmt = $this->execute($sql, [$cardId]);
$row = $stmt->fetch();
return ($row['owner'] === $userId);
}
public function findBoardId($cardId) {
$sql = 'SELECT * FROM `*PREFIX*deck_boards` WHERE `id` IN (SELECT board_id FROM `*PREFIX*deck_stacks` WHERE id IN (SELECT stack_id FROM `*PREFIX*deck_cards` WHERE id = ?))';
$stmt = $this->execute($sql, [$cardId]);
$row = $stmt->fetch();
return $row['id'];
}
} }

View File

@@ -23,7 +23,6 @@
namespace OCA\Deck\Db; namespace OCA\Deck\Db;
use OCP\AppFramework\Db\Entity;
use OCP\AppFramework\Db\Mapper; use OCP\AppFramework\Db\Mapper;
abstract class DeckMapper extends Mapper { abstract class DeckMapper extends Mapper {

View File

@@ -21,26 +21,21 @@
* *
*/ */
namespace OCA\Deck\Controller;
use OCP\IRequest;
use OCP\AppFramework\ApiController as BaseApiController;
class StyleController extends Controller {
private $defaults;
public function __construct($appName,
IRequest $request, OC_Defaults $defaults){
parent::__construct($appName, $request);
$this->defaults = $defaults;
}
/** /**
* @PublicPage * Created by PhpStorm.
* @NoCSRFRequired * User: jus
* Date: 19.08.16
* Time: 22:25
*/ */
public function generateCss() {
$color = $this->config->getAppValue($this->appName, 'color'); namespace OCA\Deck\Db;
$responseCss .= '';
$response = new Http\DataDownloadResponse($responseCss, 'style', 'text/css');
} interface IPermissionMapper {
public function isOwner($userId, $id);
public function findBoardId($id);
} }

View File

@@ -28,7 +28,7 @@ use OCP\IDb;
use OCP\AppFramework\Db\Mapper; use OCP\AppFramework\Db\Mapper;
class LabelMapper extends DeckMapper { class LabelMapper extends DeckMapper implements IPermissionMapper {
public function __construct(IDb $db) { public function __construct(IDb $db) {
parent::__construct($db, 'deck_labels', '\OCA\Deck\Db\Label'); parent::__construct($db, 'deck_labels', '\OCA\Deck\Db\Label');
@@ -66,4 +66,16 @@ class LabelMapper extends DeckMapper {
} }
return $result; return $result;
} }
public function isOwner($userId, $labelId) {
$sql = 'SELECT * FROM `*PREFIX*deck_boards` WHERE `id` IN (SELECT board_id FROM `*PREFIX*deck_labels` WHERE id = ?)';
$stmt = $this->execute($sql, [$labelId]);
$row = $stmt->fetch();
return ($row['owner'] === $userId);
}
public function findBoardId($labelId) {
$entity = $this->find($labelId);
return $entity->getBoardId();
}
} }

View File

@@ -28,7 +28,7 @@ use OCP\IDb;
use OCP\AppFramework\Db\Mapper; use OCP\AppFramework\Db\Mapper;
class StackMapper extends Mapper { class StackMapper extends Mapper implements IPermissionMapper {
private $cardMapper; private $cardMapper;
@@ -59,4 +59,16 @@ class StackMapper extends Mapper {
// FIXME: delete linked elements, because owncloud doesn't support foreign keys for apps // FIXME: delete linked elements, because owncloud doesn't support foreign keys for apps
return parent::delete($entity); return parent::delete($entity);
} }
public function isOwner($userId, $stackId) {
$sql = 'SELECT * FROM `*PREFIX*deck_boards` WHERE `id` IN (SELECT board_id FROM `*PREFIX*deck_stacks` WHERE id = ?)';
$stmt = $this->execute($sql, [$stackId]);
$row = $stmt->fetch();
return ($row['owner'] === $userId);
}
public function findBoardId($stackId) {
$entity = $this->find($stackId);
return $entity->getBoardId();
}
} }

View File

@@ -23,31 +23,260 @@
namespace OCA\Deck\Middleware; namespace OCA\Deck\Middleware;
use OC\OCS\Exception;
use OCA\Deck\Controller\BoardController;
use OCA\Deck\Controller\CardController;
use OCA\Deck\Controller\LabelController;
use OCA\Deck\Controller\PageController;
use OCA\Deck\Controller\ShareController;
use OCA\Deck\Db\CardMapper;
use OCA\Deck\NoPermissionException;
use OCA\Deck\Service\ServicePermissionException;
use OCA\Files_Versions\Expiration;
use \OCP\AppFramework\Middleware; use \OCP\AppFramework\Middleware;
use OCP\IContainer;
use OCP\IDBConnection; use OCP\IDBConnection;
use OCP\IGroupManager; use OCP\IGroupManager;
use OCP\IRequest;
use OCP\IUserManager; use OCP\IUserManager;
use OCA\Deck\Controller\StackController;
use OCP\IUserSession;
use OCP\AppFramework\Http\JSONResponse;
use OC\AppFramework\Utility\ControllerMethodReflector;
class SharingMiddleware extends Middleware { class SharingMiddleware extends Middleware {
private $container;
private $request;
private $userSession;
private $reflector;
private $groupManager;
public function __construct( public function __construct(
IUserManager $userManager, IContainer $container,
IGroupManager $groupManager, IRequest $request,
IDBConnection $db, IUserSession $userSession,
$userId ControllerMethodReflector $reflector
) { ) {
$this->userId = $userId; $this->container = $container;
$this->db = $db; $this->request = $request;
$this->userManager = $userManager; $this->userSession = $userSession;
$this->groupManager = $groupManager; $this->reflector = $reflector;
$this->aclMapper = $this->container->query('OCA\Deck\Db\AclMapper');
$this->groupManager = $this->container->query('\OCP\IGroupManager');
} }
/**
* All permission checks for controller access
*
* The following method annotaitons are possible
* - RequireReadPermission
* - RequireEditPermission
* - RequireSharePermission
* - RequireManagePermission
*
* Depending on the Controller class we call a corresponding mapper to find the board_id
* With the board_id we can check for ownership/permissions in the acl table
*
* @param \OCP\AppFramework\Controller $controller
* @param string $methodName
* @throws NoPermissionException
*/
public function beforeController($controller, $methodName) { public function beforeController($controller, $methodName) {
\OCP\Util::writeLog('deck', "", \OCP\Util::ERROR); $this->start = microtime(true);
//$userBoards = $this->boardMapper->findAllByUser($userInfo['user']); $userId = null;
//$groupBoards = $this->boardMapper->findAllByGroups($userInfo['user'], $userInfo['groups']); if($this->userSession->getUser()) {
$userId = $this->userSession->getUser()->getUID();
}
$method = $this->request->getMethod();
$params = $this->request->getParams();
$this->checkPermissions($userId, $controller, $method, $params);
// TODO: remove, just for testing
\OCP\Util::writeLog('deck', (microtime(true)-$this->start), \OCP\Util::ERROR);
} }
private function checkPermissions($userId, $controller, $method, $params) {
// no permission checks needed for plain html page
if($controller instanceof PageController) {
return;
}
// FIXME: ShareController#search should be limited to board users/groups
if($controller instanceof BoardController or $controller instanceof ShareController) {
$mapper = $this->container->query('OCA\Deck\Db\BoardMapper');
$id = $params['boardId'];
}
if($controller instanceof StackController) {
if($method==="GET" || $method === "POST") {
$mapper = $this->container->query('OCA\Deck\Db\BoardMapper');
$id = $params['boardId'];
} else {
$mapper = $this->container->query('OCA\Deck\Db\StackMapper');
$id = $params['stackId'];
}
}
if($controller instanceof CardController) {
if($method==="GET" || $method === "POST") {
$mapper = $this->container->query('OCA\Deck\Db\StackMapper');
$id = $params['stackId'];
} else {
$mapper = $this->container->query('OCA\Deck\Db\CardMapper');
$id = $params['cardId'];
}
}
if($controller instanceof LabelController) {
if($method==="GET" || $method === "POST") {
$mapper = $this->container->query('OCA\Deck\Db\BoardMapper');
$id = $params['boardId'];
} else {
$mapper = $this->container->query('OCA\Deck\Db\LabelMapper');
$id = $params['labelId'];
}
}
if($this->reflector->hasAnnotation('RequireReadPermission')) {
if(!$this->checkReadPermission($userId, $mapper, $id)) {
throw new NoPermissionException("User ". $userId . " has no permission to read.");
}
}
if($this->reflector->hasAnnotation('RequireEditPermission')) {
if(!$this->checkEditPermission($userId, $mapper, $id)) {
throw new NoPermissionException("User ". $userId . " has no permission to edit.");
}
}
if($this->reflector->hasAnnotation('RequireSharePermission')) {
if(!$this->checkSharePermission($userId, $mapper, $id)) {
throw new NoPermissionException("User ". $userId . " has no permission to share.");
}
}
if($this->reflector->hasAnnotation('RequireManagePermission')) {
if(!$this->checkManagePermission($userId, $mapper, $id)) {
throw new NoPermissionException("User ". $userId . " has no permission to manage.");
}
}
// FIXME: Default should be nopermisison mybe add norequirepermission for index
}
/* TODO: Priorize groups with higher permissions */
public function checkReadPermission($userId, $mapper, $id) {
// is owner
if($mapper->isOwner($userId, $id)) {
return true;
}
$boardId = $mapper->findBoardId($id);
$acls = $this->aclMapper->findAllShared($boardId);
// check for users
foreach ($acls as $acl) {
if($acl->getType() === "user" && $acl->getParticipant() === $userId) {
return true;
}
}
// check for groups
foreach ($acls as $acl) {
if($acl->getType() === "user" && $this->groupManager->isInGroup($userId, $acl->getParticipant())) {
return true;
}
}
throw new NoPermissionException("User ". $userId . " has no permission to read.");
}
public function checkEditPermission($userId, $mapper, $id) {
// is owner
if($mapper->isOwner($userId, $id)) {
return true;
}
// check if is in acl
$boardId = $mapper->findBoardId($id);
$acls = $this->aclMapper->findAllShared($boardId);
// check for users
foreach ($acls as $acl) {
if($acl->getType() === "user" && $acl->getParticipant() === $userId) {
return $acl->getPermissionWrite();
}
}
// check for groups
foreach ($acls as $acl) {
if($acl->getType() === "user" && $this->groupManager->isInGroup($userId, $acl->getParticipant())) {
return $acl->getPermissionWrite();
}
}
throw new NoPermissionException("User ". $userId . " has no permission to edit.");
}
public function checkManagePermission($userId, $mapper, $id) {
// is owner
if($mapper->isOwner($userId, $id)) {
return true;
}
// check if is in acl
$boardId = $mapper->findBoardId($id);
$acls = $this->aclMapper->findAllShared($boardId);
// check for users
foreach ($acls as $acl) {
if($acl->getType() === "user" && $acl->getParticipant() === $userId) {
return $acl->getPermissionManage();
}
}
// check for groups
foreach ($acls as $acl) {
if($acl->getType() === "user" && $this->groupManager->isInGroup($userId, $acl->getParticipant())) {
return $acl->getPermissionManage();
}
}
throw new NoPermissionException();
}
public function checkSharePermission($userId, $mapper, $id) {
// is owner
if($mapper->isOwner($userId, $id)) {
return true;
}
// check if is in acl
$boardId = $mapper->findBoardId($id);
$acls = $this->aclMapper->findAllShared($boardId);
// check for users
foreach ($acls as $acl) {
if($acl->getType() === "user" && $acl->getParticipant() === $userId) {
return $acl->getPermissionInvite();
}
}
// check for groups
foreach ($acls as $acl) {
if($acl->getType() === "user" && $this->groupManager->isInGroup($userId, $acl->getParticipant())) {
return $acl->getPermissionInvite();
}
}
throw new NoPermissionException();
}
public function afterException($controller, $methodName, \Exception $exception) {
\OCP\Util::writeLog('deck', (microtime(true)-$this->start), \OCP\Util::ERROR);
if(is_a($exception, '\OCA\Deck\NoPermissionException')) {
return new JSONResponse([
"status" => 401,
"message" => $exception->getMessage()
], 401);
}
throw $exception;
}
} }

View File

@@ -0,0 +1,29 @@
<?php
/**
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.net>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Deck;
class NoPermissionException extends \Exception {
}

View File

@@ -66,13 +66,7 @@ class BoardService {
public function find($userId, $boardId) { public function find($userId, $boardId) {
$board = $this->boardMapper->find($boardId); $board = $this->boardMapper->find($boardId);
if ($board->getOwner() === $userId)
return $board; return $board;
else
return null;
// FIXME: [share] Check for user permissions
} }
public function create($title, $userId, $color) { public function create($title, $userId, $color) {

View File

@@ -51,7 +51,6 @@ class LabelService {
public function find($userId, $labelId) { public function find($userId, $labelId) {
$label = $this->labelMapper->find($labelId); $label = $this->labelMapper->find($labelId);
// FIXME: [share] Check for user permissions
return $label; return $label;
} }

View File

@@ -9,9 +9,9 @@
<h1> <h1>
{{ boardservice.data[id].title }} {{ boardservice.data[id].title }}
<div id="board-actions"> <div id="board-actions">
<div class="board-action-button" ng-if="filter!='archive'"><a ng-click="switchFilter('archive')" style="opacity:0.5;"><img src="/apps/deck/img/archive-white.svg" /></a></div> <div class="board-action-button" ng-if="filter!='archive'"><a ng-click="switchFilter('archive')" style="opacity:0.5;"><i class="icon icon-archive-white"></i></a></div>
<div class="board-action-button" ng-if="filter=='archive'"><a ng-click="switchFilter('')"><img src="/apps/deck/img/archive-white.svg" /></a></a></div> <div class="board-action-button" ng-if="filter=='archive'"><a ng-click="switchFilter('')"><i class="icon icon-archive-white"></i></a></div>
<div class="board-action-button"><a ui-sref="board.detail({ id: id })"><img src="/apps/deck/img/details-white.svg" /></a> <div class="board-action-button"><a ui-sref="board.detail({ id: id })"><i class="icon icon-details"></i></a>
</div> </div>
</div> </div>
</h1> </h1>