Files
DarkflameServer/dDashboardServer/auth/RequireAuthMiddleware.h
Aaron Kimbrell 821b7c8767 feat(dashboard): permission grants count in every dashboard permission check
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>
2026-09-29 01:16:38 -05:00

54 lines
2.2 KiB
C++

#ifndef __REQUIREAUTHMIDDLEWARE_H__
#define __REQUIREAUTHMIDDLEWARE_H__
#include <memory>
#include <cstdint>
#include <functional>
#include <string>
#include "IHTTPMiddleware.h"
/**
* RequireAuthMiddleware: Enforces authentication on protected routes
*
* Returns 401 Unauthorized if user is not authenticated
* Returns 403 Forbidden if user's GM level is below minimum required
*/
class RequireAuthMiddleware final : public IHTTPMiddleware {
public:
/**
* @param minGmLevel Minimum GM level required to access this route
* 0 = any authenticated user, higher numbers = GM-only
*/
explicit RequireAuthMiddleware(uint8_t minGmLevel = 0);
// The required level is looked up on every request (a permission that can be changed while running)
explicit RequireAuthMiddleware(std::function<uint8_t()> requiredLevel);
// A route guarded by a named permission: an API key must also have it in its scope
RequireAuthMiddleware(std::function<uint8_t()> requiredLevel, std::string permission);
~RequireAuthMiddleware() override = default;
bool Process(HTTPContext& context, HTTPReply& reply) override;
// Whether a signed-in account may use the API (requests signed in with a token in the Authorization header rather
// than the browser's cookie). Set by the dashboard from its api_access permission; unset allows everyone.
static void SetApiAccessCheck(std::function<bool(const HTTPContext& context)> check);
// Renders the page a signed-in account gets when it may not open a page (not /api/); unset replies with JSON
static void SetForbiddenPage(std::function<void(const HTTPContext& context, HTTPReply& reply)> render);
// The route only reads, although it may be a POST (DataTables and lookups send their query as a body): read-only
// API keys may use it
void SetReadsOnly() { readsOnly = true; }
// Told when an API key is refused a route for its scope or because it is read-only (for the audit log)
static void SetApiKeyDeniedHook(std::function<void(const HTTPContext& context, const std::string& reason)> hook);
std::string GetName() const override { return "RequireAuthMiddleware"; }
private:
std::function<uint8_t()> requiredLevel;
std::string permission;
bool readsOnly{};
};
#endif // !__REQUIREAUTHMIDDLEWARE_H__