mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 02:43:44 +00:00
What someone may do on the dashboard is now their GM level's permissions plus the grants on their account, minus its denies (PermissionGrants.h). A deny beats a grant; denies never apply to GM 9, and settings and permissions_manage stay GM 9 only. The account's grants are read with every request (like its GM level), so a change applies at once, and they are passed through every check: RouteUtils::Can, CanViewCharacter, the rank rules (self_* and manage_equal_rank), routes guarded by a permission, the templates' `can`, the API documentation, API access, API key scopes (a key never does more than its owner may now) and WebSocket subscriptions. New permission grants_manage (GM 9 by default) and the API to manage grants: GET /api/grants/catalog, GET /api/grants, POST /api/grants, POST /api/grants/:id/remove. Nobody grants or takes away what they don't hold themselves (a permission, every permission of a group, a command they may use, every command up to their own GM level), and only on accounts the rank rules let them manage (their own with self_moderation). Commands with a fixed level or a floor above GM 1 (/execute) can't be granted. Every change goes in the audit log (grant_permission, deny_permission, remove_grant). Also: the Showcase gate and the traffic subscription now check their permission by name. Check: grant a GM 2 account accounts_ban (it can ban, and the Ban button shows); deny a GM 8 account accounts_view (the accounts list is refused); give an expiry a minute ahead and see it stop; try to grant a permission your account doesn't have (refused); dWebTests PermissionGrantsTests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
84 lines
2.7 KiB
C++
84 lines
2.7 KiB
C++
#pragma once
|
|
|
|
#include <string>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <algorithm>
|
|
#include "eHTTPStatusCode.h"
|
|
#include "json.hpp"
|
|
#include "ApiKeyScope.h"
|
|
|
|
namespace PermissionGrants { struct Held; }
|
|
|
|
/**
|
|
* HTTP Request Context
|
|
*
|
|
* Carries all request metadata through the middleware chain.
|
|
* Populated by the Web framework before middleware/handlers are called.
|
|
*/
|
|
struct HTTPContext {
|
|
// Request metadata
|
|
std::string method{};
|
|
std::string path{}; // lowercased: routes match without regard to case
|
|
std::string originalPath{}; // as sent, for parameters whose case matters (file names)
|
|
std::string queryString{};
|
|
std::string body{};
|
|
|
|
// Request headers (header name -> value)
|
|
// Header names are lowercase for case-insensitive lookup
|
|
std::map<std::string, std::string> headers{};
|
|
|
|
// Client information
|
|
std::string clientIP{};
|
|
unsigned long connectionId = 0; // the web server's id of the connection (for Web::Defer)
|
|
|
|
// Authentication information (populated by auth middleware)
|
|
bool isAuthenticated = false;
|
|
std::string authenticatedUser{};
|
|
uint32_t accountId = 0;
|
|
uint8_t gmLevel = 0;
|
|
// Set when an API key authenticated the request: the key's scope, on top of what the account may do (gmLevel).
|
|
// Every permission check must honour it (RouteUtils::Can and friends do).
|
|
std::shared_ptr<const ApiKeys::Scope> apiKey{};
|
|
// The account's permission grants in force (PermissionGrants.h), loaded with the sign-in; every permission check
|
|
// passes them on (RouteUtils::Can and friends do). nullptr: none were loaded, the GM level alone counts.
|
|
std::shared_ptr<const PermissionGrants::Held> grants{};
|
|
|
|
// Custom data for middleware to communicate
|
|
std::map<std::string, std::string> userData{};
|
|
|
|
/**
|
|
* Get header value (case-insensitive)
|
|
*/
|
|
const std::string& GetHeader(const std::string& headerName) const {
|
|
static const std::string empty{};
|
|
|
|
// Convert to lowercase for comparison
|
|
std::string lowerName = headerName;
|
|
std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), ::tolower);
|
|
|
|
const auto it = headers.find(lowerName);
|
|
return it != headers.end() ? it->second : empty;
|
|
}
|
|
|
|
/**
|
|
* Set header value (automatically lowercased)
|
|
*/
|
|
void SetHeader(const std::string& headerName, const std::string& value) {
|
|
std::string lowerName = headerName;
|
|
std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), ::tolower);
|
|
headers[lowerName] = value;
|
|
}
|
|
|
|
/**
|
|
* Get user data as JSON for template rendering
|
|
*/
|
|
nlohmann::json GetUserDataJson() const {
|
|
nlohmann::json userData;
|
|
userData["username"] = authenticatedUser;
|
|
userData["gmLevel"] = gmLevel;
|
|
userData["accountId"] = accountId;
|
|
return userData;
|
|
}
|
|
};
|