Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
216 changes: 118 additions & 98 deletions src/django_program/conference/management/commands/setup_groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,128 +5,141 @@
from django.contrib.auth.models import Group, Permission
from django.core.management.base import BaseCommand

# All custom Conference permissions for granting to the organizer group.
_ALL_CONFERENCE_PERMS = [
"view_dashboard",
"manage_conference_settings",
"view_program",
"change_program",
"view_registration",
"change_registration",
"view_commerce",
"change_commerce",
"view_badges",
"change_badges",
"view_sponsors",
"change_sponsors",
"view_bulk_purchases",
"change_bulk_purchases",
"view_finance",
"change_finance",
"view_reports",
"export_reports",
"view_checkin",
"use_terminal",
"view_overrides",
"change_overrides",
]

_ALL_VIEW_CONFERENCE_PERMS = [
p for p in _ALL_CONFERENCE_PERMS if (p.startswith("view_") or p == "export_reports") and p != "view_checkin"
]

# Mapping of group name -> list of (app_label, codename) permissions.
# Uses the app labels defined in each app's AppConfig (program_conference, etc.).
_GROUP_PERMISSIONS: dict[str, list[tuple[str, str]]] = {
"Program: Conference Organizers": [
# Full access to conference and section management
"Conference Organizer": [
# Django CRUD on Conference model
("program_conference", "add_conference"),
("program_conference", "change_conference"),
("program_conference", "delete_conference"),
("program_conference", "view_conference"),
("program_conference", "add_section"),
("program_conference", "change_section"),
("program_conference", "delete_section"),
("program_conference", "view_section"),
# Full access to ticket types and add-ons
("program_registration", "add_tickettype"),
("program_registration", "change_tickettype"),
("program_registration", "delete_tickettype"),
("program_registration", "view_tickettype"),
("program_registration", "add_addon"),
("program_registration", "change_addon"),
("program_registration", "delete_addon"),
("program_registration", "view_addon"),
# Voucher management
("program_registration", "add_voucher"),
("program_registration", "change_voucher"),
("program_registration", "delete_voucher"),
("program_registration", "view_voucher"),
# View orders and carts (read-only for operational awareness)
("program_registration", "view_order"),
("program_registration", "view_orderlineitem"),
("program_registration", "view_cart"),
("program_registration", "view_cartitem"),
("program_registration", "view_payment"),
("program_registration", "view_credit"),
# Activity signup management
# All custom Conference permissions
*[("program_conference", p) for p in _ALL_CONFERENCE_PERMS],
# Programs app
("program_programs", "view_activity"),
("program_programs", "manage_activity"),
("program_programs", "view_travel_grant"),
("program_programs", "review_travel_grant"),
("program_programs", "disburse_travel_grant"),
("program_programs", "review_receipt"),
],
"Program: Registration & Ticket Support": [
# View conference context
"Program Committee": [
("program_conference", "view_conference"),
("program_conference", "view_section"),
# View and manage tickets/add-ons
("program_registration", "view_tickettype"),
("program_registration", "change_tickettype"),
("program_registration", "view_addon"),
("program_registration", "change_addon"),
# Voucher management (issue comps, apply discounts)
("program_registration", "add_voucher"),
("program_registration", "change_voucher"),
("program_registration", "view_voucher"),
# Cart and order support
("program_registration", "view_cart"),
("program_registration", "view_cartitem"),
("program_registration", "view_order"),
("program_registration", "change_order"),
("program_registration", "view_orderlineitem"),
("program_registration", "view_payment"),
("program_registration", "view_credit"),
("program_conference", "view_dashboard"),
("program_conference", "view_program"),
("program_conference", "change_program"),
("program_conference", "view_overrides"),
("program_conference", "change_overrides"),
],
"Program: Finance & Accounting": [
# Read-only conference context
"Registration Manager": [
("program_conference", "view_conference"),
# Full access to financial records
("program_registration", "view_order"),
("program_registration", "change_order"),
("program_registration", "view_orderlineitem"),
("program_registration", "view_payment"),
("program_registration", "add_payment"),
("program_registration", "change_payment"),
("program_registration", "view_credit"),
("program_registration", "add_credit"),
("program_registration", "change_credit"),
# View vouchers for audit trail
("program_registration", "view_voucher"),
# View tickets for revenue reporting
("program_registration", "view_tickettype"),
("program_registration", "view_addon"),
("program_conference", "view_dashboard"),
("program_conference", "view_registration"),
("program_conference", "change_registration"),
("program_conference", "view_commerce"),
("program_conference", "change_commerce"),
("program_conference", "view_badges"),
("program_conference", "change_badges"),
("program_conference", "view_checkin"),
("program_conference", "use_terminal"),
("program_conference", "view_bulk_purchases"),
("program_conference", "change_bulk_purchases"),
],
"Program: Activity Organizers": [
"Finance Team": [
("program_conference", "view_conference"),
("program_programs", "manage_activity"),
("program_conference", "view_dashboard"),
("program_conference", "view_finance"),
("program_conference", "change_finance"),
("program_conference", "view_reports"),
("program_conference", "export_reports"),
("program_conference", "view_registration"),
("program_conference", "view_commerce"),
],
"Travel Grant Reviewer": [
("program_conference", "view_conference"),
("program_conference", "view_dashboard"),
("program_programs", "view_travel_grant"),
("program_programs", "review_travel_grant"),
("program_programs", "review_receipt"),
],
"Sponsor Manager": [
("program_conference", "view_conference"),
("program_conference", "view_dashboard"),
("program_conference", "view_sponsors"),
("program_conference", "change_sponsors"),
("program_conference", "view_bulk_purchases"),
("program_conference", "change_bulk_purchases"),
],
"Check-in Staff": [
("program_conference", "view_conference"),
("program_conference", "view_dashboard"),
("program_conference", "view_checkin"),
],
"Activity Organizer": [
("program_conference", "view_conference"),
("program_conference", "view_dashboard"),
("program_programs", "view_activity"),
("program_programs", "view_activitysignup"),
("program_programs", "manage_activity"),
],
"Program: Reports": [
# Read-only access for report dashboards
"Reports Viewer": [
("program_conference", "view_conference"),
("program_registration", "view_tickettype"),
("program_registration", "view_addon"),
("program_registration", "view_voucher"),
("program_registration", "view_order"),
("program_registration", "view_orderlineitem"),
("program_registration", "view_payment"),
("program_registration", "view_credit"),
("program_conference", "view_dashboard"),
("program_conference", "view_reports"),
],
"Program: Read-Only Staff": [
"Read-Only Staff": [
("program_conference", "view_conference"),
("program_conference", "view_section"),
("program_registration", "view_tickettype"),
("program_registration", "view_addon"),
("program_registration", "view_voucher"),
("program_registration", "view_cart"),
("program_registration", "view_cartitem"),
("program_registration", "view_order"),
("program_registration", "view_orderlineitem"),
("program_registration", "view_payment"),
("program_registration", "view_credit"),
*[("program_conference", p) for p in _ALL_VIEW_CONFERENCE_PERMS],
("program_programs", "view_activity"),
("program_programs", "view_travel_grant"),
],
}


class Command(BaseCommand):
"""Create default permission groups for conference staff roles.

Creates six groups with appropriate permissions:
Creates ten groups with granular permissions:

* **Conference Organizers** -- full conference, ticket, voucher, and activity management
* **Registration & Ticket Support** -- ticket ops, voucher issuing, order support
* **Finance & Accounting** -- orders, payments, credits, and revenue visibility
* **Reports** -- read-only access to report dashboards and underlying data
* **Activity Organizers** -- per-activity signup management
* **Read-Only Staff** -- view-only access to all registration models
* **Conference Organizer** -- full access to all conference management
* **Program Committee** -- program content and Pretalx overrides
* **Registration Manager** -- attendees, orders, commerce, badges, check-in
* **Finance Team** -- financial dashboard, expenses, reports, read-only commerce
* **Travel Grant Reviewer** -- travel grant review and receipt approval
* **Sponsor Manager** -- sponsor and bulk purchase management
* **Check-in Staff** -- check-in dashboard access only
* **Activity Organizer** -- activity and signup management
* **Reports Viewer** -- read-only access to reports dashboard
* **Read-Only Staff** -- view-only access to all sections

Safe to run multiple times; existing groups are updated with the defined
permission set.
Expand All @@ -147,12 +160,19 @@ def handle(self, *args: Any, **options: Any) -> None:
group, created = Group.objects.get_or_create(name=group_name)
verb = "Created" if created else "Updated"

perm_set = set(perm_specs)
app_labels = {app for app, _ in perm_set}
permissions = Permission.objects.filter(
content_type__app_label__in={app for app, _ in perm_specs},
content_type__app_label__in=app_labels,
)
matched = [p for p in permissions if (p.content_type.app_label, p.codename) in perm_specs]
matched = [p for p in permissions if (p.content_type.app_label, p.codename) in perm_set]
missing = perm_set - {(p.content_type.app_label, p.codename) for p in matched}
group.permissions.set(matched)

if missing and verbosity > 0:
labels = ", ".join(f"{app}.{code}" for app, code in sorted(missing))
self.stdout.write(self.style.WARNING(f" Warning: {group_name} missing permissions: {labels}"))

if verbosity > 0:
self.stdout.write(self.style.SUCCESS(f" {verb} group '{group_name}' with {len(matched)} permissions"))

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Generated by Django 5.2.11 on 2026-03-19 18:27

from django.db import migrations


class Migration(migrations.Migration):
dependencies = [
("program_conference", "0009_featureflags_visa_letters_enabled"),
]

operations = [
migrations.AlterModelOptions(
name="conference",
options={
"ordering": ["-start_date"],
"permissions": [
("view_dashboard", "Can view conference dashboard"),
("manage_conference_settings", "Can edit conference settings and sync"),
("view_program", "Can view program content"),
("change_program", "Can edit program content"),
("view_registration", "Can view attendees and orders"),
("change_registration", "Can manage orders and visa letters"),
("view_commerce", "Can view ticket types, add-ons, vouchers"),
("change_commerce", "Can manage ticket types, add-ons, vouchers"),
("view_badges", "Can view badges and templates"),
("change_badges", "Can manage badges and templates"),
("view_sponsors", "Can view sponsors"),
("change_sponsors", "Can manage sponsors"),
("view_bulk_purchases", "Can view bulk purchases"),
("change_bulk_purchases", "Can manage bulk purchases"),
("view_finance", "Can view financial dashboard and expenses"),
("change_finance", "Can manage expenses"),
("view_reports", "Can view reports and analytics"),
("export_reports", "Can export report data"),
("view_checkin", "Can access check-in"),
("use_terminal", "Can use Terminal POS"),
("view_overrides", "Can view Pretalx overrides"),
("change_overrides", "Can manage Pretalx overrides"),
],
},
),
]
24 changes: 24 additions & 0 deletions src/django_program/conference/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,30 @@ class Conference(models.Model):

class Meta:
ordering = ["-start_date"]
permissions = [
("view_dashboard", "Can view conference dashboard"),
("manage_conference_settings", "Can edit conference settings and sync"),
("view_program", "Can view program content"),
("change_program", "Can edit program content"),
("view_registration", "Can view attendees and orders"),
("change_registration", "Can manage orders and visa letters"),
("view_commerce", "Can view ticket types, add-ons, vouchers"),
("change_commerce", "Can manage ticket types, add-ons, vouchers"),
("view_badges", "Can view badges and templates"),
("change_badges", "Can manage badges and templates"),
("view_sponsors", "Can view sponsors"),
("change_sponsors", "Can manage sponsors"),
("view_bulk_purchases", "Can view bulk purchases"),
("change_bulk_purchases", "Can manage bulk purchases"),
("view_finance", "Can view financial dashboard and expenses"),
("change_finance", "Can manage expenses"),
("view_reports", "Can view reports and analytics"),
("export_reports", "Can export report data"),
("view_checkin", "Can access check-in"),
("use_terminal", "Can use Terminal POS"),
("view_overrides", "Can view Pretalx overrides"),
("change_overrides", "Can manage Pretalx overrides"),
]

def __str__(self) -> str:
return self.name
Expand Down
Loading
Loading