载入中...
搜索中...
未找到
EditorPluginPermissions.cpp
浏览该文件的文档.
2
3#include <set>
4#include <utility>
5
6namespace eve::editor {
7namespace {
8const EditorValue* field(const EditorValue& value, const char* key) {
9 const auto* object = value.getIf<EditorValue::Object>();
10 if (!object) return nullptr;
11 const auto found = object->find(key);
12 return found == object->end() ? nullptr : &found->second;
13}
14EditorValue grantValue(const PluginPermissionGrant& grant) {
15 return EditorValue::Object{{"id", grant.id.value()}, {"plugin", grant.plugin}, {"capability", grant.capability},
16 {"scope", grant.scope}, {"decision", grant.decision}};
17}
18Result<PluginPermissionGrant> parseGrant(const EditorValue& value) {
19 auto string = [&](const char* key) -> const std::string* {
20 const auto* entry = field(value, key);
21 return entry ? entry->getIf<std::string>() : nullptr;
22 };
23 const auto *id = string("id"), *plugin = string("plugin"), *capability = string("capability"),
24 *scope = string("scope"), *decision = string("decision");
25 static const std::set<std::string> capabilities{"filesystem.read", "filesystem.write", "network.connect",
26 "database.connect", "script.native", "process.execute"};
27 static const std::set<std::string> decisions{"allow", "deny", "ask"};
28 if (!id || id->empty() || !plugin || plugin->empty() || !capability || !capabilities.contains(*capability) ||
29 !scope || scope->empty() || !decision || !decisions.contains(*decision) || *scope == "*" || *scope == "/")
30 return eve::editing::failed<PluginPermissionGrant>(
31 EditorStatus::Rejected, RuleId("editor.plugins.invalid-permission"),
32 "Permission requires plugin, known capability, explicit narrow scope and decision");
33 return eve::editing::applied<PluginPermissionGrant>({StableId(*id), *plugin, *capability, *scope, *decision});
34}
35DomainOperation operation(const char* type, const char* inverseType, const std::string& target, EditorValue payload,
36 EditorValue inverse, const StableId& id) {
37 DomainOperation result;
38 result.type = type; result.inverseType = inverseType; result.target = TargetId(target);
39 result.payload = std::move(payload); result.inverse = std::move(inverse); result.hasInverse = true;
40 result.affectedObjects.push_back({TargetId(target), id.value(), 0});
41 return result;
42}
43} // namespace
44
47 return {TargetId(id_),
48 "plugin-permissions",
50 false,
51 {CapabilityId("eve.editor.target.plugin-permissions")}};
52}
54 return capability == CapabilityId("eve.editor.target.plugin-permissions") ? this : nullptr;
55}
57 if (operation.target != TargetId(id_))
58 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.plugins.permission-target"),
59 "Permission operation targets another document");
60 if (operation.type == "plugin.permission.set.v1") {
61 auto parsed = parseGrant(operation.payload);
62 if (!parsed.ok()) return Result<void>::failure(parsed.status());
63 for (const auto& [id, current] : grants_)
64 if (id != parsed.value().id && current.plugin == parsed.value().plugin &&
65 current.capability == parsed.value().capability && current.scope == parsed.value().scope)
66 return eve::editing::failed<void>(EditorStatus::Conflict, RuleId("editor.plugins.duplicate-permission"),
67 "Equivalent permission already exists");
68 grants_.insert_or_assign(parsed.value().id, parsed.value());
69 } else if (operation.type == "plugin.permission.remove.v1") {
70 const auto* id = operation.payload.getIf<std::string>();
71 if (!id || !grants_.erase(StableId(*id)))
72 return eve::editing::failed<void>(EditorStatus::NotFound, RuleId("editor.plugins.permission-not-found"),
73 "Permission grant was not found");
74 } else {
75 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.plugins.permission-operation"),
76 "Unsupported permission operation");
77 }
79 widenDirty(0, 0);
80 return eve::editing::applied<void>();
81}
82std::unique_ptr<IDomainOperationTarget> PluginPermissionTarget::cloneDomainState() const {
83 return std::make_unique<PluginPermissionTarget>(*this);
84}
85Result<void> PluginPermissionTarget::commitDomainState(std::unique_ptr<IDomainOperationTarget> candidate) {
86 auto* typed = dynamic_cast<PluginPermissionTarget*>(candidate.get());
87 if (!typed || typed->id_ != id_)
88 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.plugins.permission-staging"),
89 "Invalid staged permission state");
90 *this = *typed;
91 return eve::editing::applied<void>();
92}
93std::vector<PluginPermissionGrant> PluginPermissionTarget::grants() const {
94 std::vector<PluginPermissionGrant> result;
95 for (const auto& [id, grant] : grants_) { (void)id; result.push_back(grant); }
96 return result;
97}
99 auto parsed = parseGrant(grantValue(grant));
100 if (!parsed.ok()) return Result<DomainOperation>::failure(parsed.status());
101 for (const auto& [id, current] : grants_)
102 if (id != grant.id && current.plugin == grant.plugin && current.capability == grant.capability &&
103 current.scope == grant.scope)
104 return eve::editing::failed<DomainOperation>(EditorStatus::Conflict,
105 RuleId("editor.plugins.duplicate-permission"),
106 "Equivalent permission already exists");
107 const auto found = grants_.find(grant.id);
108 return eve::editing::applied<DomainOperation>(operation(
109 "plugin.permission.set.v1", found == grants_.end() ? "plugin.permission.remove.v1" : "plugin.permission.set.v1",
110 id_, grantValue(grant), found == grants_.end() ? EditorValue(grant.id.value()) : grantValue(found->second),
111 grant.id));
112}
114 const auto found = grants_.find(id);
115 if (found == grants_.end())
116 return eve::editing::failed<DomainOperation>(
117 EditorStatus::NotFound, RuleId("editor.plugins.permission-not-found"), "Permission grant was not found");
118 return eve::editing::applied<DomainOperation>(operation("plugin.permission.remove.v1", "plugin.permission.set.v1",
119 id_, id.value(), grantValue(found->second), id));
120}
121std::string PluginPermissionTarget::decision(const std::string& plugin, const std::string& capability,
122 const std::string& scope) const {
123 for (const auto& [id, grant] : grants_) {
124 (void)id;
125 if (grant.plugin == plugin && grant.capability == capability && grant.scope == scope) return grant.decision;
126 }
127 return "ask";
128}
131 for (const auto& [id, grant] : grants_) { (void)id; grants.push_back(grantValue(grant)); }
132 return EditorValue::Object{{"schemaVersion", int64_t{1}}, {"grants", std::move(grants)}};
133}
135 const auto *versionEntry = field(snapshot, "schemaVersion"), *grantsEntry = field(snapshot, "grants");
136 const auto* version = versionEntry ? versionEntry->getIf<int64_t>() : nullptr;
137 const auto* grants = grantsEntry ? grantsEntry->getIf<EditorValue::Array>() : nullptr;
138 if (!version || *version != 1 || !grants)
139 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.plugins.permission-snapshot"),
140 "Invalid permission snapshot envelope");
141 PluginPermissionTarget candidate(id_);
142 for (const auto& entry : *grants) {
143 auto parsed = parseGrant(entry);
144 if (!parsed.ok()) return Result<void>::failure(parsed.status());
145 auto planned = candidate.makeSet(parsed.value());
146 if (!planned.ok()) return Result<void>::failure(planned.status());
147 auto applied = candidate.applyDomainOperation(planned.value());
148 if (!applied.ok()) return applied;
149 }
150 grants_ = std::move(candidate.grants_);
151 bumpRevision();
152 widenDirty(0, 0);
153 return eve::editing::applied<void>();
154}
155} // namespace eve::editor
LogicalId target
ActionParameterOperation operation
double value
Value::Object payload
std::uint32_t key
JobScope scope
std::string id
Definition PlayHost.cpp:108
bool found
double current
std::string string
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
UUID-backed identifier adapter for legacy textual boundaries.
Definition Identity.h:314
const std::string & value() const noexcept
Returns the exact legacy or canonical compatibility spelling.
Definition Identity.h:362
void bumpRevision()
Advance the revision without touching the dirty region.
Revision revisionValue() const noexcept
Return the revision as the shared protocol revision type.
void widenDirty()
Mark an unknown area dirty without advancing the revision.
eve::OptionalRef< C > capability()
Query a capability through its interface-owned stable identity.
Deterministic owning value tree shared by authoring hosts.
std::vector< Value > Array
std::map< std::string, Value > Object
const T * getIf() const
Return an immediately borrowed typed value.
Revisioned plugin permission policy with reversible, auditable grants.
Result< void > loadSnapshot(const EditorValue &snapshot)
Atomically load a validated permission policy.
void * queryCapability(const CapabilityId &capability) override
Query an optional target capability.
EditorValue snapshotValue() const
Capture deterministic permission policy.
Result< void > commitDomainState(std::unique_ptr< IDomainOperationTarget > candidate) override
Commits domain state.
std::unique_ptr< IDomainOperationTarget > cloneDomainState() const override
Clones domain state.
TargetDescriptor describe() const override
Describe.
std::string decision(const std::string &plugin, const std::string &capability, const std::string &scope) const
Resolve exact plugin/capability/scope policy, defaulting to ask.
PluginPermissionTarget(std::string id)
Plugin permission target.
Result< DomainOperation > makeSet(const PluginPermissionGrant &grant) const
Plan creation or replacement after least-privilege validation.
std::vector< PluginPermissionGrant > grants() const
Enumerate grants in stable-id order.
Result< void > applyDomainOperation(const DomainOperation &operation) override
Applies domain operation.
Result< DomainOperation > makeRemove(const StableId &id) const
Plan grant removal.
eve::editing::StableId StableId
Definition EditorIds.h:36
eve::editing::Value EditorValue
Compatibility alias for the UI-independent authoring value tree.
Definition EditorValue.h:8
eve::editing::TargetId TargetId
Definition EditorIds.h:29
eve::editing::RuleId RuleId
Definition EditorIds.h:32
eve::editing::CapabilityId CapabilityId
Definition EditorIds.h:33
eve::editing::DomainOperation DomainOperation
const EditorValue * field(const EditorValue &value, const char *name)
DomainOperation public API.
Stable discovery metadata exposed by every editable target.
Explicit project-owned permission decision for one plugin capability scope.