student life social platform
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'lebihae/warn-canbook-bypasses' into 'main'

warning admins/managers when they're bypassing checks when booking a ticket

See merge request churros/churros!588

+55 -21
+6
.changeset/fair-days-yell.md
··· 1 + --- 2 + '@churros/api': minor 3 + '@churros/app': minor 4 + --- 5 + 6 + warn managers or admins when they're bypassing checks when booking a ticket
+4
CHANGELOG.md
··· 11 11 12 12 ## [Unreleased] 13 13 14 + ### Nouveautés 15 + 16 + - Les managers d'évènements et admins sont maintenant avertis quand iels s'apprêtent à prendre une place qu'iels ne pourrait pas prendre sans leur statut de manager/admin 17 + 14 18 ## [4.7.0] - 2025-02-08 15 19 16 20 ## [4.6.1] - 2025-02-07
+7 -1
packages/api/src/modules/ticketing/types/ticket.ts
··· 188 188 themself: t.arg.boolean({ 189 189 description: 'On souhaite réserver pour soi-même', 190 190 }), 191 + asRegularPerson: t.arg.boolean({ 192 + defaultValue: false, 193 + description: 194 + "Ignorer le fait qu'on puisse voir toutes les places (managers, admins, admins AE, etc)", 195 + }), 191 196 }, 192 - async resolve({ id }, { themself }, { user }) { 197 + async resolve({ id }, { themself, asRegularPerson }, { user }) { 193 198 const ticket = await prisma.ticket.findUniqueOrThrow({ 194 199 where: { id }, 195 200 include: canBookTicket.prismaIncludes, ··· 205 210 : null, 206 211 beneficiary: themself ? null : 'someone else', 207 212 pointOfContact: ticket.event.managers.at(0)?.user, 213 + ignoreCanSeeAllBookings: asRegularPerson, 208 214 }); 209 215 return can ? null : whynot; 210 216 },
+9 -5
packages/api/src/modules/ticketing/utils/permissions.ts
··· 1 - import { canEditEvent, canEditEventPrismaIncludes } from '#modules/events'; 2 1 import { fullName, log, type Capacity, type Context } from '#lib'; 2 + import { canEditEvent, canEditEventPrismaIncludes } from '#modules/events'; 3 3 import { actualPrice } from '#modules/payments'; 4 4 import { userIsAdminOf } from '#permissions'; 5 5 import type { Prisma, User } from '@churros/db/prisma'; ··· 216 216 /** 217 217 * @returns [canBook, why] 218 218 * @param beneficiary - can be a string if it's a beneficiary (free form) or a User if it's a churrosBeneficiary 219 + * @param ignoreCanSeeAllBookings - if true, will ignore the canSeeAllBookings check. useful to determine if someone can book a ticket only because they're managers/admins/etc 219 220 */ 220 221 export function canBookTicket({ 221 222 user, ··· 224 225 ticket, 225 226 debug, 226 227 pointOfContact, 228 + ignoreCanSeeAllBookings = false, 227 229 }: { 228 230 user: Context['user']; 229 231 userAdditionalData: null | Prisma.UserGetPayload<{ ··· 233 235 beneficiary: string | User | null | undefined; 234 236 ticket: Prisma.TicketGetPayload<{ include: typeof canBookTicket.prismaIncludes }>; 235 237 debug?: boolean; 238 + ignoreCanSeeAllBookings?: boolean; 236 239 }): [boolean, string] { 237 240 const dret = <T extends [boolean, string]>(ret: T, data: unknown) => { 238 241 if (debug) void log('ticketing', 'debug/can-book-ticket', { ret, data }, ticket.id, user); ··· 242 245 if (debug) void log('ticketing', 'debug/can-book-ticket', { data }, ticket.id, user); 243 246 }; 244 247 245 - if (canSeeAllBookings(ticket.event, user)) return dret([true, ''], { why: 'canSeeAllBookings' }); 248 + if (!ignoreCanSeeAllBookings && canSeeAllBookings(ticket.event, user)) 249 + return dret([true, ''], { why: 'canSeeAllBookings' }); 246 250 247 251 d({ canSeeAllBookings: false }); 248 252 ··· 288 292 289 293 d({ shotgunPastClosed: true }); 290 294 291 - if (ticket.onlyManagersCanProvide && !canSeeAllBookings(ticket.event, user)) 295 + if (ticket.onlyManagersCanProvide) 292 296 return dret([false, 'Seul un·e manager peut faire une réservation pour ce billet'], {}); 293 297 294 298 d({ managersOnlyCheck: true }); 295 299 296 - if (!canSeeAllBookings(ticket.event, user) && user) { 300 + if (user) { 297 301 const bookingsByUser = ticket.registrations.filter((r) => r.authorId === user.id); 298 302 if (ticket.godsonLimit > 0 && bookingsByUser.length > ticket.godsonLimit) 299 303 return dret([false, 'Vous avez atteint la limite de parrainages pour ce billet'], {}); ··· 305 309 306 310 d({ placesLeftCheck: true }); 307 311 308 - if (!canSeeAllBookings(ticket.event, user) && ticket.godsonLimit <= 0 && beneficiary) 312 + if (ticket.godsonLimit <= 0 && beneficiary) 309 313 return dret([false, "Ce billet n'accepte pas de parrainages"], {}); 310 314 311 315 d({ godsonEnabledCheck: true });
+4
packages/app/schema.graphql
··· 6356 6356 """ 6357 6357 cannotBookReason( 6358 6358 """ 6359 + Ignorer le fait qu'on puisse voir toutes les places (managers, admins, admins AE, etc) 6360 + """ 6361 + asRegularPerson: Boolean! = false 6362 + """ 6359 6363 On souhaite réserver pour soi-même 6360 6364 """ 6361 6365 themself: Boolean!
+25 -15
packages/app/src/routes/(app)/events/[id]/ModalBookTicket.svelte
··· 11 11 import LoadingText from '$lib/components/LoadingText.svelte'; 12 12 import MaybeError from '$lib/components/MaybeError.svelte'; 13 13 import ModalOrDrawer from '$lib/components/ModalOrDrawer.svelte'; 14 - import { loaded, onceLoaded } from '$lib/loading'; 14 + import { countThing } from '$lib/i18n'; 15 + import { allLoaded, loaded } from '$lib/loading'; 15 16 import { mutate } from '$lib/mutations'; 16 17 import { refroute } from '$lib/navigation'; 17 18 import { route } from '$lib/ROUTES'; ··· 40 41 price: minimumPrice(applyPromotions: true) 41 42 cannotBookForMe: cannotBookReason(themself: true) 42 43 cannotBookForSomeoneElse: cannotBookReason(themself: false) 44 + cannotBookAsRegularUser: cannotBookReason(themself: true, asRegularPerson: true) 43 45 godsonLimit 44 46 remainingGodsons 45 47 event { ··· 154 156 </h2> 155 157 </header> 156 158 <div class="contents"> 157 - {#if !$data} 159 + {#if !$data || !allLoaded( [$data.cannotBookAsRegularUser, $data.cannotBookForMe, $data.cannotBookForSomeoneElse], )} 158 160 <LoadingChurros /> 159 161 <p>Chargement...</p> 160 162 <nav> ··· 165 167 <LoadingText tag="p" value={$data.cannotBookForMe} /> 166 168 </Alert> 167 169 {:else if step === 'start'} 168 - {#if !$data.cannotBookForSomeoneElse && $data.godsonLimit > 0} 169 - <Alert theme="warning"> 170 - <LoadingText tag="p" value={$data.cannotBookForSomeoneElse} /> 171 - </Alert> 172 - {:else if $data?.godsonLimit > 0} 173 - <p class="godsons-remaining"> 174 - Il te reste <LoadingText value={$data.remainingGodsons} /> parrainage{onceLoaded( 175 - $data.remainingGodsons, 176 - (count) => (count > 1 ? 's' : ''), 177 - '(s)', 178 - )} 179 - </p> 180 - {/if} 170 + <section class="notices"> 171 + {#if !$data.cannotBookForMe && $data.cannotBookAsRegularUser} 172 + <Alert theme="warning"> 173 + <p>Si tu n'étais pas admin ou manager, tu ne pourrais pas réserver cette place:</p> 174 + <LoadingText tag="p" value={$data.cannotBookAsRegularUser} /> 175 + </Alert> 176 + {/if} 177 + {#if !$data.cannotBookForSomeoneElse && $data.godsonLimit > 0} 178 + <Alert theme="warning"> 179 + <LoadingText tag="p" value={$data.cannotBookForSomeoneElse} /> 180 + </Alert> 181 + {:else if $data?.godsonLimit > 0} 182 + <p class="godsons-remaining"> 183 + Il te reste {countThing('parrainage', $data.remainingGodsons)} 184 + </p> 185 + {/if} 186 + </section> 181 187 <div class="actions"> 182 188 <ButtonSecondary 183 189 disabled={Boolean($data.cannotBookForMe)} ··· 339 345 gap: 0.5rem; 340 346 align-items: center; 341 347 justify-content: center; 348 + } 349 + 350 + .notices { 351 + margin-bottom: 1.2em; 342 352 } 343 353 </style>