Skip to content

Permissions & Roles

ProxPanel has a two-axis access control system: a user_type enum + an optional permission_group.

IDNameIntended use
1SubscriberAn end customer logging into the customer portal. Different auth path.
2ResellerA billing partner managing their own subscribers.
3SupportReseller staff handling tickets, lookups, basic diagnostics.
4AdminSystem-wide administrator. Bypasses all permission checks.
5CollectorField agent / cashier who marks invoices paid.
6ReadonlyView-only access for auditors, managers, partners.

user_type = 4 (Admin) always has every permission. The middleware does:

if user.UserType == models.UserTypeAdmin {
return next() // no permission check
}

Permission groups assigned to an admin are silently ignored.

If a reseller has permission_group = NULL, they get full access to everything (no permission check applied). This dates from when ProxPanel didn’t have permission groups at all.

To restrict a reseller, you must assign them a permission group with the specific subset of permissions you want them to have.

For Support, Collector, Readonly: permission_group = NULL means no access (everything is denied). You must assign them a permission group.

Permissions are dotted names like subscribers.view, subscribers.edit, nas.delete, invoices.view_all. Categories:

PrefixCountExamples
dashboard.*4view, stats, view_admin
subscribers.*~50view, create, edit, delete, renew, reset_fup, view_all, change_owner, etc.
services.*4view, create, edit, delete
nas.*6view, create, edit, delete, sync, test
sessions.*4view, view_all, disconnect, view_history
resellers.*17view, create, edit, add_money, withdraw, etc.
invoices.*7view, create, edit, delete, print, email, mark_paid
prepaid.*9view, create, generate, delete, etc.
reports.*8revenue, subscribers, churn, etc.
transactions.*4view, view_all, create, delete
tickets.*7view, create, reply, close, assign, etc.
backups.*6view, create, restore, delete, download
bandwidth.*, cdn.*, fup.*, sharing.*, communication.*, audit.*, users.*, permissions.*, settings.*restsimilar shape

See the Permission List for the complete table.

For data-scoped categories (subscribers, sessions, invoices, transactions, reports, etc.), there’s a paired *.view_all permission that bypasses the reseller hierarchy filter.

Without subscribers.view_all, a reseller’s user only sees subscribers in their own tree. With it, they see everyone — same view as admin. This is the “global read-only partner” pattern.

The pages that honor *.view_all:

  • subscribers.view_all → Subscribers, FUP Counters, Reports
  • sessions.view_all → Sessions list
  • dashboard.* (admin) → Dashboard stats (since v1.0.549)
  • invoices.view_all → Invoices list
  • tickets.view_all → Tickets
  • transactions.view_all → Transactions

Not all handlers honor view_all yet. Audit ongoing — see Audit history.

  1. JWT validated. Claims extracted: user_id, user_type, reseller_id, permission_group.
  2. If user_type == Admin → allow.
  3. If permission_group == NULL and user_type == Reseller → allow (backwards-compat).
  4. Otherwise, query permission_group_permissions JOIN permissions to check if the required permission is in the group.
  5. If yes → allow. If no → return 403.
  6. Data-scope filters apply next: if the handler reads subscriber rows, scope to WHERE reseller_id IN (own + sub-resellers) unless caller has subscribers.view_all.

When an admin reassigns a permission group:

  • Active user’s JWT keeps working (no logout needed).
  • On the next request, the middleware does a fresh permission query → new permissions take effect immediately.
  • The frontend permission list refreshes on page reload (/api/auth/me re-fetched).

So: ask the user to refresh the page (F5), no logout / re-login required.