Skip to content

Commit e4de09d

Browse files
JacobCoffeeclaude
andauthored
feat: add granular permission and group system for conference management (#60)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent fe996d1 commit e4de09d

32 files changed

Lines changed: 621 additions & 332 deletions

src/django_program/conference/management/commands/setup_groups.py

Lines changed: 118 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -5,128 +5,141 @@
55
from django.contrib.auth.models import Group, Permission
66
from django.core.management.base import BaseCommand
77

8+
# All custom Conference permissions for granting to the organizer group.
9+
_ALL_CONFERENCE_PERMS = [
10+
"view_dashboard",
11+
"manage_conference_settings",
12+
"view_program",
13+
"change_program",
14+
"view_registration",
15+
"change_registration",
16+
"view_commerce",
17+
"change_commerce",
18+
"view_badges",
19+
"change_badges",
20+
"view_sponsors",
21+
"change_sponsors",
22+
"view_bulk_purchases",
23+
"change_bulk_purchases",
24+
"view_finance",
25+
"change_finance",
26+
"view_reports",
27+
"export_reports",
28+
"view_checkin",
29+
"use_terminal",
30+
"view_overrides",
31+
"change_overrides",
32+
]
33+
34+
_ALL_VIEW_CONFERENCE_PERMS = [
35+
p for p in _ALL_CONFERENCE_PERMS if (p.startswith("view_") or p == "export_reports") and p != "view_checkin"
36+
]
37+
838
# Mapping of group name -> list of (app_label, codename) permissions.
9-
# Uses the app labels defined in each app's AppConfig (program_conference, etc.).
1039
_GROUP_PERMISSIONS: dict[str, list[tuple[str, str]]] = {
11-
"Program: Conference Organizers": [
12-
# Full access to conference and section management
40+
"Conference Organizer": [
41+
# Django CRUD on Conference model
1342
("program_conference", "add_conference"),
1443
("program_conference", "change_conference"),
1544
("program_conference", "delete_conference"),
1645
("program_conference", "view_conference"),
17-
("program_conference", "add_section"),
18-
("program_conference", "change_section"),
19-
("program_conference", "delete_section"),
20-
("program_conference", "view_section"),
21-
# Full access to ticket types and add-ons
22-
("program_registration", "add_tickettype"),
23-
("program_registration", "change_tickettype"),
24-
("program_registration", "delete_tickettype"),
25-
("program_registration", "view_tickettype"),
26-
("program_registration", "add_addon"),
27-
("program_registration", "change_addon"),
28-
("program_registration", "delete_addon"),
29-
("program_registration", "view_addon"),
30-
# Voucher management
31-
("program_registration", "add_voucher"),
32-
("program_registration", "change_voucher"),
33-
("program_registration", "delete_voucher"),
34-
("program_registration", "view_voucher"),
35-
# View orders and carts (read-only for operational awareness)
36-
("program_registration", "view_order"),
37-
("program_registration", "view_orderlineitem"),
38-
("program_registration", "view_cart"),
39-
("program_registration", "view_cartitem"),
40-
("program_registration", "view_payment"),
41-
("program_registration", "view_credit"),
42-
# Activity signup management
46+
# All custom Conference permissions
47+
*[("program_conference", p) for p in _ALL_CONFERENCE_PERMS],
48+
# Programs app
49+
("program_programs", "view_activity"),
4350
("program_programs", "manage_activity"),
51+
("program_programs", "view_travel_grant"),
52+
("program_programs", "review_travel_grant"),
53+
("program_programs", "disburse_travel_grant"),
54+
("program_programs", "review_receipt"),
4455
],
45-
"Program: Registration & Ticket Support": [
46-
# View conference context
56+
"Program Committee": [
4757
("program_conference", "view_conference"),
48-
("program_conference", "view_section"),
49-
# View and manage tickets/add-ons
50-
("program_registration", "view_tickettype"),
51-
("program_registration", "change_tickettype"),
52-
("program_registration", "view_addon"),
53-
("program_registration", "change_addon"),
54-
# Voucher management (issue comps, apply discounts)
55-
("program_registration", "add_voucher"),
56-
("program_registration", "change_voucher"),
57-
("program_registration", "view_voucher"),
58-
# Cart and order support
59-
("program_registration", "view_cart"),
60-
("program_registration", "view_cartitem"),
61-
("program_registration", "view_order"),
62-
("program_registration", "change_order"),
63-
("program_registration", "view_orderlineitem"),
64-
("program_registration", "view_payment"),
65-
("program_registration", "view_credit"),
58+
("program_conference", "view_dashboard"),
59+
("program_conference", "view_program"),
60+
("program_conference", "change_program"),
61+
("program_conference", "view_overrides"),
62+
("program_conference", "change_overrides"),
6663
],
67-
"Program: Finance & Accounting": [
68-
# Read-only conference context
64+
"Registration Manager": [
6965
("program_conference", "view_conference"),
70-
# Full access to financial records
71-
("program_registration", "view_order"),
72-
("program_registration", "change_order"),
73-
("program_registration", "view_orderlineitem"),
74-
("program_registration", "view_payment"),
75-
("program_registration", "add_payment"),
76-
("program_registration", "change_payment"),
77-
("program_registration", "view_credit"),
78-
("program_registration", "add_credit"),
79-
("program_registration", "change_credit"),
80-
# View vouchers for audit trail
81-
("program_registration", "view_voucher"),
82-
# View tickets for revenue reporting
83-
("program_registration", "view_tickettype"),
84-
("program_registration", "view_addon"),
66+
("program_conference", "view_dashboard"),
67+
("program_conference", "view_registration"),
68+
("program_conference", "change_registration"),
69+
("program_conference", "view_commerce"),
70+
("program_conference", "change_commerce"),
71+
("program_conference", "view_badges"),
72+
("program_conference", "change_badges"),
73+
("program_conference", "view_checkin"),
74+
("program_conference", "use_terminal"),
75+
("program_conference", "view_bulk_purchases"),
76+
("program_conference", "change_bulk_purchases"),
8577
],
86-
"Program: Activity Organizers": [
78+
"Finance Team": [
8779
("program_conference", "view_conference"),
88-
("program_programs", "manage_activity"),
80+
("program_conference", "view_dashboard"),
81+
("program_conference", "view_finance"),
82+
("program_conference", "change_finance"),
83+
("program_conference", "view_reports"),
84+
("program_conference", "export_reports"),
85+
("program_conference", "view_registration"),
86+
("program_conference", "view_commerce"),
87+
],
88+
"Travel Grant Reviewer": [
89+
("program_conference", "view_conference"),
90+
("program_conference", "view_dashboard"),
91+
("program_programs", "view_travel_grant"),
92+
("program_programs", "review_travel_grant"),
93+
("program_programs", "review_receipt"),
94+
],
95+
"Sponsor Manager": [
96+
("program_conference", "view_conference"),
97+
("program_conference", "view_dashboard"),
98+
("program_conference", "view_sponsors"),
99+
("program_conference", "change_sponsors"),
100+
("program_conference", "view_bulk_purchases"),
101+
("program_conference", "change_bulk_purchases"),
102+
],
103+
"Check-in Staff": [
104+
("program_conference", "view_conference"),
105+
("program_conference", "view_dashboard"),
106+
("program_conference", "view_checkin"),
107+
],
108+
"Activity Organizer": [
109+
("program_conference", "view_conference"),
110+
("program_conference", "view_dashboard"),
89111
("program_programs", "view_activity"),
90-
("program_programs", "view_activitysignup"),
112+
("program_programs", "manage_activity"),
91113
],
92-
"Program: Reports": [
93-
# Read-only access for report dashboards
114+
"Reports Viewer": [
94115
("program_conference", "view_conference"),
95-
("program_registration", "view_tickettype"),
96-
("program_registration", "view_addon"),
97-
("program_registration", "view_voucher"),
98-
("program_registration", "view_order"),
99-
("program_registration", "view_orderlineitem"),
100-
("program_registration", "view_payment"),
101-
("program_registration", "view_credit"),
116+
("program_conference", "view_dashboard"),
117+
("program_conference", "view_reports"),
102118
],
103-
"Program: Read-Only Staff": [
119+
"Read-Only Staff": [
104120
("program_conference", "view_conference"),
105-
("program_conference", "view_section"),
106-
("program_registration", "view_tickettype"),
107-
("program_registration", "view_addon"),
108-
("program_registration", "view_voucher"),
109-
("program_registration", "view_cart"),
110-
("program_registration", "view_cartitem"),
111-
("program_registration", "view_order"),
112-
("program_registration", "view_orderlineitem"),
113-
("program_registration", "view_payment"),
114-
("program_registration", "view_credit"),
121+
*[("program_conference", p) for p in _ALL_VIEW_CONFERENCE_PERMS],
122+
("program_programs", "view_activity"),
123+
("program_programs", "view_travel_grant"),
115124
],
116125
}
117126

118127

119128
class Command(BaseCommand):
120129
"""Create default permission groups for conference staff roles.
121130
122-
Creates six groups with appropriate permissions:
131+
Creates ten groups with granular permissions:
123132
124-
* **Conference Organizers** -- full conference, ticket, voucher, and activity management
125-
* **Registration & Ticket Support** -- ticket ops, voucher issuing, order support
126-
* **Finance & Accounting** -- orders, payments, credits, and revenue visibility
127-
* **Reports** -- read-only access to report dashboards and underlying data
128-
* **Activity Organizers** -- per-activity signup management
129-
* **Read-Only Staff** -- view-only access to all registration models
133+
* **Conference Organizer** -- full access to all conference management
134+
* **Program Committee** -- program content and Pretalx overrides
135+
* **Registration Manager** -- attendees, orders, commerce, badges, check-in
136+
* **Finance Team** -- financial dashboard, expenses, reports, read-only commerce
137+
* **Travel Grant Reviewer** -- travel grant review and receipt approval
138+
* **Sponsor Manager** -- sponsor and bulk purchase management
139+
* **Check-in Staff** -- check-in dashboard access only
140+
* **Activity Organizer** -- activity and signup management
141+
* **Reports Viewer** -- read-only access to reports dashboard
142+
* **Read-Only Staff** -- view-only access to all sections
130143
131144
Safe to run multiple times; existing groups are updated with the defined
132145
permission set.
@@ -147,12 +160,19 @@ def handle(self, *args: Any, **options: Any) -> None:
147160
group, created = Group.objects.get_or_create(name=group_name)
148161
verb = "Created" if created else "Updated"
149162

163+
perm_set = set(perm_specs)
164+
app_labels = {app for app, _ in perm_set}
150165
permissions = Permission.objects.filter(
151-
content_type__app_label__in={app for app, _ in perm_specs},
166+
content_type__app_label__in=app_labels,
152167
)
153-
matched = [p for p in permissions if (p.content_type.app_label, p.codename) in perm_specs]
168+
matched = [p for p in permissions if (p.content_type.app_label, p.codename) in perm_set]
169+
missing = perm_set - {(p.content_type.app_label, p.codename) for p in matched}
154170
group.permissions.set(matched)
155171

172+
if missing and verbosity > 0:
173+
labels = ", ".join(f"{app}.{code}" for app, code in sorted(missing))
174+
self.stdout.write(self.style.WARNING(f" Warning: {group_name} missing permissions: {labels}"))
175+
156176
if verbosity > 0:
157177
self.stdout.write(self.style.SUCCESS(f" {verb} group '{group_name}' with {len(matched)} permissions"))
158178

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Generated by Django 5.2.11 on 2026-03-19 18:27
2+
3+
from django.db import migrations
4+
5+
6+
class Migration(migrations.Migration):
7+
dependencies = [
8+
("program_conference", "0009_featureflags_visa_letters_enabled"),
9+
]
10+
11+
operations = [
12+
migrations.AlterModelOptions(
13+
name="conference",
14+
options={
15+
"ordering": ["-start_date"],
16+
"permissions": [
17+
("view_dashboard", "Can view conference dashboard"),
18+
("manage_conference_settings", "Can edit conference settings and sync"),
19+
("view_program", "Can view program content"),
20+
("change_program", "Can edit program content"),
21+
("view_registration", "Can view attendees and orders"),
22+
("change_registration", "Can manage orders and visa letters"),
23+
("view_commerce", "Can view ticket types, add-ons, vouchers"),
24+
("change_commerce", "Can manage ticket types, add-ons, vouchers"),
25+
("view_badges", "Can view badges and templates"),
26+
("change_badges", "Can manage badges and templates"),
27+
("view_sponsors", "Can view sponsors"),
28+
("change_sponsors", "Can manage sponsors"),
29+
("view_bulk_purchases", "Can view bulk purchases"),
30+
("change_bulk_purchases", "Can manage bulk purchases"),
31+
("view_finance", "Can view financial dashboard and expenses"),
32+
("change_finance", "Can manage expenses"),
33+
("view_reports", "Can view reports and analytics"),
34+
("export_reports", "Can export report data"),
35+
("view_checkin", "Can access check-in"),
36+
("use_terminal", "Can use Terminal POS"),
37+
("view_overrides", "Can view Pretalx overrides"),
38+
("change_overrides", "Can manage Pretalx overrides"),
39+
],
40+
},
41+
),
42+
]

src/django_program/conference/models.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,30 @@ class Conference(models.Model):
9898

9999
class Meta:
100100
ordering = ["-start_date"]
101+
permissions = [
102+
("view_dashboard", "Can view conference dashboard"),
103+
("manage_conference_settings", "Can edit conference settings and sync"),
104+
("view_program", "Can view program content"),
105+
("change_program", "Can edit program content"),
106+
("view_registration", "Can view attendees and orders"),
107+
("change_registration", "Can manage orders and visa letters"),
108+
("view_commerce", "Can view ticket types, add-ons, vouchers"),
109+
("change_commerce", "Can manage ticket types, add-ons, vouchers"),
110+
("view_badges", "Can view badges and templates"),
111+
("change_badges", "Can manage badges and templates"),
112+
("view_sponsors", "Can view sponsors"),
113+
("change_sponsors", "Can manage sponsors"),
114+
("view_bulk_purchases", "Can view bulk purchases"),
115+
("change_bulk_purchases", "Can manage bulk purchases"),
116+
("view_finance", "Can view financial dashboard and expenses"),
117+
("change_finance", "Can manage expenses"),
118+
("view_reports", "Can view reports and analytics"),
119+
("export_reports", "Can export report data"),
120+
("view_checkin", "Can access check-in"),
121+
("use_terminal", "Can use Terminal POS"),
122+
("view_overrides", "Can view Pretalx overrides"),
123+
("change_overrides", "Can manage Pretalx overrides"),
124+
]
101125

102126
def __str__(self) -> str:
103127
return self.name

0 commit comments

Comments
 (0)