diff --git a/lib/Db/WatchMapper.php b/lib/Db/WatchMapper.php index 85ec5df2a8..d361fb3041 100644 --- a/lib/Db/WatchMapper.php +++ b/lib/Db/WatchMapper.php @@ -8,7 +8,6 @@ namespace OCA\Polls\Db; -use OCA\Polls\AppConstants; use OCA\Polls\UserSession; use OCP\AppFramework\Db\QBMapper; use OCP\IDBConnection; @@ -42,7 +41,7 @@ public function findUpdatesForPollId(int $pollId, int $offset): array { ) ->andWhere($qb->expr()->orX( $qb->expr()->eq('poll_id', $qb->createNamedParameter($pollId)), - $qb->expr()->eq('table', $qb->createNamedParameter(AppConstants::APP_ID)) + $qb->expr()->eq('table', $qb->createNamedParameter(Watch::OBJECT_POLLS)) )); return $this->findEntities($qb); diff --git a/src/Exceptions/Exceptions.ts b/src/Exceptions/Exceptions.ts index 07aaff2b38..9046c5a6ad 100644 --- a/src/Exceptions/Exceptions.ts +++ b/src/Exceptions/Exceptions.ts @@ -16,6 +16,13 @@ class NotReady extends Error { } } +class NotAllowed extends Error { + constructor(message: string | undefined) { + super(message) + this.name = 'NotAllowed' + } +} + class InvalidJSON extends Error { constructor(message: string | undefined) { super(message) @@ -23,4 +30,4 @@ class InvalidJSON extends Error { } } -export { Exception, InvalidJSON, NotReady } +export { Exception, InvalidJSON, NotReady, NotAllowed } diff --git a/src/composables/usePollWatcher.ts b/src/composables/usePollWatcher.ts index f0bd99c22b..5e647e913b 100644 --- a/src/composables/usePollWatcher.ts +++ b/src/composables/usePollWatcher.ts @@ -26,6 +26,7 @@ import type { } from './usePollWatcher.types' import type { Watcher } from '../stores/session.types' +import { NotAllowed } from '../Exceptions/Exceptions' /** * poll watcher to keep polls collection and the current poll @@ -179,14 +180,31 @@ export const usePollWatcher = (interval = 30000) => { const handleWatcherTasks = (tasks: string[]) => { Logger.info('[PollWatcher] Tasks to handle:', { tasks }) - tasks.forEach((task: string) => { + tasks.forEach(async (task: string) => { switch (task) { case 'shares': - sharesStore.load() + try { + await sharesStore.load() + } catch (error) { + if ((error as NotAllowed).name === 'NotAllowed') { + // User is not allowed to load shares. + // instead assume a changed share affecting the user. + // Reload the session context to update the share + sessionStore.load() + return + } + Logger.error('Error loading poll shares', { error }) + } break case 'polls': - pollStore.load() - pollsStore.load() + try { + await pollsStore.load() + } catch (error) { + if ((error as NotAllowed).name === 'NotAllowed') { + return + } + Logger.error('Error loading polls list', { error }) + } break case 'votes': votesStore.load() diff --git a/src/stores/polls.ts b/src/stores/polls.ts index 5ecd47174f..2f907ad254 100644 --- a/src/stores/polls.ts +++ b/src/stores/polls.ts @@ -23,6 +23,7 @@ import type { FilterType, SortType, } from './polls.types' +import { NotAllowed } from '../Exceptions/Exceptions' export const sortColumnsMapping: { [key in SortType]: string } = { created: 'status.created', @@ -312,7 +313,13 @@ export const usePollsStore = defineStore('polls', { * @return {Promise} */ async load(forced: boolean = true): Promise { - const pollGroupsStore = usePollGroupsStore() + const sessionStore = useSessionStore() + + if (!sessionStore.userStatus.isLoggedin) { + this.polls = [] + this.meta.status = '' + throw new NotAllowed('Not allowed to load polls; not logged in') + } if ( this.meta.status === 'loading' @@ -327,6 +334,8 @@ export const usePollsStore = defineStore('polls', { this.meta.status = 'loading' + const pollGroupsStore = usePollGroupsStore() + try { const response = await PollsAPI.getPolls() this.polls = response.data.polls diff --git a/src/stores/shares.ts b/src/stores/shares.ts index 773ff0abe7..2e2323b298 100644 --- a/src/stores/shares.ts +++ b/src/stores/shares.ts @@ -20,6 +20,8 @@ import type { SharePurpose, PublicPollEmailConditions, } from './shares.types' +import { usePollStore } from './poll' +import { NotAllowed } from '../Exceptions/Exceptions' export const useSharesStore = defineStore('shares', { state: (): SharesStore => ({ @@ -69,6 +71,14 @@ export const useSharesStore = defineStore('shares', { if (purpose === 'pollGroup') { const pollGroupsStore = usePollGroupsStore() + if ( + pollGroupsStore.currentPollGroup?.owner.id + !== useSessionStore().currentUser.id + ) { + this.shares = [] + throw new NotAllowed('Not allowed to load shares for this group') + } + Logger.info('Loading group shares') // For group shares, we need to use the current poll group ID @@ -77,6 +87,11 @@ export const useSharesStore = defineStore('shares', { } pollOrPollGroupId = pollGroupsStore.currentPollGroup.id } else { + const pollStore = usePollStore() + if (!pollStore.permissions.edit) { + this.shares = [] + throw new NotAllowed('Not allowed to load shares for this poll') + } Logger.info('Loading poll shares') // For regular poll shares, we use the current poll ID const sessionStore = useSessionStore() diff --git a/src/stores/votes.ts b/src/stores/votes.ts index 26f36ebb58..ded0d86140 100644 --- a/src/stores/votes.ts +++ b/src/stores/votes.ts @@ -115,7 +115,11 @@ export const useVotesStore = defineStore('votes', { (user) => user.id === sessionStore.currentUser?.id, ) - if (currentUserIndex < 0 && !pollStore.status.isExpired) { + if ( + currentUserIndex < 0 + && !pollStore.status.isExpired + && sessionStore.currentUser.type !== 'public' + ) { // add current user to the begining of the list if not already present // and if the poll is not expired participants.unshift(sessionStore.currentUser) diff --git a/src/views/Vote.vue b/src/views/Vote.vue index 2c0cdf1b92..d103b47c9e 100644 --- a/src/views/Vote.vue +++ b/src/views/Vote.vue @@ -40,6 +40,7 @@ import { useSubscriptionStore } from '../stores/subscription' import type { CollapsibleProps } from '../components/Base/modules/Collapsible.vue' import { Event } from '../Types' +import { loadContext } from '../composables/context' const pollStore = usePollStore() const optionsStore = useOptionsStore() @@ -156,7 +157,11 @@ async function resetPoll() { subscriptionStore.$reset() } -onBeforeRouteUpdate(async () => { +onBeforeRouteUpdate(async (to, from) => { + if (to.name === 'publicVote' && to.params.token !== from.params.token) { + loadContext(to) + // Same poll, no need to reload + } loadPoll(true) }) diff --git a/src/workers/pollWatcher.worker.ts b/src/workers/pollWatcher.worker.ts index 2eb81210be..db4fa4481c 100644 --- a/src/workers/pollWatcher.worker.ts +++ b/src/workers/pollWatcher.worker.ts @@ -182,7 +182,7 @@ self.onmessage = async (props: MessageEvent) => { status: 'idle', mode, interval, - message: `[Worker] Sleeping for .${Math.floor(interval / 1000)} s`, + message: `[Worker] Sleeping for ${Math.floor(interval / 1000)} s`, }) await sleep(interval) }