Skip to content
Open
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
9 changes: 5 additions & 4 deletions include/cms_users.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

// Execution of queries in cms_users_page.php
$cms_users_lower_level_query = '
SELECT u.*, NOT u.is_admin AS visible, g.label AS usergroup, 0 AS preventdelete, 0 as disableifistrue
SELECT u.*, NOT u.is_admin AS visible, g.label AS usergroup, 0 AS preventdelete, 0 as disableifistrue, 0 AS preventedit
FROM cms_users AS u
LEFT OUTER JOIN cms_usergroups AS g ON g.id = u.cms_usergroups_id
LEFT OUTER JOIN cms_usergroups_camps AS uc ON uc.cms_usergroups_id = g.id
Expand All @@ -46,13 +46,14 @@
// Do not forget to specify :userGroupLevel and :user in the db call later
// related to this trello card https://trello.com/c/KI47eGPI
$cms_users_same_or_upper_level_query = '
SELECT u.*, 0 AS visible, g.label AS usergroup, 1 AS preventdelete, 1 as disableifistrue
SELECT u.*, IF(u.id = :user, 1, 0) AS visible, g.label AS usergroup, 1 AS preventdelete, 1 as disableifistrue, IF(u.id = :user, 0, 1) AS preventedit
FROM cms_users AS u
INNER JOIN cms_usergroups AS g ON g.id = u.cms_usergroups_id
INNER JOIN cms_usergroups_camps AS uc ON uc.cms_usergroups_id = g.id
INNER JOIN cms_usergroups_levels AS l ON l.id = g.userlevel
WHERE (l.level >= :userGroupLevel AND u.id != :user)
AND uc.camp_id IN ('.($_SESSION['camp']['id'] ?: 0).')
WHERE l.level >= :userGroupLevel
AND (u.id != :user OR :userGroupLevel = 100)
AND uc.camp_id IN ('.(intval($_SESSION['camp']['id']) ?: 0).')
AND NOT (u.valid_lastday < CURDATE() AND UNIX_TIMESTAMP(u.valid_lastday) != 0)
AND UNIX_TIMESTAMP(u.deleted) = 0
GROUP BY u.id
Expand Down
4 changes: 2 additions & 2 deletions include/cms_users_deactivated.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
);

// Execution of queries in cms_users_page.php
$cms_users_lower_level_query = 'SELECT u.id, u.naam, SUBSTR(u.email, 1, LENGTH(u.email)-LENGTH(".deleted.")-LENGTH(u.id)) AS email, u.valid_firstday, u.valid_lastday, NOT u.is_admin AS visible, g.label AS usergroup, 0 AS preventdelete, 1 as disableifistrue
$cms_users_lower_level_query = 'SELECT u.id, u.naam, SUBSTR(u.email, 1, LENGTH(u.email)-LENGTH(".deleted.")-LENGTH(u.id)) AS email, u.valid_firstday, u.valid_lastday, NOT u.is_admin AS visible, g.label AS usergroup, 0 AS preventdelete, 1 as disableifistrue, 0 as preventedit
FROM cms_users AS u
LEFT OUTER JOIN cms_usergroups AS g ON g.id = u.cms_usergroups_id
LEFT OUTER JOIN cms_usergroups_camps AS uc ON uc.cms_usergroups_id = g.id
Expand All @@ -40,7 +40,7 @@

// Do not forget to specify :usergroup and :user in the db call later
$cms_users_same_level_query = '
SELECT u.id, u.naam, SUBSTR(u.email, 1, LENGTH(u.email)-LENGTH(".deleted.")-LENGTH(u.id)) AS email, u.valid_firstday, u.valid_lastday, 0 AS visible, g.label AS usergroup, 1 AS preventdelete, 1 as disableifistrue
SELECT u.id, u.naam, SUBSTR(u.email, 1, LENGTH(u.email)-LENGTH(".deleted.")-LENGTH(u.id)) AS email, u.valid_firstday, u.valid_lastday, 0 AS visible, g.label AS usergroup, 1 AS preventdelete, 1 as disableifistrue, 1 AS preventedit
FROM cms_users AS u
LEFT OUTER JOIN cms_usergroups AS g ON g.id = u.cms_usergroups_id
WHERE u.cms_usergroups_id = :usergroup
Expand Down
49 changes: 42 additions & 7 deletions include/cms_users_edit.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,37 @@
WHERE ug.id = :id AND (NOT ug.deleted OR ug.deleted IS NULL)', ['id' => $_POST['cms_usergroups_id'][0]]);
$is_admin = $_SESSION['user']['is_admin'];
$organisation_allowed = ($_SESSION['organisation']['id'] == $posteduser['organisation_id']);
// allow admins to create another admin account
// allow HoO to create another HoO account
// related to this trello card https://trello.com/c/YAF3Az4P
$userlevel_allowed = ($_SESSION['usergroup']['userlevel'] > $posteduser['userlevel']) || ($_SESSION['usergroup']['userlevel'] == $posteduser['userlevel'] && '100' == $_SESSION['usergroup']['userlevel']);

// Prevent HoO user from downgrading their usergroup if they're the only HoO
if (!$is_admin
&& $_POST['id'] == $_SESSION['user']['id']
&& 100 == $_SESSION['usergroup']['userlevel']
&& $posteduser['userlevel'] < $_SESSION['usergroup']['userlevel']) {
// Count how many HoO users exist in this organization
$hoo_count = db_value(
'
SELECT COUNT(DISTINCT u.id)
FROM cms_users AS u
LEFT JOIN cms_usergroups AS ug ON ug.id = u.cms_usergroups_id
LEFT JOIN cms_usergroups_levels AS ugl ON ugl.id = ug.userlevel
WHERE ug.organisation_id = :org_id
AND ugl.level = 100
AND (NOT u.deleted OR u.deleted IS NULL)
AND (NOT ug.deleted OR ug.deleted IS NULL)
AND NOT (u.valid_lastday < CURDATE() AND UNIX_TIMESTAMP(u.valid_lastday) != 0)',
['org_id' => $_SESSION['organisation']['id']]
);

// If this is the last HoO, prevent the change
if ($hoo_count <= 1) {
trigger_error('You cannot downgrade yourself. Your organisation must have at least one Head of Operations user.', E_USER_NOTICE);
redirect('?action=cms_users_edit&id='.$_POST['id'].'&origin='.$_POST['_origin'].'&warning=1&message=You cannot downgrade yourself. Your organisation must have at least one Head of Operations user.');
}
}

if ($is_admin || ($organisation_allowed && $userlevel_allowed)) {
$keys = ['naam', 'email', 'cms_usergroups_id', 'valid_firstday', 'valid_lastday'];
$userId = db_transaction(function () use ($table, $keys, $userId) {
Expand Down Expand Up @@ -114,7 +141,12 @@
FROM cms_usergroups AS ug
LEFT OUTER JOIN cms_usergroups_levels AS ugl ON ugl.id=ug.userlevel
WHERE ug.id = :id AND (NOT ug.deleted OR ug.deleted IS NULL)', ['id' => $data['cms_usergroups_id']]);
if (!$_SESSION['user']['is_admin'] && ($data && ($data['is_admin'] || ($_SESSION['organisation']['id'] != $requesteduser['organisation_id']) || ($_SESSION['usergroup']['userlevel'] <= $requesteduser['userlevel'])))) {
if (!$_SESSION['user']['is_admin']
&& $data
&& $data['id'] != $_SESSION['user']['id']
&& ($data['is_admin']
|| $_SESSION['organisation']['id'] != $requesteduser['organisation_id']
|| $_SESSION['usergroup']['userlevel'] <= $requesteduser['userlevel'])) {
throw new Exception('You do not have access to this user!', 403);
Comment on lines +144 to 150
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This access check now guards on $data, but $data['cms_usergroups_id'] is still dereferenced earlier when building $requesteduser. If an invalid/non-existent id is requested, db_row() returns false and the earlier dereference will raise a PHP warning before this guard runs. Consider handling the “user not found” case immediately after loading $data (before using $data[...]).

Copilot uses AI. Check for mistakes.
}

Expand All @@ -130,11 +162,14 @@
// display admin role in the usergroup - only for user with admin roles
// related to this trello card https://trello.com/c/YAF3Az4P
$usergroups = db_array('
SELECT ug.id AS value, ug.label
FROM cms_usergroups AS ug
LEFT OUTER JOIN cms_usergroups_levels AS ugl ON (ugl.id=ug.userlevel)
WHERE ug.organisation_id = :organisation_id AND (ugl.level < :userlevel OR :is_admin OR (ugl.level <= :userlevel AND 100 = :userlevel)) AND (NOT ug.deleted OR ug.deleted IS NULL)
ORDER BY ug.label', ['organisation_id' => $_SESSION['organisation']['id'], 'userlevel' => $_SESSION['usergroup']['userlevel'], 'is_admin' => $_SESSION['user']['is_admin']]);
SELECT ug.id AS value, ug.label
FROM cms_usergroups AS ug
LEFT OUTER JOIN cms_usergroups_levels AS ugl ON (ugl.id=ug.userlevel)
WHERE ug.organisation_id = :organisation_id
AND (ugl.level < :userlevel OR (ugl.level <= :userlevel AND 100 = :userlevel))
AND (:is_admin OR ug.label != "Boxtribute God")
AND (NOT ug.deleted OR ug.deleted IS NULL)
ORDER BY ug.label', ['organisation_id' => $_SESSION['organisation']['id'], 'userlevel' => $_SESSION['usergroup']['userlevel'], 'is_admin' => $_SESSION['user']['is_admin']]);
addfield('select', 'Select user group', 'cms_usergroups_id', ['required' => true, 'options' => $usergroups, 'testid' => 'user_group']);

addfield('line');
Expand Down
4 changes: 2 additions & 2 deletions include/cms_users_expired.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
);

// Execution of queries in cms_users_page.php
$cms_users_lower_level_query = 'SELECT u.*, NOT u.is_admin AS visible, g.label AS usergroup, 0 AS preventdelete, 1 as disableifistrue
$cms_users_lower_level_query = 'SELECT u.*, NOT u.is_admin AS visible, g.label AS usergroup, 0 AS preventdelete, 1 as disableifistrue, 0 AS preventedit
FROM cms_users AS u
LEFT OUTER JOIN cms_usergroups AS g ON g.id = u.cms_usergroups_id
LEFT OUTER JOIN cms_usergroups_camps AS uc ON uc.cms_usergroups_id = g.id
Expand All @@ -46,7 +46,7 @@

// Do not forget to specify :usergroup and :user in the db call later
$cms_users_same_level_query = '
SELECT u.*, 0 AS visible, g.label AS usergroup, 1 AS preventdelete, 1 as disableifistrue
SELECT u.*, 0 AS visible, g.label AS usergroup, 1 AS preventdelete, 1 as disableifistrue, 1 AS preventedit
FROM cms_users AS u
LEFT OUTER JOIN cms_usergroups AS g ON g.id = u.cms_usergroups_id
WHERE u.cms_usergroups_id = :usergroup
Expand Down