From 9576e7f0e7fee64a32975a82606993a1a78c1991 Mon Sep 17 00:00:00 2001 From: dartcafe Date: Mon, 27 Jan 2020 07:40:36 +0100 Subject: Extend acl no access to deleted polls via public link #773 --- lib/Model/Acl.php | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/Model/Acl.php b/lib/Model/Acl.php index 949bbbef..31362b63 100644 --- a/lib/Model/Acl.php +++ b/lib/Model/Acl.php @@ -177,10 +177,11 @@ class Acl implements JsonSerializable { public function getAllowView(): bool { return ( $this->getIsOwner() - || $this->getIsAdmin() + || ($this->getIsAdmin() && $this->poll->getAdminAccess()) || ($this->getGroupShare() && !$this->poll->getDeleted()) || ($this->getPersonalShare() && !$this->poll->getDeleted()) - || $this->poll->getAccess() !== 'hidden' + || ($this->getPublicShare() && !$this->poll->getDeleted()) + || ($this->poll->getAccess() !== 'hidden' && !$this->getPublicShare()) ); } @@ -213,6 +214,21 @@ class Acl implements JsonSerializable { ); } + /** + * @NoAdminRequired + * @return bool + */ + public function getPublicShare(): bool { + + return count( + array_filter($this->shareMapper->findByPoll($this->getPollId()), function($item) { + if ($item->getType() === 'public' && $item->getToken() === $this->getToken()) { + return true; + } + }) + ); + } + /** * @NoAdminRequired * @return bool @@ -233,6 +249,7 @@ class Acl implements JsonSerializable { ($this->getAllowView() || $this->getFoundByToken()) && !$this->getExpired() && !$this->poll->getDeleted() + && $this->userId ) { return true; @@ -368,6 +385,7 @@ class Acl implements JsonSerializable { 'allowSeeAllVotes' => $this->getAllowSeeAllVotes(), 'groupShare' => $this->getGroupShare(), 'personalShare' => $this->getPersonalShare(), + 'publicShare' => $this->getPublicShare(), 'foundByToken' => $this->getFoundByToken(), 'accessLevel' => $this->getAccessLevel() ]; -- cgit v1.2.3 From 849c52bc55ae243e0cd366b0385043ca895d14e6 Mon Sep 17 00:00:00 2001 From: dartcafe Date: Mon, 27 Jan 2020 07:40:47 +0100 Subject: Display error for not found or no access #772 --- appinfo/routes.php | 1 + lib/Controller/PollController.php | 6 ++++-- src/js/router.js | 8 +++++++ src/js/store/modules/poll.js | 6 ++++-- src/js/views/NotFound.vue | 44 +++++++++++++++++++++++++++++++++++++++ src/js/views/Vote.vue | 21 ++++++++++++------- 6 files changed, 74 insertions(+), 12 deletions(-) create mode 100644 src/js/views/NotFound.vue diff --git a/appinfo/routes.php b/appinfo/routes.php index 47b5c6d9..f6533215 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -24,6 +24,7 @@ return [ 'routes' => [ ['name' => 'page#index', 'url' => '/', 'verb' => 'GET'], + ['name' => 'page#index', 'url' => '/not-found', 'verb' => 'GET', 'postfix' => 'notfound'], ['name' => 'page#index', 'url' => '/list/{id}', 'verb' => 'GET', 'postfix' => 'list'], ['name' => 'page#index', 'url' => '/vote/{id}', 'verb' => 'GET', 'postfix' => 'vote'], ['name' => 'page#vote_public', 'url' => '/s/{token}', 'verb' => 'GET', 'postfix' => 'public'], diff --git a/lib/Controller/PollController.php b/lib/Controller/PollController.php index c3805ecf..1d42620f 100644 --- a/lib/Controller/PollController.php +++ b/lib/Controller/PollController.php @@ -139,7 +139,9 @@ class PollController extends Controller { $this->acl->setPollId($pollId); } $this->poll = $this->pollMapper->find($pollId); - + if (!$this->acl->getAllowView()) { + return new DataResponse(null, Http::STATUS_UNAUTHORIZED); + } return new DataResponse([ 'poll' => $this->poll, 'acl' => $this->acl @@ -147,7 +149,7 @@ class PollController extends Controller { } catch (DoesNotExistException $e) { $this->logger->info('Poll ' . $pollId . ' not found!', ['app' => 'polls']); - return new DataResponse($e, Http::STATUS_NOT_FOUND); + return new DataResponse(null, Http::STATUS_NOT_FOUND); } } diff --git a/src/js/router.js b/src/js/router.js index d11cbc71..9547c76e 100644 --- a/src/js/router.js +++ b/src/js/router.js @@ -30,6 +30,7 @@ import { generateUrl } from '@nextcloud/router' const List = () => import('./views/PollList') const Vote = () => import('./views/Vote') const PublicVote = () => import('./views/PublicVote') +const NotFound = () => import('./views/NotFound') Vue.use(Router) @@ -55,6 +56,13 @@ export default new Router({ props: true, name: 'list' }, + { + path: '/not-found', + components: { + default: NotFound + }, + name: 'notfound' + }, { path: '/vote/:id', components: { diff --git a/src/js/store/modules/poll.js b/src/js/store/modules/poll.js index 384738f4..65edae5c 100644 --- a/src/js/store/modules/poll.js +++ b/src/js/store/modules/poll.js @@ -100,9 +100,11 @@ const actions = { .then((response) => { context.commit('setPoll', { poll: response.data.poll }) context.commit('acl/setAcl', { acl: response.data.acl }) + return response }, (error) => { - if (error.response.status !== '404') { - console.error('Error loading poll', { error: error.response }, { payload: payload }) + if (error.response.status !== '404' && error.response.status !== '401') { + console.debug('Error loading poll', { error: error.response }, { payload: payload }) + return error.response } throw error }) diff --git a/src/js/views/NotFound.vue b/src/js/views/NotFound.vue new file mode 100644 index 00000000..60a4d899 --- /dev/null +++ b/src/js/views/NotFound.vue @@ -0,0 +1,44 @@ + + + + + diff --git a/src/js/views/Vote.vue b/src/js/views/Vote.vue index 6c98e636..6968488b 100644 --- a/src/js/views/Vote.vue +++ b/src/js/views/Vote.vue @@ -131,18 +131,23 @@ export default { loadPoll() { this.isLoading = true this.$store.dispatch({ type: 'loadPollMain', pollId: this.$route.params.id }) - .then(() => { - this.$store.dispatch({ type: 'loadPoll', pollId: this.$route.params.id }) - .then(() => { - if (this.acl.allowEdit && moment.unix(this.poll.created).diff() > -10000) { - this.openConfiguration() - } - this.isLoading = false - }) + .then((response) => { + if (response.status === 200) { + this.$store.dispatch({ type: 'loadPoll', pollId: this.$route.params.id }) + .then(() => { + if (this.acl.allowEdit && moment.unix(this.poll.created).diff() > -10000) { + this.openConfiguration() + } + this.isLoading = false + }) + } else { + this.$router.replace({ name: 'notfound' }) + } }) .catch((error) => { console.error(error) this.isLoading = false + this.$router.replace({ name: 'notfound' }) }) }, -- cgit v1.2.3 From e63c9423dabfd90fa01edec1e5cb03dd248186b1 Mon Sep 17 00:00:00 2001 From: dartcafe Date: Tue, 28 Jan 2020 16:09:24 +0100 Subject: Consolidate PublicVote and show navigation if user is logged in --- lib/Controller/PageController.php | 10 ++++++++-- src/js/App.vue | 21 ++------------------- src/js/router.js | 3 +-- src/js/views/Vote.vue | 17 +++++++++++------ 4 files changed, 22 insertions(+), 29 deletions(-) diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index 82932a44..71e0a7cd 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -71,8 +71,14 @@ class PageController extends Controller { * @return PublicTemplateResponse */ public function votePublic() { - return new PublicTemplateResponse('polls', 'polls.tmpl', [ - 'urlGenerator' => $this->urlGenerator]); + if (\OC::$server->getUserSession()->isLoggedIn()) { + return new TemplateResponse('polls', 'polls.tmpl', [ + 'urlGenerator' => $this->urlGenerator]); + } else { + return new PublicTemplateResponse('polls', 'polls.tmpl', [ + 'urlGenerator' => $this->urlGenerator]); + } + } } diff --git a/src/js/App.vue b/src/js/App.vue index ec588070..cca48a0e 100644 --- a/src/js/App.vue +++ b/src/js/App.vue @@ -22,7 +22,7 @@ @@ -34,25 +34,8 @@ export default { name: 'App', components: { Navigation - }, - - data() { - return { - loadNavigation: false - } - }, - - computed: { - isPublic() { - return (this.$route.name === 'publicVote') - } - }, - - watch: { - $route() { - this.loadNavigation = (this.$route.name !== 'publicVote') - } } + } diff --git a/src/js/router.js b/src/js/router.js index 9547c76e..de609ba8 100644 --- a/src/js/router.js +++ b/src/js/router.js @@ -29,7 +29,6 @@ import { generateUrl } from '@nextcloud/router' // Dynamic loading const List = () => import('./views/PollList') const Vote = () => import('./views/Vote') -const PublicVote = () => import('./views/PublicVote') const NotFound = () => import('./views/NotFound') Vue.use(Router) @@ -78,7 +77,7 @@ export default new Router({ { path: '/s/:token', components: { - default: PublicVote + default: Vote }, props: true, name: 'publicVote' diff --git a/src/js/views/Vote.vue b/src/js/views/Vote.vue index 6968488b..8cd15a92 100644 --- a/src/js/views/Vote.vue +++ b/src/js/views/Vote.vue @@ -32,17 +32,21 @@ +
- +
+ {{ t('polls', 'There are no vote options. Maybe the owner did not provide some until now.') }} +
- +
@@ -61,6 +65,7 @@ import PollDescription from '../components/Base/PollDescription' import PollInformation from '../components/Base/PollInformation' import PollTitle from '../components/Base/PollTitle' import LoadingOverlay from '../components/Base/LoadingOverlay' +import VoteHeaderPublic from '../components/VoteTable/VoteHeaderPublic' import SideBar from '../components/SideBar/SideBar' import VoteList from '../components/VoteTable/VoteList' import VoteTable from '../components/VoteTable/VoteTable' @@ -76,6 +81,7 @@ export default { PollInformation, PollTitle, LoadingOverlay, + VoteHeaderPublic, SideBar, VoteTable, VoteList @@ -85,10 +91,9 @@ export default { return { voteSaved: false, delay: 50, - sideBarOpen: false, + sideBarOpen: true, isLoading: false, initialTab: 'comments', - newName: '', tableMode: true, activeTab: t('polls', 'Comments').toLowerCase() } @@ -130,10 +135,10 @@ export default { loadPoll() { this.isLoading = true - this.$store.dispatch({ type: 'loadPollMain', pollId: this.$route.params.id }) + this.$store.dispatch({ type: 'loadPollMain', pollId: this.$route.params.id, token: this.$route.params.token }) .then((response) => { if (response.status === 200) { - this.$store.dispatch({ type: 'loadPoll', pollId: this.$route.params.id }) + this.$store.dispatch({ type: 'loadPoll', pollId: this.$route.params.id, token: this.$route.params.token }) .then(() => { if (this.acl.allowEdit && moment.unix(this.poll.created).diff() > -10000) { this.openConfiguration() -- cgit v1.2.3 From b7bd95813f28e26e224b9170279065fbee6d96be Mon Sep 17 00:00:00 2001 From: dartcafe Date: Tue, 28 Jan 2020 16:10:19 +0100 Subject: Give access to user, if he participated in poll --- lib/Db/VoteMapper.php | 20 ++++++++++++++++++++ lib/Model/Acl.php | 18 ++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/lib/Db/VoteMapper.php b/lib/Db/VoteMapper.php index 2e759d8c..244eaf26 100644 --- a/lib/Db/VoteMapper.php +++ b/lib/Db/VoteMapper.php @@ -96,6 +96,26 @@ class VoteMapper extends QBMapper { return $this->findEntities($qb); } + /** + * @param int $pollId + * @throws \OCP\AppFramework\Db\DoesNotExistException if not found + * @return array + */ + public function findParticipantsVotes($pollId, $userId) { + $qb = $this->db->getQueryBuilder(); + + $qb->select('*') + ->from($this->getTableName()) + ->where( + $qb->expr()->eq('poll_id', $qb->createNamedParameter($pollId, IQueryBuilder::PARAM_INT)) + ) + ->andWhere( + $qb->expr()->eq('user_id', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)) + ); + + return $this->findEntities($qb); + } + /** * @param int $pollId * @param string $userId diff --git a/lib/Model/Acl.php b/lib/Model/Acl.php index 31362b63..de1794e3 100644 --- a/lib/Model/Acl.php +++ b/lib/Model/Acl.php @@ -33,6 +33,7 @@ use OCP\ILogger; use OCA\Polls\Db\Poll; use OCA\Polls\Db\Share; use OCA\Polls\Db\PollMapper; +use OCA\Polls\Db\VoteMapper; use OCA\Polls\Db\ShareMapper; /** @@ -80,6 +81,7 @@ class Acl implements JsonSerializable { * @param ILogger $logger * @param IGroupManager $groupManager * @param PollMapper $pollMapper + * @param VoteMapper $voteMapper * @param ShareMapper $shareMapper * @param Poll $pollMapper * @@ -89,6 +91,7 @@ class Acl implements JsonSerializable { ILogger $logger, IGroupManager $groupManager, PollMapper $pollMapper, + VoteMapper $voteMapper, ShareMapper $shareMapper, Poll $poll ) { @@ -96,6 +99,7 @@ class Acl implements JsonSerializable { $this->logger = $logger; $this->groupManager = $groupManager; $this->pollMapper = $pollMapper; + $this->voteMapper = $voteMapper; $this->shareMapper = $shareMapper; $this->poll = $poll; } @@ -177,11 +181,13 @@ class Acl implements JsonSerializable { public function getAllowView(): bool { return ( $this->getIsOwner() + || $this->getUserHasVoted() || ($this->getIsAdmin() && $this->poll->getAdminAccess()) || ($this->getGroupShare() && !$this->poll->getDeleted()) || ($this->getPersonalShare() && !$this->poll->getDeleted()) || ($this->getPublicShare() && !$this->poll->getDeleted()) || ($this->poll->getAccess() !== 'hidden' && !$this->getPublicShare()) + ); } @@ -199,6 +205,17 @@ class Acl implements JsonSerializable { ); } + /** + * @NoAdminRequired + * @return bool + */ + public function getUserHasVoted(): bool { + $this->logger->alert($this->getPollId() . ' ' . $this->getUserId() . ' ' . json_encode($this->voteMapper->findParticipantsVotes($this->getPollId(), $this->getUserId()))); + return count( + $this->voteMapper->findParticipantsVotes($this->getPollId(), $this->getUserId()) + ); + } + /** * @NoAdminRequired * @return bool @@ -383,6 +400,7 @@ class Acl implements JsonSerializable { 'allowEdit' => $this->getAllowEdit(), 'allowSeeUsernames' => $this->getAllowSeeUsernames(), 'allowSeeAllVotes' => $this->getAllowSeeAllVotes(), + 'userHasVoted' => $this->getUserHasVoted(), 'groupShare' => $this->getGroupShare(), 'personalShare' => $this->getPersonalShare(), 'publicShare' => $this->getPublicShare(), -- cgit v1.2.3 From fd27edd645d21af37fa1216a735297164403ddcd Mon Sep 17 00:00:00 2001 From: dartcafe Date: Tue, 28 Jan 2020 19:22:40 +0100 Subject: Combine poll and acl for pollList --- lib/Controller/PollController.php | 22 ++++++++++++++++++---- lib/Model/Acl.php | 1 - src/js/components/Navigation/Navigation.vue | 9 +++++++++ src/js/store/modules/polls.js | 3 +++ src/js/views/PollList.vue | 3 +++ src/js/views/PublicVote.vue | 17 +++++++++++------ 6 files changed, 44 insertions(+), 11 deletions(-) diff --git a/lib/Controller/PollController.php b/lib/Controller/PollController.php index 1d42620f..99917de3 100644 --- a/lib/Controller/PollController.php +++ b/lib/Controller/PollController.php @@ -115,14 +115,28 @@ class PollController extends Controller { public function list() { if (\OC::$server->getUserSession()->isLoggedIn()) { try { - $polls = array_values(array_filter($this->pollMapper->findAll(), function($item) { - return $this->acl->setPollId($item->getId())->getAllowView(); - })); - return new DataResponse($polls, Http::STATUS_OK); + // $polls = array_values(array_filter($this->pollMapper->findAll(), function($item) { + // return $this->acl->setPollId($item->getId())->getAllowView(); + // })); + + $polls = $this->pollMapper->findAll(); + + foreach ($polls as $poll) { + $combinedPoll = (object) array_merge( + (array) json_decode(json_encode($poll)), (array) json_decode(json_encode($this->acl->setPollId($poll->getId())))); + if ($combinedPoll->allowView) { + $pollList[] = $combinedPoll; + } + } + + return new DataResponse($pollList, Http::STATUS_OK); } catch (DoesNotExistException $e) { return new DataResponse($e, Http::STATUS_NOT_FOUND); } + } else { + return new DataResponse([], Http::STATUS_OK); } + } /** diff --git a/lib/Model/Acl.php b/lib/Model/Acl.php index de1794e3..68096490 100644 --- a/lib/Model/Acl.php +++ b/lib/Model/Acl.php @@ -210,7 +210,6 @@ class Acl implements JsonSerializable { * @return bool */ public function getUserHasVoted(): bool { - $this->logger->alert($this->getPollId() . ' ' . $this->getUserId() . ' ' . json_encode($this->voteMapper->findParticipantsVotes($this->getPollId(), $this->getUserId()))); return count( $this->voteMapper->findParticipantsVotes($this->getPollId(), $this->getUserId()) ); diff --git a/src/js/components/Navigation/Navigation.vue b/src/js/components/Navigation/Navigation.vue index be07f1d3..9bd9c5af 100644 --- a/src/js/components/Navigation/Navigation.vue +++ b/src/js/components/Navigation/Navigation.vue @@ -41,6 +41,14 @@ + +
    + +
+
+
    @@ -97,6 +105,7 @@ export default { 'myPolls', 'publicPolls', 'hiddenPolls', + 'participatedPolls', 'deletedPolls' ]), diff --git a/src/js/store/modules/polls.js b/src/js/store/modules/polls.js index 9618d44d..fd91da7d 100644 --- a/src/js/store/modules/polls.js +++ b/src/js/store/modules/polls.js @@ -51,6 +51,9 @@ const getters = { }, deletedPolls: (state) => { return state.list.filter(poll => (poll.deleted)) + }, + participatedPolls: (state) => { + return state.list.filter(poll => (poll.userHasVoted)) } } diff --git a/src/js/views/PollList.vue b/src/js/views/PollList.vue index 9bba0b68..91640262 100644 --- a/src/js/views/PollList.vue +++ b/src/js/views/PollList.vue @@ -75,6 +75,7 @@ export default { 'myPolls', 'publicPolls', 'hiddenPolls', + 'participatedPolls', 'deletedPolls' ]), @@ -87,6 +88,8 @@ export default { return this.hiddenPolls } else if (this.$route.params.type === 'deleted') { return this.deletedPolls + } else if (this.$route.params.type === 'participated') { + return this.participatedPolls } else { return this.allPolls } diff --git a/src/js/views/PublicVote.vue b/src/js/views/PublicVote.vue index fcc0b448..28a040d6 100644 --- a/src/js/views/PublicVote.vue +++ b/src/js/views/PublicVote.vue @@ -32,7 +32,7 @@
- + @@ -115,15 +115,20 @@ export default { loadPoll() { this.isLoading = true this.$store.dispatch('loadPollMain', { token: this.$route.params.token }) - .then(() => { - this.$store.dispatch('loadPoll', { token: this.$route.params.token }) - .then(() => { - this.isLoading = false - }) + .then((response) => { + if (response.status === 200) { + this.$store.dispatch('loadPoll', { token: this.$route.params.token }) + .then(() => { + this.isLoading = false + }) + } else { + this.$router.replace({ name: 'notfound' }) + } }) .catch((error) => { console.error(error) this.isLoading = false + this.$router.replace({ name: 'notfound' }) }) }, -- cgit v1.2.3 From 0bd70b482a01de419c13cf4b4aca2645aab1e93a Mon Sep 17 00:00:00 2001 From: dartcafe Date: Tue, 28 Jan 2020 19:25:16 +0100 Subject: publicVote.vue isn't needed anymore --- src/js/views/PublicVote.vue | 177 -------------------------------------------- 1 file changed, 177 deletions(-) delete mode 100644 src/js/views/PublicVote.vue diff --git a/src/js/views/PublicVote.vue b/src/js/views/PublicVote.vue deleted file mode 100644 index 28a040d6..00000000 --- a/src/js/views/PublicVote.vue +++ /dev/null @@ -1,177 +0,0 @@ - - - - - - - -- cgit v1.2.3