VoltMod
C++23 framework for CS2 server plugins
Loading...
Searching...
No Matches
Policy.cpp
Go to the documentation of this file.
3
4namespace VoltMod
5{
6
7Status Policy::CheckPermission(const Player& caller, std::string_view permission) const
8{
9 if (permission.empty())
10 {
11 return {};
12 }
13
14 // Without a policy there is no trusted permission source. Deny and say so once, so the
15 // plugin misconfiguration is visible instead of silently locking every admin out.
16 if (!HasPermission)
17 {
18 if (!_missingPermissionWarned)
19 {
20 _missingPermissionWarned = true;
22 "Denying '{}': no HasPermission policy is installed. "
23 "Set Runtime::Policy.HasPermission in Load.",
25 }
26 return std::unexpected(Error::Denied("cmd.noPermission"));
27 }
28
29 if (!HasPermission(caller.SteamId(), permission))
30 {
31 return std::unexpected(Error::Denied("cmd.noPermission"));
32 }
33
34 return {};
35}
36
37Status Policy::CheckImmunity(const Player& caller, int64_t targetSteamId) const
38{
39 if (targetSteamId == caller.SteamId() || !CanTarget)
40 {
41 return {};
42 }
43
44 if (!CanTarget(caller.SteamId(), targetSteamId))
45 {
46 return std::unexpected(Error::Immune("target.immune"));
47 }
48
49 return {};
50}
51
53 std::string_view permission) const
54{
55 Player* callerPlayer = _players.Get(caller);
56 if (!callerPlayer)
57 {
58 return std::unexpected(Error::NotFound("caller is not connected"));
59 }
60
61 Player* targetPlayer = nullptr;
62 if (target)
63 {
64 targetPlayer = _players.Get(*target);
65 if (!targetPlayer)
66 {
67 return std::unexpected(Error{ErrorCode::NotFound, "target is not connected", "target.noMatch"});
68 }
69 }
70
71 if (auto allowed = CheckPermission(*callerPlayer, permission); !allowed)
72 {
73 return std::unexpected(allowed.error());
74 }
75
76 if (targetPlayer)
77 {
78 if (auto allowed = CheckImmunity(*callerPlayer, targetPlayer->SteamId()); !allowed)
79 {
80 return std::unexpected(allowed.error());
81 }
82 }
83
84 return Authorized{.Caller = *callerPlayer, .Target = targetPlayer};
85}
86
88{
89 Player* callerPlayer = _players.Get(caller);
90 if (!callerPlayer)
91 {
92 return std::unexpected(Error::NotFound("caller is not connected"));
93 }
94
95 if (auto allowed = CheckPermission(*callerPlayer, permission); !allowed)
96 {
97 return std::unexpected(allowed.error());
98 }
99
100 return CheckImmunity(*callerPlayer, targetSteamId);
101}
102
103} // namespace VoltMod
Player * Get(int slot)
One connected player, owned by PlayerManager for the length of the connection.
Definition Player.hpp:23
Status AuthorizeSteamId(PlayerRef caller, int64_t targetSteamId, std::string_view permission) const
Authorize for a target that may be offline, addressed by SteamID.
Definition Policy.cpp:87
std::function< bool(int64_t steamId, std::string_view permission)> HasPermission
Definition Policy.hpp:50
std::function< bool(int64_t callerSteamId, int64_t targetSteamId)> CanTarget
Definition Policy.hpp:59
Result< Authorized > Authorize(PlayerRef caller, std::optional< PlayerRef > target, std::string_view permission) const
The single gate. Commands, target resolution and menu rows call exactly this.
Definition Policy.cpp:52
void Error(std::format_string< Args... > fmt, Args &&... args)
Definition Log.hpp:97
@ NotFound
The named thing does not exist (no such player, convar, row).
static std::string ReadFile(const std::filesystem::path &path)
Definition Loader.cpp:56
std::expected< void, Error > Status
Definition Result.hpp:67
std::expected< T, Error > Result
Definition Result.hpp:64
A caller/target pair that cleared Policy::Authorize.
Definition Policy.hpp:23
One failure: a code to branch on, text for the log, and an optional translation key.
Definition Result.hpp:42
static Error Denied(std::string key)
Definition Result.hpp:54
static Error Immune(std::string key)
Definition Result.hpp:55
static Error NotFound(std::string detail)
Definition Result.hpp:49
A storable reference to a player: the slot plus the SteamID that occupied it.
Definition PlayerRef.hpp:19