|
56 | 56 | */ |
57 | 57 |
|
58 | 58 | /** @var bool $canListAdmins */ |
59 | | -$canListAdmins = $userbank->HasAccess(WebPermission::mask(WebPermission::Owner, WebPermission::ListAdmins)); |
| 59 | +$canListAdmins = $userbank->HasAccess(WebPermission::mask( |
| 60 | + WebPermission::Owner, |
| 61 | + WebPermission::ListAdmins, |
| 62 | + WebPermission::EditAdmins, |
| 63 | + WebPermission::DeleteAdmins, |
| 64 | +)); |
60 | 65 | /** @var bool $canAddAdmins */ |
61 | 66 | $canAddAdmins = $userbank->HasAccess(WebPermission::mask(WebPermission::Owner, WebPermission::AddAdmins)); |
62 | 67 | /** @var bool $canEditAdmins */ |
|
79 | 84 | [ |
80 | 85 | 'slug' => 'admins', |
81 | 86 | 'name' => 'Admins', |
82 | | - 'permission' => ADMIN_OWNER | ADMIN_LIST_ADMINS, |
| 87 | + 'permission' => ADMIN_OWNER | ADMIN_LIST_ADMINS | ADMIN_EDIT_ADMINS | ADMIN_DELETE_ADMINS, |
83 | 88 | 'url' => 'index.php?p=admin&c=admins§ion=admins', |
84 | 89 | 'icon' => 'users', |
85 | 90 | ], |
|
195 | 200 | * combined filter form and AND the populated filters server-side. |
196 | 201 | * |
197 | 202 | * The new wire format reads each filter from its own query parameter |
198 | | - * (`name`, `steamid`, `steam_match`, `admemail`, `webgroup`, |
| 203 | + * (`name`, `steamid`, `admemail`, `webgroup`, |
199 | 204 | * `srvadmgroup`, `srvgroup`, `admwebflag[]`, `admsrvflag[]`, `server`) |
200 | | - * so a single GET submit carries the full filter snapshot. URL-shareable |
201 | | - * searches are preserved by the legacy-shim block below: any incoming |
202 | | - * `?advType=…&advSearch=…` is translated into the new shape so old |
| 205 | + * so a single GET submit carries the full filter snapshot. Text filters |
| 206 | + * always substring-match (`LIKE %…%`). URL-shareable searches are |
| 207 | + * preserved by the legacy-shim block below: any incoming |
| 208 | + * `?advType=…&advSearch=…` is translated into the modern shape so old |
203 | 209 | * bookmarks and cross-page links keep working. |
204 | 210 | * |
205 | 211 | * Server-side filters are AND-combined: a request with two non-empty |
|
238 | 244 | case 'steam': |
239 | 245 | // The legacy form distinguished exact (`steamid`) from |
240 | 246 | // partial (`steam`) matches as two distinct advTypes. The |
241 | | - // modern form folds both onto `steamid` + `steam_match`. |
| 247 | + // modern form folds both onto `steamid` (always partial). |
242 | 248 | if (!isset($_GET['steamid']) || $_GET['steamid'] === '') { |
243 | | - $_GET['steamid'] = $legacyValue; |
244 | | - $_GET['steam_match'] = '1'; |
| 249 | + $_GET['steamid'] = $legacyValue; |
245 | 250 | } |
246 | 251 | break; |
247 | 252 | case 'admwebflag': |
|
253 | 258 | } |
254 | 259 | } |
255 | 260 |
|
256 | | -// 1) Login name (exact or partial against ADM.user). |
257 | | -// `name_match` was added in #1231; default is partial ('1') so |
258 | | -// pre-#1231 URLs (`?name=alice` with no name_match) keep their |
259 | | -// substring semantics. `0` flips to exact. |
| 261 | +// 1) Login name (partial against ADM.user). |
260 | 262 | if (!empty($_GET['name']) && is_string($_GET['name'])) { |
261 | | - $partialName = !isset($_GET['name_match']) || (string) $_GET['name_match'] !== '0'; |
262 | | - if ($partialName) { |
263 | | - $where .= " AND ADM.user LIKE ?"; |
264 | | - $whereParams[] = '%' . $_GET['name'] . '%'; |
265 | | - } else { |
266 | | - $where .= " AND ADM.user = ?"; |
267 | | - $whereParams[] = $_GET['name']; |
268 | | - } |
269 | | - $activeFilters['name'] = (string) $_GET['name']; |
270 | | - $activeFilters['name_match'] = $partialName ? '1' : '0'; |
| 263 | + $where .= " AND ADM.user LIKE ?"; |
| 264 | + $whereParams[] = '%' . $_GET['name'] . '%'; |
| 265 | + $activeFilters['name'] = (string) $_GET['name']; |
271 | 266 | } |
272 | 267 |
|
273 | | -// 2) Steam ID (exact or partial against ADM.authid). |
| 268 | +// 2) Steam ID (partial against ADM.authid). |
274 | 269 | if (!empty($_GET['steamid']) && is_string($_GET['steamid'])) { |
275 | | - $partial = isset($_GET['steam_match']) && (string) $_GET['steam_match'] === '1'; |
276 | | - if ($partial) { |
277 | | - $where .= " AND ADM.authid LIKE ?"; |
278 | | - $whereParams[] = '%' . $_GET['steamid'] . '%'; |
279 | | - } else { |
280 | | - $where .= " AND ADM.authid = ?"; |
281 | | - $whereParams[] = $_GET['steamid']; |
282 | | - } |
283 | | - $activeFilters['steamid'] = (string) $_GET['steamid']; |
284 | | - $activeFilters['steam_match'] = $partial ? '1' : '0'; |
| 270 | + $where .= " AND ADM.authid LIKE ?"; |
| 271 | + $whereParams[] = '%' . $_GET['steamid'] . '%'; |
| 272 | + $activeFilters['steamid'] = (string) $_GET['steamid']; |
285 | 273 | } |
286 | 274 |
|
287 | | -// 3) E-mail (exact or partial; `admemail_match` was added in #1231, |
288 | | -// same default-partial shape as `name_match`). Gated on the same |
289 | | -// flag the search box gates the input field on so URL forgery |
290 | | -// can't bypass the visibility gate. |
| 275 | +// 3) E-mail (partial). Gated on the same flag the search box gates |
| 276 | +// the input field on so URL forgery can't bypass the visibility gate. |
291 | 277 | if (!empty($_GET['admemail']) && is_string($_GET['admemail']) && $userbank->HasAccess(WebPermission::mask(WebPermission::Owner, WebPermission::EditAdmins))) { |
292 | | - $partialEmail = !isset($_GET['admemail_match']) || (string) $_GET['admemail_match'] !== '0'; |
293 | | - if ($partialEmail) { |
294 | | - $where .= " AND ADM.email LIKE ?"; |
295 | | - $whereParams[] = '%' . $_GET['admemail'] . '%'; |
296 | | - } else { |
297 | | - $where .= " AND ADM.email = ?"; |
298 | | - $whereParams[] = $_GET['admemail']; |
299 | | - } |
300 | | - $activeFilters['admemail'] = (string) $_GET['admemail']; |
301 | | - $activeFilters['admemail_match'] = $partialEmail ? '1' : '0'; |
| 278 | + $where .= " AND ADM.email LIKE ?"; |
| 279 | + $whereParams[] = '%' . $_GET['admemail'] . '%'; |
| 280 | + $activeFilters['admemail'] = (string) $_GET['admemail']; |
302 | 281 | } |
303 | 282 |
|
304 | 283 | // 4) Web group (`:prefix_groups.gid` -> `:prefix_admins.gid`). |
|
360 | 339 | } |
361 | 340 | } |
362 | 341 |
|
363 | | -// 8) Server permission flags (multi). |
| 342 | +// 8) Server permission flags (multi). SM_* constants are single-char |
| 343 | +// strings (`SM_ROOT` = `z`); pass them to HasAccess as strings so the |
| 344 | +// srv_flags path runs. SM_ROOT implies every other server flag. |
364 | 345 | $rawSrvFlags = $_GET['admsrvflag'] ?? null; |
365 | 346 | if (is_string($rawSrvFlags)) { |
366 | 347 | $rawSrvFlags = explode(',', $rawSrvFlags); |
|
369 | 350 | /** @var list<string> $srvFlagNames */ |
370 | 351 | $srvFlagNames = []; |
371 | 352 | foreach ($rawSrvFlags as $candidate) { |
372 | | - if (is_string($candidate) && preg_match('/^SM_[A-Z_]+$/', $candidate) && defined($candidate)) { |
| 353 | + if (is_string($candidate) && preg_match('/^SM_[A-Z0-9_]+$/', $candidate) && defined($candidate)) { |
373 | 354 | $srvFlagNames[] = $candidate; |
374 | 355 | } |
375 | 356 | } |
376 | 357 | if (!empty($srvFlagNames)) { |
377 | | - $flagBits = array_map(fn(string $name): int => (int) constant($name), $srvFlagNames); |
378 | | - $alladmins = $GLOBALS['PDO']->query("SELECT aid, authid FROM `:prefix_admins` WHERE aid > 0")->resultset(); |
| 358 | + /** @var list<string> $flagChars */ |
| 359 | + $flagChars = array_map(fn(string $name): string => (string) constant($name), $srvFlagNames); |
| 360 | + $alladmins = $GLOBALS['PDO']->query("SELECT aid FROM `:prefix_admins` WHERE aid > 0")->resultset(); |
379 | 361 | $accessAids = []; |
380 | 362 | foreach ($alladmins as $row) { |
| 363 | + $aid = (int) $row['aid']; |
381 | 364 | $matched = false; |
382 | | - foreach ($flagBits as $fla) { |
383 | | - if ($userbank->HasAccess($fla, $row['authid'])) { |
| 365 | + foreach ($flagChars as $fla) { |
| 366 | + if ($userbank->HasAccess($fla, $aid)) { |
384 | 367 | $matched = true; |
385 | 368 | break; |
386 | 369 | } |
387 | 370 | } |
388 | | - if (!$matched && $userbank->HasAccess(SM_ROOT, $row['authid'])) { |
| 371 | + if (!$matched && $userbank->HasAccess(SM_ROOT, $aid)) { |
389 | 372 | $matched = true; |
390 | 373 | } |
391 | 374 | if ($matched) { |
392 | | - $accessAids[] = (int) $row['aid']; |
| 375 | + $accessAids[] = $aid; |
393 | 376 | } |
394 | 377 | } |
395 | 378 | if (empty($accessAids)) { |
|
470 | 453 | $admin['web_group'] = $userbank->GetProperty("group_name", $admin['aid']); |
471 | 454 | $admin['server_group'] = $userbank->GetProperty("srv_groups", $admin['aid']); |
472 | 455 | if (empty($admin['web_group']) || $admin['web_group'] == " ") { |
473 | | - $admin['web_group'] = "No Group/Individual Permissions"; |
| 456 | + $admin['web_group'] = "No groups"; |
474 | 457 | } |
475 | 458 | if (empty($admin['server_group']) || $admin['server_group'] == " ") { |
476 | | - $admin['server_group'] = "No Group/Individual Permissions"; |
| 459 | + $admin['server_group'] = "No groups"; |
477 | 460 | } |
478 | 461 | $GLOBALS['PDO']->query("SELECT count(authid) AS num FROM `:prefix_bans` WHERE aid = :aid"); |
479 | 462 | $GLOBALS['PDO']->bind(':aid', $admin['aid']); |
|
0 commit comments