Permissions and RBAC¶
XC_VM access control combines:
- Group permissions -- allowed capabilities assigned to an admin group
- Object-level authorization -- ownership checks for specific entities (users, lines)
- Page-level authorization -- route/page gating in admin and reseller panels
Model¶
user -> member_group_id -> group
-> is_admin (boolean)
-> is_reseller (boolean)
-> advanced[] (array of permission keys)
Permission state is loaded into the $rPermissions global during session initialization and remains available throughout the request lifecycle.
Key fields in $rPermissions:
| Field | Type | Description |
|---|---|---|
is_admin |
bool | Whether the user is an admin |
advanced |
array | List of granted permission key strings |
all_reports |
array | Reseller report tree (user IDs this reseller manages) |
create_line |
bool | Reseller: can create lines |
create_sub_resellers |
bool | Reseller: can create sub-resellers |
create_mag |
bool | Reseller: can create MAG devices |
create_enigma |
bool | Reseller: can create Enigma devices |
can_view_vod |
bool | Reseller: can view VOD/streams content |
reseller_client_connection_logs |
bool | Reseller: can view connection logs |
Permission Keys¶
Permission keys are declared in src/config/permissions.php as the $rPermissionKeys array. Each key is a string identifier used with Authorization::check('adv', $key).
Categories:
| Category | Examples |
|---|---|
| Create/Add | add_stream, add_movie, add_user, add_server, add_bouquet, add_epg, add_code, add_hmac, add_rtmp |
| Edit | edit_stream, edit_movie, edit_user, edit_server, edit_bouquet, edit_series, edit_reguser |
| Mass operations | mass_edit_streams, mass_edit_lines, mass_edit_mags, mass_edit_enigmas, mass_edit_radio, mass_edit_users, mass_sedits, mass_sedits_vod, mass_delete |
| Import | import_streams, import_movies, import_episodes |
| Security/Blocking | block_ips, block_isps, block_uas, block_asns, fingerprint |
| Section visibility | streams, movies, series, episodes, radio, users, servers, bouquets, epg, settings, database |
| Logs | connection_logs, live_connections, client_request_log, credits_log, login_logs, panel_logs, reg_userlog, restream_logs |
| Tools | quick_tools, stream_tools, process_monitor, stream_errors |
| Management | mng_regusers, mng_groups, mng_packages, manage_mag, manage_e2, manage_events, manage_tickets |
| Other | categories, channel_order, player, tprofile, tprofiles, rtmp, folder_watch, folder_watch_add, folder_watch_output, folder_watch_settings, ticket, add_code, add_hmac |
Authorization Classes¶
Authorization¶
File: src/Core/Auth/Authorization.php
Primary method:
Preconditions: Returns false immediately if $rUserInfo, $rPermissions, or $db are not initialized.
Type: user¶
Checks whether the current user can access a target admin user. Builds a list from the current user's ID plus their all_reports tree, then queries the users table to verify the target user's owner_id is in that list (or the target is the current user).
Type: line¶
Checks whether the current user can access a target line. Same report-tree approach -- queries the lines table to verify the target line's member_id is in the current user's report tree.
Type: adv¶
Checks whether the current admin user has a specific advanced permission key.
Important: is_admin gate. Before checking the advanced permissions array, the method requires $rPermissions['is_admin'] to be true. If the user is not an admin, check('adv', ...) always returns false:
This means adv checks are exclusively for admin users. Reseller permissions use a separate system (see below).
Super admin bypass¶
member_group_id = 1 is the super admin group. When the advanced permissions array is non-empty but the user belongs to group 1, the per-key check is skipped and the method returns true:
if (0 < count($rPermissions['advanced']) && $rUserInfo['member_group_id'] != 1) {
return in_array($rID, $rPermissions['advanced']);
}
return true;
This means super admins pass all adv checks regardless of which keys are assigned to their group.
Reseller helper¶
Returns whether $rPermissions[$type] is non-empty. Used for reseller-specific boolean flags like create_line, create_mag, etc.
PageAuthorization¶
File: src/Core/Auth/PageAuthorization.php
Provides page-level gating for admin and reseller panels. Called during request dispatch to determine whether the current user can access a given page.
PageAuthorization::checkPermissions(?string $page = null): bool
PageAuthorization::checkResellerPermissions(?string $page = null): bool
If $page is omitted, the page name is inferred from SCRIPT_FILENAME (basename without .php extension, lowercased).
Default-allow behavior¶
Both methods return true for any page not explicitly listed in their switch statements. This means pages without a mapping are accessible to all authorized users of the appropriate type (admin or reseller). Only pages with explicit entries are restricted.
Admin Page Permission Mappings¶
The checkPermissions() method maps admin panel pages to adv permission keys. The complete mapping is listed below, grouped by category.
Create-vs-edit pattern¶
Many entity pages use conditional logic based on request parameters:
- If an
idparameter is present, the edit permission is checked - If no
idparameter is present, the add permission is checked - Some pages (stream, movie) also check for an
importparameter and require the corresponding import permission
When neither condition is met, behavior depends on the page: some fall through to a related listing permission, others fall through to the switch default (which returns true).
Streams and Content¶
| Page | Permission | Notes |
|---|---|---|
streams, stream_view, provider, providers, epg_view, created_channels, stream_rank, archive |
streams |
|
stream |
edit_stream |
When id is present |
stream |
add_stream |
When no id |
stream |
import_streams |
When import param is present (in addition to add_stream) |
stream_categories |
categories |
|
stream_category |
add_cat |
|
stream_errors |
stream_errors |
|
stream_mass, created_channel_mass |
mass_edit_streams |
|
mass_edit_streams |
edit_stream |
|
review |
import_streams |
|
channel_order |
channel_order |
|
created_channel |
edit_cchannel |
When id is present |
created_channel |
create_channel |
When no id |
Movies and VOD¶
| Page | Permission | Notes |
|---|---|---|
movies |
movies |
|
movie |
edit_movie |
When id is present |
movie |
add_movie |
When no id |
movie |
import_movies |
When import param is present (in addition to add_movie) |
movie_mass |
mass_sedits_vod |
|
record |
add_movie |
|
recordings |
movies |
Series and Episodes¶
| Page | Permission | Notes |
|---|---|---|
series |
series |
|
serie |
edit_series |
When id is present |
serie |
add_series |
When no id |
series_order |
edit_series |
|
episodes |
episodes |
|
episode |
edit_episode |
When id is present |
episode |
add_episode |
When no id; falls through to episodes on denial |
series_mass, episodes_mass |
mass_sedits |
Radio¶
| Page | Permission | Notes |
|---|---|---|
radios |
radio |
|
radio |
edit_radio |
When id is present |
radio |
add_radio |
When no id |
radio_mass |
mass_edit_radio |
Lines (Subscriber Users)¶
| Page | Permission | Notes |
|---|---|---|
lines |
users |
|
line |
edit_user |
When id is present |
line |
add_user |
When no id |
line_mass |
mass_edit_lines |
|
line_activity, theft_detection, line_ips |
connection_logs |
|
live_connections |
live_connections |
MAG and Enigma Devices¶
| Page | Permission | Notes |
|---|---|---|
mags |
manage_mag |
|
mag |
edit_mag |
When id is present |
mag |
add_mag |
When no id |
mag_events |
manage_events |
|
mag_mass |
mass_edit_mags |
|
enigmas |
manage_e2 |
|
enigma_mass |
mass_edit_enigmas |
Admin Users (Registered Users)¶
| Page | Permission | Notes |
|---|---|---|
users |
mng_regusers |
|
user |
edit_reguser |
When id is present |
user |
add_reguser |
When no id |
user_mass |
mass_edit_users |
|
user_logs |
reg_userlog |
Bouquets and Packages¶
| Page | Permission | Notes |
|---|---|---|
bouquets |
bouquets |
|
bouquet |
edit_bouquet |
When id is present |
bouquet |
add_bouquet |
When no id; falls through to edit_bouquet on denial |
bouquet_order, bouquet_sort |
edit_bouquet |
|
packages, addons |
mng_packages |
|
package |
edit_package |
When id is present |
package |
add_packages |
When no id |
Groups¶
| Page | Permission | Notes |
|---|---|---|
groups |
mng_groups |
|
group |
edit_group |
When id is present |
group |
add_group |
When no id; falls through to mng_groups on denial |
EPG¶
| Page | Permission | Notes |
|---|---|---|
epgs |
epg |
|
epg |
epg_edit |
When id is present |
epg |
add_epg |
When no id; falls through to epg on denial |
Servers¶
| Page | Permission | Notes |
|---|---|---|
servers, server_view, server_order, proxies |
servers |
|
server, proxy |
edit_server |
When id is present |
server, proxy |
add_server |
When no id |
server_install |
add_server |
Security and Blocking¶
| Page | Permission | Notes |
|---|---|---|
isps, isp, asns |
block_isps |
|
ip, ips |
block_ips |
|
useragents, useragent |
block_uas |
|
fingerprint |
fingerprint |
Tickets¶
| Page | Permission | Notes |
|---|---|---|
ticket |
ticket |
|
ticket_view, tickets |
manage_tickets |
Tools and Settings¶
| Page | Permission | Notes |
|---|---|---|
settings |
settings |
|
backups, cache, setup |
database |
|
settings_watch, settings_plex |
folder_watch_settings |
|
plex, watch |
folder_watch |
|
plex_add, watch_add |
folder_watch_add |
|
watch_output |
folder_watch_output |
|
mass_delete |
mass_delete |
|
quick_tools |
quick_tools |
|
stream_tools |
stream_tools |
|
process_monitor |
process_monitor |
|
queue |
streams OR episodes OR series |
Access if user has any one of these |
Profiles and Codes¶
| Page | Permission | Notes |
|---|---|---|
profiles |
tprofiles |
|
profile |
tprofile |
|
player |
player |
|
code, codes |
add_code |
|
hmac, hmacs |
add_hmac |
RTMP¶
| Page | Permission | Notes |
|---|---|---|
rtmp_ip |
add_rtmp |
|
rtmp_ips, rtmp_monitor |
rtmp |
Logs¶
| Page | Permission | Notes |
|---|---|---|
client_logs |
client_request_log |
|
credit_logs |
credits_log |
|
mysql_syslog, panel_logs |
panel_logs |
|
login_logs |
login_logs |
|
restream_logs |
restream_logs |
Reseller Page Permission Mappings¶
The checkResellerPermissions() method maps reseller panel pages to boolean flags in $rPermissions. Unlike admin permissions which use the advanced array via Authorization::check('adv', ...), reseller permissions are simple boolean fields checked directly.
| Pages | Required permission |
|---|---|
user, users |
create_sub_resellers |
line, lines |
create_line |
mag, mags |
create_mag |
enigma, enigmas |
create_enigma |
epg_view, streams, created_channels, movies, episodes, radios |
can_view_vod |
live_connections, line_activity |
reseller_client_connection_logs |
Any reseller page not listed above returns true (accessible by default).
Adding a New Permission¶
- Add the key to
src/config/permissions.php:
- Use it in code via
Authorization::check():
- If the permission should gate a page, add a case to
PageAuthorization::checkPermissions():
- For create/edit entity pages, use the conditional pattern:
case 'my_entity':
if (isset(RequestManager::getAll()['id']) && Authorization::check('adv', 'edit_my_entity')) {
return true;
}
if (isset(RequestManager::getAll()['id']) || !Authorization::check('adv', 'add_my_entity')) {
break;
}
return true;
- For reseller permissions, add a boolean field to
$rPermissionsand a case tocheckResellerPermissions().
Related files¶
| File | Purpose |
|---|---|
src/config/permissions.php |
Permission key registry ($rPermissionKeys array) |
src/Core/Auth/Authorization.php |
Object-level and advanced permission checks |
src/Core/Auth/PageAuthorization.php |
Page-level gating for admin and reseller panels |
src/Core/Auth/SessionManager.php |
Session context; populates $rPermissions and $rUserInfo |
src/Core/Auth/Authenticator.php |
Authentication (login, credential verification) |