fix: user roles

This commit is contained in:
Benno Tielen 2025-03-12 15:08:13 +01:00
parent 9b674343f8
commit 02cd209493
15 changed files with 146 additions and 88 deletions

View file

@ -1,5 +1,5 @@
import { CollectionConfig } from 'payload'
import { isAdminOrEmployee } from '@/collections/access/admin'
import { hide, isAdminOrEmployee } from '@/collections/access/admin'
import { nextSunday } from '@/utils/sunday'
export const Announcements: CollectionConfig = {
@ -46,6 +46,9 @@ export const Announcements: CollectionConfig = {
required: true
}
],
admin: {
hidden: hide
},
access: {
read: () => true,
create: isAdminOrEmployee(),

View file

@ -1,5 +1,5 @@
import { CollectionConfig } from 'payload'
import { isAdminOrEmployee } from '@/collections/access/admin'
import { hide, isAdminOrEmployee } from '@/collections/access/admin'
import { ParagraphBlock } from '@/collections/blocks/Paragraph'
import { DocumentBlock } from '@/collections/blocks/Document'
import { ContactformBlock } from '@/collections/blocks/Contactform'
@ -62,7 +62,8 @@ export const Blog: CollectionConfig = {
},
],
admin: {
useAsTitle: 'title'
useAsTitle: 'title',
hidden: hide
},
access: {
read: () => true,

View file

@ -1,5 +1,5 @@
import { CollectionConfig } from 'payload'
import { isAdminOrEmployee } from '@/collections/access/admin'
import { hide, isAdminOrEmployee } from '@/collections/access/admin'
export const Churches: CollectionConfig = {
slug: 'church',
@ -31,6 +31,7 @@ export const Churches: CollectionConfig = {
],
admin: {
useAsTitle: 'name',
hidden: hide
},
access: {
read: () => true,

View file

@ -1,5 +1,5 @@
import { CollectionConfig } from 'payload'
import { isAdminOrEmployee } from '@/collections/access/admin'
import { hide, isAdminOrEmployee } from '@/collections/access/admin'
export const ContactPerson: CollectionConfig = {
slug: 'contactPerson',
@ -38,7 +38,8 @@ export const ContactPerson: CollectionConfig = {
}
],
admin: {
useAsTitle: "name"
useAsTitle: "name",
hidden: hide
},
access: {
read: () => true,

View file

@ -1,4 +1,5 @@
import type { CollectionConfig } from 'payload'
import { hide, isAdminOrEmployee } from '@/collections/access/admin'
export const Documents: CollectionConfig = {
slug: 'documents',
@ -12,8 +13,13 @@ export const Documents: CollectionConfig = {
},
access: {
read: () => true,
update: isAdminOrEmployee(),
delete: isAdminOrEmployee()
},
fields: [],
admin: {
hidden: hide
},
upload: {
mimeTypes: ['application/pdf']
},

View file

@ -1,5 +1,6 @@
import { CollectionConfig } from 'payload'
import { isAdminOrEmployee } from '@/collections/access/admin'
import { stringify } from 'qs-esm'
import { Event, Group, User } from '@/payload-types'
export const Events: CollectionConfig = {
slug: 'event',
@ -12,14 +13,6 @@ export const Events: CollectionConfig = {
},
},
fields: [
{
name: 'photo',
label: {
de: 'Foto',
},
type: 'upload',
relationTo: 'media',
},
{
name: 'title',
type: 'text',
@ -67,7 +60,7 @@ export const Events: CollectionConfig = {
return 'You are not allowed to do this'
}
if (user.roles === 'user' && value) {
if (user.roles === 'user' && value && value.length > 0) {
return 'Sie sind nur erlaubt Veranstaltungen für Gruppen zu erstellen.'
}
@ -82,7 +75,20 @@ export const Events: CollectionConfig = {
label: {
de: 'Gruppe',
},
validate: (value: any, options: { req: { user: any } }) => {
access: {
update: ({req: { user}, data}) => {
if(user && (user.roles == "admin" || user.roles =="employee")) {
return true
}
if(hasGroup(user, data)) {
return true
}
return false
}
},
validate: (value, options: { req: { user: any } }) => {
let user = options.req.user
if (!user) {
@ -90,8 +96,16 @@ export const Events: CollectionConfig = {
}
if (user.roles === 'user') {
if (!user.groups || (user.groups && !user.groups.includes(value))) {
return 'Sie können nur Veranstaltungen für Ihre eigene Gruppen erstellen!'
if(!Array.isArray(value) || value.length === 0) {
return 'Sie müssen die Veranstaltung verknüpfen mit ihrer Gruppe.'
}
if(!Array.isArray(user.groups) || user.groups.length === 0) {
return "Sie sind kein Mitglied einer Gruppe, und können deswegen keine Veranstaltung erstellen."
}
if(!value.every(id => user.groups.includes(id))) {
return "Sie sind nur berechtigt Veranstaltungen für ihrer Gruppe zu erstellen"
}
}
@ -131,8 +145,19 @@ export const Events: CollectionConfig = {
de: "Anmeldelink"
}
},
{
name: 'photo',
label: {
de: 'Foto',
},
type: 'upload',
relationTo: 'media',
},
{
name: 'flyer',
label: {
de: "Flyer (PDF)"
},
type: 'upload',
relationTo: 'documents'
},
@ -160,6 +185,70 @@ export const Events: CollectionConfig = {
},
access: {
read: () => true,
delete: isAdminOrEmployee(),
// admins and employees can delete, others only if they are member of the group
delete: async ({ req: { user }, id }) => {
if (!user) {
return false
}
if(user.roles === 'admin' || user.roles === 'employee')
return true
if(typeof id !== 'string') {
return false
}
const event = await fetchEvent(id)
if (hasGroup(user, event)) {
return true
}
return false
},
},
}
/**
* Check if we have
* - a user
* - data with groups
* - the user is member of one of the groups
*
* @param user
* @param data
*/
const hasGroup = (user: null | User , data: Partial<any> | undefined) => {
return user
&& user.roles === 'user'
&& data
&& Array.isArray(data.group)
&& data.group.length > 0
&& data.group.some((group: string | Group) => {
if (!Array.isArray(user.groups)) {
return false;
}
if (typeof group === "string")
return user.groups.includes(group)
else
return user.groups.includes(group.id)
})
}
/**
* Fetch event
* @param id
*/
const fetchEvent = async (id: string): Promise<undefined|Event> => {
const stringifiedQuery = stringify(
{
select: {
group: true,
},
},
{ addQueryPrefix: true },
)
const res = await fetch(`http://localhost:3000/api/event/${id}${stringifiedQuery}`);
if (!res.ok) return undefined
return res.json()
}

View file

@ -1,5 +1,5 @@
import { CollectionConfig } from 'payload'
import { isAdminOrEmployee } from '@/collections/access/admin'
import { hide, isAdminOrEmployee } from '@/collections/access/admin'
export const Highlight: CollectionConfig = {
slug: 'highlight',
@ -62,7 +62,8 @@ export const Highlight: CollectionConfig = {
}
],
admin: {
useAsTitle: 'text'
useAsTitle: 'text',
hidden: hide
},
access: {
read: () => true,

View file

@ -1,5 +1,5 @@
import { CollectionConfig } from 'payload'
import { isAdminOrEmployee } from '@/collections/access/admin'
import { hide, isAdminOrEmployee } from '@/collections/access/admin'
import { nextSunday } from '@/utils/sunday'
export const LiturgicalCalendar: CollectionConfig = {
@ -46,6 +46,9 @@ export const LiturgicalCalendar: CollectionConfig = {
required: true
}
],
admin: {
hidden: hide
},
access: {
read: () => true,
create: isAdminOrEmployee(),

View file

@ -1,10 +1,12 @@
import type { CollectionConfig } from 'payload'
import { Media as MediaType } from '../payload-types'
import { isAdminOrEmployee } from '@/collections/access/admin'
export const Media: CollectionConfig = {
slug: 'media',
access: {
read: () => true,
update: isAdminOrEmployee(),
delete: isAdminOrEmployee()
},
admin: {
listSearchableFields: ['search']

View file

@ -1,5 +1,5 @@
import { CollectionConfig } from 'payload'
import { isAdmin, isAdminOrEmployee } from '@/collections/access/admin'
import { hide, isAdmin, isAdminOrEmployee } from '@/collections/access/admin'
export const Parish: CollectionConfig = {
slug: 'parish',
@ -131,6 +131,7 @@ export const Parish: CollectionConfig = {
],
admin: {
useAsTitle: 'name',
hidden: hide
},
access: {
read: () => true,

View file

@ -1,5 +1,5 @@
import { CollectionConfig } from 'payload'
import { isAdminOrEmployee } from '@/collections/access/admin'
import { hide, isAdminOrEmployee } from '@/collections/access/admin'
export const PopesPrayerIntentions: CollectionConfig = {
slug: 'popePrayerIntentions',
@ -100,6 +100,7 @@ export const PopesPrayerIntentions: CollectionConfig = {
],
admin: {
useAsTitle: 'title',
hidden: hide
},
access: {
read: () => true,

View file

@ -1,5 +1,5 @@
import type { CollectionConfig } from 'payload'
import { isAdminOrEmployee } from '@/collections/access/admin'
import { isAdmin, isAdminOrEmployee } from '@/collections/access/admin'
export const Users: CollectionConfig = {
slug: 'users',
@ -69,8 +69,9 @@ export const Users: CollectionConfig = {
},
],
access: {
read: isAdminOrEmployee(),
create: isAdminOrEmployee(),
update: isAdminOrEmployee(),
delete: isAdminOrEmployee(),
delete: isAdmin(),
},
}

View file

@ -1,5 +1,5 @@
import { CollectionConfig } from 'payload'
import { isAdminOrEmployee } from '@/collections/access/admin'
import { hide, isAdminOrEmployee } from '@/collections/access/admin'
export const Worship: CollectionConfig = {
slug: 'worship',
@ -101,7 +101,8 @@ export const Worship: CollectionConfig = {
],
admin: {
defaultColumns: ["date", 'location', 'type', 'celebrant'],
listSearchableFields: ['date', 'location']
listSearchableFields: ['date', 'location'],
hidden: hide
},
access: {
read: () => true,

View file

@ -1,4 +1,4 @@
import type { Access } from 'payload'
import type { Access, ClientUser } from 'payload'
export const isAdmin =
(): Access =>
@ -15,3 +15,5 @@ export const isAdminOrEmployee =
return user.roles === 'admin' || user.roles === 'employee'
}
export const hide = (args: { user: ClientUser }) => args.user && args.user.roles === "user"

View file

@ -6,65 +6,10 @@
* and re-run `payload generate:types` to regenerate this file.
*/
/**
* Supported timezones in IANA format.
*
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "supportedTimezones".
*/
export type SupportedTimezones =
| 'Pacific/Midway'
| 'Pacific/Niue'
| 'Pacific/Honolulu'
| 'Pacific/Rarotonga'
| 'America/Anchorage'
| 'Pacific/Gambier'
| 'America/Los_Angeles'
| 'America/Tijuana'
| 'America/Denver'
| 'America/Phoenix'
| 'America/Chicago'
| 'America/Guatemala'
| 'America/New_York'
| 'America/Bogota'
| 'America/Caracas'
| 'America/Santiago'
| 'America/Buenos_Aires'
| 'America/Sao_Paulo'
| 'Atlantic/South_Georgia'
| 'Atlantic/Azores'
| 'Atlantic/Cape_Verde'
| 'Europe/London'
| 'Europe/Berlin'
| 'Africa/Lagos'
| 'Europe/Athens'
| 'Africa/Cairo'
| 'Europe/Moscow'
| 'Asia/Riyadh'
| 'Asia/Dubai'
| 'Asia/Baku'
| 'Asia/Karachi'
| 'Asia/Tashkent'
| 'Asia/Calcutta'
| 'Asia/Dhaka'
| 'Asia/Almaty'
| 'Asia/Jakarta'
| 'Asia/Bangkok'
| 'Asia/Shanghai'
| 'Asia/Singapore'
| 'Asia/Tokyo'
| 'Asia/Seoul'
| 'Australia/Sydney'
| 'Pacific/Guam'
| 'Pacific/Noumea'
| 'Pacific/Auckland'
| 'Pacific/Fiji';
export interface Config {
auth: {
users: UserAuthOperations;
};
blocks: {};
collections: {
parish: Parish;
church: Church;
@ -399,7 +344,6 @@ export interface Highlight {
*/
export interface Event {
id: string;
photo?: (string | null) | Media;
title: string;
date: string;
location: string | Location;
@ -409,6 +353,7 @@ export interface Event {
shortDescription: string;
description: string;
rsvpLink?: string | null;
photo?: (string | null) | Media;
flyer?: (string | null) | Document;
cancelled: boolean;
isRecurring: boolean;
@ -814,7 +759,6 @@ export interface HighlightSelect<T extends boolean = true> {
* via the `definition` "event_select".
*/
export interface EventSelect<T extends boolean = true> {
photo?: T;
title?: T;
date?: T;
location?: T;
@ -824,6 +768,7 @@ export interface EventSelect<T extends boolean = true> {
shortDescription?: T;
description?: T;
rsvpLink?: T;
photo?: T;
flyer?: T;
cancelled?: T;
isRecurring?: T;