载入中...
搜索中...
未找到
Authority.cpp
浏览该文件的文档.
2
3#include "common/Json.h"
5
6#include <simplesquirrel/simplesquirrel.hpp>
7
8#include <algorithm>
9#include <cmath>
10#include <functional>
11#include <iomanip>
12#include <sstream>
13#include <utility>
14
15namespace eve::authority {
16namespace {
17
19struct ScriptAuthorityStore {
20 explicit ScriptAuthorityStore(AuthorityStoreHandleRef value) : reference(value) {}
21 ~ScriptAuthorityStore() noexcept {
22 Authority::release(reference).ignore("script authority store proxy destruction");
23 }
25};
26
27template <class Ref, class Proxy, class Release>
28ssq::Table makeOwnedProxy(HSQUIRRELVM vm, eve::Result<Ref>&& reference, Release&& release) {
30 const Ref ref = std::move(reference).takeValue();
31 auto object = eve::script::makeOwnedSquirrelInstance<Proxy>(vm, std::make_unique<Proxy>(ref));
32 if (!object) {
33 const eve::Status status = object.status();
34 object.ignore("failed to create owned authority proxy");
35 std::invoke(std::forward<Release>(release), ref).ignore("rollback failed owned authority allocation");
37 }
38 ssq::Object owned = std::move(object).takeValue();
40 result.set("value", owned);
41 result.set("ownership", std::string("owned"));
42 result.set("ownerEpoch", static_cast<std::int64_t>(ref.ownerEpoch));
43 result.set("handle", static_cast<std::int64_t>(ref.packed()));
44 return result;
45}
46
47std::string quote(const std::string& value) {
48 std::ostringstream out;
49 out << '"';
50 for (unsigned char c : value) {
51 switch (c) {
52 case '"': out << "\\\""; break;
53 case '\\': out << "\\\\"; break;
54 case '\n': out << "\\n"; break;
55 case '\r': out << "\\r"; break;
56 case '\t': out << "\\t"; break;
57 default:
58 if (c < 0x20)
59 out << "\\u" << std::hex << std::setw(4) << std::setfill('0') << int(c) << std::dec;
60 else
61 out << static_cast<char>(c);
62 }
63 }
64 return out.str() + '"';
65}
66
67bool parseU64(const eve::json::Value& value, uint64_t& output) {
68 if (!value.isString()) return false;
69 try {
70 size_t pos = 0;
71 output = std::stoull(value.asString(), &pos);
72 return pos == value.asString().size();
73 } catch (...) {
74 return false;
75 }
76}
77
78bool parseRuleId(const std::string& id, uint64_t& output) {
79 constexpr const char* prefix = "authority-";
80 if (!id.starts_with(prefix)) return false;
81 const std::string suffix = id.substr(std::char_traits<char>::length(prefix));
82 if (suffix.size() != 16) return false;
83 try {
84 size_t pos = 0;
85 output = std::stoull(suffix, &pos);
86 return pos == suffix.size() && output != 0;
87 } catch (...) {
88 return false;
89 }
90}
91
92bool wins(const Rule& candidate, const Rule& current) {
93 if (candidate.priority != current.priority) return candidate.priority > current.priority;
94 if (candidate.effect != current.effect) return candidate.effect == RuleEffect::Deny;
95 return candidate.order < current.order;
96}
97
98void advanceRevision(eve::Revision& revision) {
99 if (const auto next = revision.incremented()) revision = *next;
100}
101
102eve::LogicalId authoritySchema() {
103 const auto schema = eve::LogicalId::parse("authority:store");
104 if (!schema) std::terminate();
105 return *schema;
106}
107
108const eve::SnapshotMigrationChain& authorityMigrations() {
109 static const eve::SnapshotMigrationChain chain = [] {
111 const auto registration =
112 result.add(authoritySchema(), eve::SchemaVersion(0), eve::SchemaVersion(1),
114 const auto* object = payload.getIf<eve::Value::Object>();
115 if (!object)
117 eve::DiagnosticCode::ParseError, "authority snapshot payload must be an object"));
118 eve::Value::Object migrated = *object;
119 migrated["version"] = eve::Value(std::int64_t(1));
120 return eve::Result<eve::Value>::success(eve::Value(std::move(migrated)));
121 });
122 if (!registration.ok()) std::terminate();
123 return result;
124 }();
125 return chain;
126}
127
128} // namespace
129
130Store::Store(eve::PersistentId instanceId) : instanceId_(instanceId) {}
131
132std::string effectName(RuleEffect effect) { return effect == RuleEffect::Grant ? "grant" : "deny"; }
134 switch (kind) {
135 case EventKind::Granted: return "granted";
136 case EventKind::Denied: return "denied";
137 case EventKind::Revoked: return "revoked";
138 case EventKind::Expired: return "expired";
139 }
140 return "unknown";
141}
142
143std::string Store::add(RuleEffect effect, const std::string& actor, const std::string& scope,
144 const std::string& capability, const std::string& source, int priority, double duration) {
145 if (actor.empty() || scope.empty() || capability.empty() || !std::isfinite(duration)) return {};
146 std::ostringstream id;
147 id << "authority-" << std::setw(16) << std::setfill('0') << nextId_++;
148 Rule rule;
149 rule.id = id.str();
150 rule.actor = actor;
151 rule.scope = scope;
152 rule.capability = capability;
153 rule.source = source;
154 rule.effect = effect;
155 rule.priority = priority;
156 rule.duration = std::max(0.0, duration);
157 rule.remaining = duration > 0.0 ? duration : -1.0;
158 rule.order = nextOrder_++;
159 rules_.push_back(std::move(rule));
160 emit(effect == RuleEffect::Grant ? EventKind::Granted : EventKind::Denied, rules_.back());
161 advanceRevision(revision_);
162 return rules_.back().id;
163}
164
165std::string Store::grant(const std::string& actor, const std::string& scope, const std::string& capability,
166 const std::string& source, int priority, double duration) {
167 return add(RuleEffect::Grant, actor, scope, capability, source, priority, duration);
168}
169std::string Store::deny(const std::string& actor, const std::string& scope, const std::string& capability,
170 const std::string& source, int priority, double duration) {
171 return add(RuleEffect::Deny, actor, scope, capability, source, priority, duration);
172}
173
174void Store::emit(EventKind kind, const Rule& rule, const std::string& reason) {
175 events_.push_back({nextSequence_++, kind, rule.id, rule.actor, rule.scope, rule.capability, rule.source, reason});
176}
177
178bool Store::revoke(const std::string& id, const std::string& reason) {
179 const auto it = std::find_if(rules_.begin(), rules_.end(), [&id](const Rule& rule) { return rule.id == id; });
180 if (it == rules_.end()) return false;
181 emit(EventKind::Revoked, *it, reason);
182 rules_.erase(it);
183 query_.clear();
184 advanceRevision(revision_);
185 return true;
186}
187
188int Store::revokeBySource(const std::string& source, const std::string& reason) {
189 int removed = 0;
190 for (auto it = rules_.begin(); it != rules_.end();) {
191 if (it->source == source) {
192 emit(EventKind::Revoked, *it, reason);
193 it = rules_.erase(it);
194 ++removed;
195 } else {
196 ++it;
197 }
198 }
199 query_.clear();
200 if (removed > 0) advanceRevision(revision_);
201 return removed;
202}
203
204void Store::update(double dtSeconds) {
205 if (dtSeconds <= 0.0 || !std::isfinite(dtSeconds)) return;
206 bool changed = false;
207 for (auto it = rules_.begin(); it != rules_.end();) {
208 if (it->remaining < 0.0) {
209 ++it;
210 continue;
211 }
212 it->remaining = std::max(0.0, it->remaining - dtSeconds);
213 changed = true;
214 if (it->remaining == 0.0) {
215 emit(EventKind::Expired, *it, "duration_elapsed");
216 it = rules_.erase(it);
217 } else {
218 ++it;
219 }
220 }
221 query_.clear();
222 if (changed) advanceRevision(revision_);
223}
224
225Decision Store::explain(const std::string& actor, const std::string& scope, const std::string& capability) const {
226 const Rule* winner = nullptr;
227 for (const auto& rule : rules_)
228 if (rule.actor == actor && rule.scope == scope && rule.capability == capability &&
229 (!winner || wins(rule, *winner)))
230 winner = &rule;
231 if (!winner) return {false, "no_matching_rule", {}, {}, 0};
232 return {winner->effect == RuleEffect::Grant, winner->effect == RuleEffect::Grant ? "granted" : "denied", winner->id,
233 winner->source, winner->priority};
234}
235bool Store::can(const std::string& actor, const std::string& scope, const std::string& capability) const {
236 return explain(actor, scope, capability).allowed;
237}
238const Rule* Store::find(const std::string& id) const {
239 const auto it = std::find_if(rules_.begin(), rules_.end(), [&id](const Rule& rule) { return rule.id == id; });
240 return it == rules_.end() ? nullptr : &*it;
241}
242
243int Store::query(const std::string& actor, const std::string& scope, const std::string& capability) {
244 query_.clear();
245 for (const auto& rule : rules_)
246 if ((actor.empty() || rule.actor == actor) && (scope.empty() || rule.scope == scope) &&
247 (capability.empty() || rule.capability == capability))
248 query_.push_back(&rule);
249 return static_cast<int>(query_.size());
250}
251int Store::queryActor(const std::string& actor) { return query(actor, {}, {}); }
252int Store::queryScope(const std::string& scope) { return query({}, scope, {}); }
253int Store::queryCapability(const std::string& capability) { return query({}, {}, capability); }
254const Rule* Store::queryAt(int index) const {
255 return index >= 0 && static_cast<size_t>(index) < query_.size() ? query_[static_cast<size_t>(index)] : nullptr;
256}
257int Store::eventCount() const { return static_cast<int>(events_.size()); }
258const Event* Store::eventAt(int index) const {
259 return index >= 0 && static_cast<size_t>(index) < events_.size() ? &events_[static_cast<size_t>(index)] : nullptr;
260}
262 if (!events_.empty()) {
263 events_.clear();
264 advanceRevision(revision_);
265 }
266}
267
268std::string Store::snapshotJson() const {
269 std::ostringstream out;
270 out << "{\"version\":1,\"nextId\":" << quote(std::to_string(nextId_))
271 << ",\"nextOrder\":" << quote(std::to_string(nextOrder_))
272 << ",\"nextSequence\":" << quote(std::to_string(nextSequence_)) << ",\"rules\":[";
273 bool first = true;
274 for (const auto& rule : rules_) {
275 if (!first) out << ',';
276 first = false;
277 out << "{\"id\":" << quote(rule.id) << ",\"actor\":" << quote(rule.actor) << ",\"scope\":" << quote(rule.scope)
278 << ",\"capability\":" << quote(rule.capability) << ",\"source\":" << quote(rule.source)
279 << ",\"effect\":" << quote(effectName(rule.effect)) << ",\"priority\":" << rule.priority
280 << ",\"duration\":" << std::setprecision(17) << rule.duration << ",\"remaining\":" << rule.remaining
281 << ",\"order\":" << quote(std::to_string(rule.order)) << '}';
282 }
283 return out.str() + "]}";
284}
285
286eve::Result<void> Store::restoreJson(const std::string& json) {
287 std::string parseError;
288 auto document = eve::json::Document::parse(json, &parseError);
289 const auto root = document.root();
290 uint64_t nextId = 0, nextOrder = 0, nextSequence = 0;
291 if (!document.valid() || !root.isObject() || root.getInt("version") != 1 || !parseU64(root.get("nextId"), nextId) ||
292 !parseU64(root.get("nextOrder"), nextOrder) || !parseU64(root.get("nextSequence"), nextSequence) ||
293 nextId == 0 || nextOrder == 0 || nextSequence == 0 || !root.get("rules").isArray()) {
296 parseError.empty() ? "invalid authority snapshot" : parseError, "authority.store"));
297 }
298 std::deque<Rule> restored;
299 const auto values = root.get("rules");
300 for (size_t i = 0; i < values.size(); ++i) {
301 const auto item = values.at(i);
302 Rule rule;
303 uint64_t order = 0, numericId = 0;
304 const auto effect = item.getString("effect");
305 if (!item.isObject() || item.getString("id").empty() || item.getString("actor").empty() ||
306 item.getString("scope").empty() || item.getString("capability").empty() ||
307 (effect != "grant" && effect != "deny") || !parseU64(item.get("order"), order) || order == 0 ||
308 order >= nextOrder || !parseRuleId(item.getString("id"), numericId) || numericId >= nextId ||
309 !item.get("priority").isNumber() || !item.get("duration").isNumber() || !item.get("remaining").isNumber()) {
311 eve::Diagnostic::error(eve::DiagnosticCode::ParseError, "invalid rule at index " + std::to_string(i),
312 "authority.store.rules[" + std::to_string(i) + "]"));
313 }
314 rule.id = item.getString("id");
315 rule.actor = item.getString("actor");
316 rule.scope = item.getString("scope");
317 rule.capability = item.getString("capability");
318 rule.source = item.getString("source");
319 rule.effect = effect == "grant" ? RuleEffect::Grant : RuleEffect::Deny;
320 rule.priority = item.getInt("priority");
321 rule.duration = item.getDouble("duration");
322 rule.remaining = item.getDouble("remaining");
323 rule.order = order;
324 if (!std::isfinite(rule.duration) || !std::isfinite(rule.remaining) || rule.duration < 0.0 ||
325 rule.remaining == 0.0 || rule.remaining < -1.0 ||
326 std::any_of(
327 restored.begin(), restored.end(),
328 [&](const Rule& old) { return old.id == rule.id || old.order == rule.order; })) {
330 eve::DiagnosticCode::InvariantViolation, "invalid rule state at index " + std::to_string(i),
331 "authority.store.rules[" + std::to_string(i) + "]"));
332 }
333 restored.push_back(std::move(rule));
334 }
335 rules_ = std::move(restored);
336 nextId_ = nextId;
337 nextOrder_ = nextOrder;
338 nextSequence_ = nextSequence;
339 events_.clear();
340 query_.clear();
341 revision_ = eve::Revision(nextSequence > 0 ? nextSequence - 1 : 0);
342 tick_ = eve::SimulationTick{};
344}
345
349 return eve::makeSnapshotEnvelope("authority.store", authoritySchema(), eve::SchemaVersion(1), instanceId_,
350 revision_, tick_, std::move(payload).takeValue(), hashProvider);
351}
352
354 const eve::SnapshotHashProvider& hashProvider) {
355 if (source.type != "authority.store" || source.schema != authoritySchema())
357 "snapshot does not belong to authority::Store"));
358 if (!instanceId_.isNil() && source.instanceId != instanceId_)
360 eve::DiagnosticCode::Conflict, "snapshot instanceId does not match authority::Store"));
361
362 auto migrated = authorityMigrations().migrate(source, eve::SchemaVersion(1), hashProvider);
363 if (!migrated.ok()) return eve::Result<void>::failure(migrated.status());
364 const auto& candidate = migrated.value();
365 auto metadata = eve::validateSnapshotPayloadMetadata(candidate.payload, candidate.revision, candidate.tick);
366 if (!metadata.ok()) return eve::Result<void>::failure(metadata.status());
367 auto payload = candidate.payload.toJson();
368 if (!payload.ok()) return eve::Result<void>::failure(payload.status());
369 const std::string payloadJson = std::move(payload).takeValue();
370
371 // restoreJson parses into local containers before replacing rules/counters;
372 // it is therefore the domain candidate-builder and has no partial writes.
373 auto restored = restoreJson(payloadJson);
374 if (!restored.ok()) return eve::Result<void>::failure(restored.status());
375
376 instanceId_ = candidate.instanceId;
377 revision_ = candidate.revision;
378 tick_ = candidate.tick;
380}
381
383 auto value = snapshot(hashProvider);
384 if (!value.ok()) return eve::Result<std::string>::failure(value.status());
385 return std::move(value).andThen(
386 [](eve::SnapshotEnvelope&& envelope) { return eve::serializeSnapshotEnvelope(envelope); });
387}
388
389eve::Result<void> Store::restoreSnapshotJson(std::string_view json, const eve::SnapshotHashProvider& hashProvider) {
390 auto source = eve::parseSnapshotEnvelope(json, hashProvider);
391 if (!source.ok()) return eve::Result<void>::failure(source.status());
392 return restoreSnapshot(std::move(source).takeValue(), hashProvider);
393}
394
396 Authority* module = Authority::create();
397 return module->stores_.emplace(std::make_unique<Store>());
398}
399
401 Authority* module = ModuleManager::getInstance<Authority>("Authority");
402 if (!module) return {};
403 return module->stores_.resolve(reference);
404}
405
407 Authority* module = ModuleManager::getInstance<Authority>("Authority");
408 if (!module)
410 "Authority module is no longer loaded", "store", {},
411 "authority.squirrel"));
412 return module->stores_.erase(reference);
413}
414
416 if (!reference.isValid()) return false;
417 Authority* module = ModuleManager::getInstance<Authority>("Authority");
418 return !module || module->stores_.isStale(reference);
419}
420
422
423void Authority::expose(ssq::Table& table) {
424 const HSQUIRRELVM vm = table.getHandle();
425 auto rule = table.addClass<Rule>("AuthorityRule", std::function<Rule*()>([] { return nullptr; }), false);
426 rule.addFunc("getId", [](Rule* r) { return r ? r->id : std::string{}; });
427 rule.addFunc("getActor", [](Rule* r) { return r ? r->actor : std::string{}; });
428 rule.addFunc("getScope", [](Rule* r) { return r ? r->scope : std::string{}; });
429 rule.addFunc("getCapability", [](Rule* r) { return r ? r->capability : std::string{}; });
430 rule.addFunc("getSource", [](Rule* r) { return r ? r->source : std::string{}; });
431 rule.addFunc("getEffect", [](Rule* r) { return r ? effectName(r->effect) : std::string{}; });
432 rule.addFunc("getPriority", [](Rule* r) { return r ? r->priority : 0; });
433 rule.addFunc("getRemaining", [](Rule* r) { return r ? static_cast<float>(r->remaining) : 0.0f; });
434
435 auto decision =
436 table.addClass<Decision>("AuthorityDecision", std::function<Decision*()>([] { return nullptr; }), false);
437 decision.addFunc("isAllowed", [](Decision* d) { return d && d->allowed; });
438 decision.addFunc("getReason", [](Decision* d) { return d ? d->reason : std::string{}; });
439 decision.addFunc("getWinningRuleId", [](Decision* d) { return d ? d->winningRuleId : std::string{}; });
440 decision.addFunc("getSource", [](Decision* d) { return d ? d->source : std::string{}; });
441 decision.addFunc("getPriority", [](Decision* d) { return d ? d->priority : 0; });
442
443 auto event = table.addClass<Event>("AuthorityEvent", std::function<Event*()>([] { return nullptr; }), false);
444 event.addFunc("getSequence", [](Event* e) { return e ? static_cast<int64_t>(e->sequence) : int64_t{0}; });
445 event.addFunc("getKind", [](Event* e) { return e ? eventKindName(e->kind) : std::string{}; });
446 event.addFunc("getRuleId", [](Event* e) { return e ? e->ruleId : std::string{}; });
447 event.addFunc("getReason", [](Event* e) { return e ? e->reason : std::string{}; });
448
449 auto store = table.addClass<Store>("AuthorityStore", std::function<Store*()>([] { return nullptr; }), false);
450 store.addFunc("grant", [](Store* s, const std::string& actor, const std::string& scope,
451 const std::string& capability, const std::string& source, int priority, float duration) {
452 return s ? s->grant(actor, scope, capability, source, priority, duration) : std::string{};
453 });
454 store.addFunc("deny", [](Store* s, const std::string& actor, const std::string& scope,
455 const std::string& capability, const std::string& source, int priority, float duration) {
456 return s ? s->deny(actor, scope, capability, source, priority, duration) : std::string{};
457 });
458 store.addFunc("revoke", &Store::revoke);
459 store.addFunc("revokeBySource", &Store::revokeBySource);
460 store.addFunc("update", [](Store* s, float dt) {
461 if (s) s->update(dt);
462 });
463 store.addFunc("can", &Store::can);
464 store.addFunc("explain", &Store::explain);
465 store.addFunc(
466 "find", [](Store* s, const std::string& id) -> Rule* { return s ? const_cast<Rule*>(s->find(id)) : nullptr; });
467 store.addFunc("queryActor", &Store::queryActor);
468 store.addFunc("queryScope", &Store::queryScope);
469 store.addFunc("queryCapability", &Store::queryCapability);
470 store.addFunc("query", &Store::query);
471 store.addFunc("queryAt",
472 [](Store* s, int index) -> Rule* { return s ? const_cast<Rule*>(s->queryAt(index)) : nullptr; });
473 store.addFunc("eventCount", &Store::eventCount);
474 store.addFunc("eventAt",
475 [](Store* s, int index) -> Event* { return s ? const_cast<Event*>(s->eventAt(index)) : nullptr; });
476 store.addFunc("clearEvents", &Store::clearEvents);
477 store.addFunc("snapshotJson", &Store::snapshotJson);
478 store.addFunc("restoreJson", [vm](Store* value, const std::string& json) {
479 if (!value)
482 "authority store must not be null", "store", {},
483 "authority.squirrel")));
484 return eve::script::projectResult(vm, value->restoreJson(json));
485 });
486
487 auto ownedStore = table.addClass<ScriptAuthorityStore>(
488 "AuthorityStoreProxy", std::function<ScriptAuthorityStore*()>([] { return nullptr; }), true);
489 ownedStore.addFunc("ownership", [](ScriptAuthorityStore*) { return std::string("owned"); });
490 ownedStore.addFunc("ownerEpoch", [](ScriptAuthorityStore* value) {
491 return value ? static_cast<int64_t>(value->reference.ownerEpoch) : int64_t{0};
492 });
493 ownedStore.addFunc("handle", [](ScriptAuthorityStore* value) {
494 return value ? static_cast<int64_t>(value->reference.packed()) : int64_t{0};
495 });
496 ownedStore.addFunc("isStale",
497 [](ScriptAuthorityStore* value) { return !value || Authority::isStale(value->reference); });
498 ownedStore.addFunc("release", [vm](ScriptAuthorityStore* value) {
499 if (!value)
502 "owned authority store proxy must not be null",
503 "store", {}, "authority.squirrel")));
505 });
506 ownedStore.addFunc(
507 "grant", [vm](ScriptAuthorityStore* value, const std::string& actor, const std::string& scope,
508 const std::string& capability, const std::string& source, int priority, float duration) {
509 if (!value)
511 vm,
513 "owned authority store proxy must not be null",
514 "store", {}, "authority.squirrel"))
515 .status());
516 auto view = Authority::resolve(value->reference);
517 if (!view.isBound())
520 "owned authority store handle is stale",
521 "store", {}, "authority.squirrel"))
522 .status());
523 const std::string id = view->grant(actor, scope, capability, source, priority, duration);
524 const auto status = id.empty() ? eve::Status::failure(eve::Diagnostic::error(
525 eve::DiagnosticCode::InvalidArgument, "authority rule was rejected",
526 "store", {}, "authority.squirrel"))
527 : eve::Status::success(eve::StatusCode::Applied);
528 return id.empty() ? eve::script::projectStatusResult(vm, status)
530 });
531 ownedStore.addFunc(
532 "deny", [vm](ScriptAuthorityStore* value, const std::string& actor, const std::string& scope,
533 const std::string& capability, const std::string& source, int priority, float duration) {
534 if (!value)
537 "authority store proxy must not be null",
538 "store", {}, "authority.squirrel"))
539 .status());
540 auto view = Authority::resolve(value->reference);
541 if (!view.isBound())
544 "authority store handle is stale", "store",
545 {}, "authority.squirrel"))
546 .status());
547 const std::string id = view->deny(actor, scope, capability, source, priority, duration);
548 const auto status = id.empty() ? eve::Status::failure(eve::Diagnostic::error(
549 eve::DiagnosticCode::InvalidArgument, "authority rule was rejected",
550 "store", {}, "authority.squirrel"))
551 : eve::Status::success(eve::StatusCode::Applied);
552 return id.empty() ? eve::script::projectStatusResult(vm, status)
554 });
555 ownedStore.addFunc("revoke", [](ScriptAuthorityStore* value, const std::string& id, const std::string& reason) {
556 auto view = value ? Authority::resolve(value->reference) : eve::script::Borrowed<Store>();
557 return view.isBound() && view->revoke(id, reason);
558 });
559 ownedStore.addFunc("revokeBySource",
560 [](ScriptAuthorityStore* value, const std::string& source, const std::string& reason) {
561 auto view = value ? Authority::resolve(value->reference) : eve::script::Borrowed<Store>();
562 return view.isBound() ? view->revokeBySource(source, reason) : 0;
563 });
564 ownedStore.addFunc("update", [](ScriptAuthorityStore* value, float dt) {
565 auto view = value ? Authority::resolve(value->reference) : eve::script::Borrowed<Store>();
566 if (view.isBound()) view->update(dt);
567 });
568 ownedStore.addFunc("can", [](ScriptAuthorityStore* value, const std::string& actor, const std::string& scope,
569 const std::string& capability) {
570 auto view = value ? Authority::resolve(value->reference) : eve::script::Borrowed<Store>();
571 return view.isBound() && view->can(actor, scope, capability);
572 });
573 ownedStore.addFunc("explain", [](ScriptAuthorityStore* value, const std::string& actor, const std::string& scope,
574 const std::string& capability) {
575 auto view = value ? Authority::resolve(value->reference) : eve::script::Borrowed<Store>();
576 return view.isBound() ? view->explain(actor, scope, capability) : Decision{};
577 });
578 ownedStore.addFunc("find", [](ScriptAuthorityStore* value, const std::string& id) -> Rule* {
579 auto view = value ? Authority::resolve(value->reference) : eve::script::Borrowed<Store>();
580 return view.isBound() ? const_cast<Rule*>(view->find(id)) : nullptr;
581 });
582 ownedStore.addFunc("queryActor", [](ScriptAuthorityStore* value, const std::string& actor) {
583 auto view = value ? Authority::resolve(value->reference) : eve::script::Borrowed<Store>();
584 return view.isBound() ? view->queryActor(actor) : 0;
585 });
586 ownedStore.addFunc("queryScope", [](ScriptAuthorityStore* value, const std::string& scope) {
587 auto view = value ? Authority::resolve(value->reference) : eve::script::Borrowed<Store>();
588 return view.isBound() ? view->queryScope(scope) : 0;
589 });
590 ownedStore.addFunc("queryCapability", [](ScriptAuthorityStore* value, const std::string& capability) {
591 auto view = value ? Authority::resolve(value->reference) : eve::script::Borrowed<Store>();
592 return view.isBound() ? view->queryCapability(capability) : 0;
593 });
594 ownedStore.addFunc("query", [](ScriptAuthorityStore* value, const std::string& actor, const std::string& scope,
595 const std::string& capability) {
596 auto view = value ? Authority::resolve(value->reference) : eve::script::Borrowed<Store>();
597 return view.isBound() ? view->query(actor, scope, capability) : 0;
598 });
599 ownedStore.addFunc("queryAt", [](ScriptAuthorityStore* value, int index) -> Rule* {
600 auto view = value ? Authority::resolve(value->reference) : eve::script::Borrowed<Store>();
601 return view.isBound() ? const_cast<Rule*>(view->queryAt(index)) : nullptr;
602 });
603 ownedStore.addFunc("eventCount", [](ScriptAuthorityStore* value) {
604 auto view = value ? Authority::resolve(value->reference) : eve::script::Borrowed<Store>();
605 return view.isBound() ? view->eventCount() : 0;
606 });
607 ownedStore.addFunc("eventAt", [](ScriptAuthorityStore* value, int index) -> Event* {
608 auto view = value ? Authority::resolve(value->reference) : eve::script::Borrowed<Store>();
609 return view.isBound() ? const_cast<Event*>(view->eventAt(index)) : nullptr;
610 });
611 ownedStore.addFunc("clearEvents", [](ScriptAuthorityStore* value) {
612 auto view = value ? Authority::resolve(value->reference) : eve::script::Borrowed<Store>();
613 if (view.isBound()) view->clearEvents();
614 });
615 ownedStore.addFunc("snapshotJson", [](ScriptAuthorityStore* value) {
616 auto view = value ? Authority::resolve(value->reference) : eve::script::Borrowed<Store>();
617 return view.isBound() ? view->snapshotJson() : std::string{};
618 });
619 ownedStore.addFunc("restoreJson", [vm](ScriptAuthorityStore* value, const std::string& json) {
620 auto view = value ? Authority::resolve(value->reference) : eve::script::Borrowed<Store>();
621 if (!view.isBound())
624 "owned authority store handle is stale", "store",
625 {}, "authority.squirrel")));
626 return eve::script::projectResult(vm, view->restoreJson(json));
627 });
628
629 auto cls = table.addClass(name, Authority::create, false);
630 expose(cls);
631}
632void Authority::expose(ssq::Class& cls) {
633 cls.addFunc("getName", &Authority::getName);
634 cls.addFunc("newStore", [vm = cls.getHandle()](Authority*) -> ssq::Table {
635 return makeOwnedProxy<AuthorityStoreHandleRef, ScriptAuthorityStore>(
636 vm, Authority::newStore(), [](AuthorityStoreHandleRef ref) { return Authority::release(ref); });
637 });
638}
639
640} // namespace eve::authority
double value
Value::Object payload
float duration
int root
Definition AnimSmr.cpp:119
std::string output
ActiveSource owned
AuthorityStoreHandleRef reference
Definition Authority.cpp:24
const std::string & s
int priority
struct SQVM * HSQUIRRELVM
HSQUIRRELVM vm
Definition ECS.cpp:20
HSQOBJECT cls
Definition ECS.cpp:21
std::map< std::string, Var > values
wgpu::PopErrorScopeStatus status
double r
std::int32_t c
std::int32_t first
JobScope scope
TokenKind kind
char quote
std::string name
std::vector< std::int32_t > order
#define Module_IMPL(ModuleName, newExpr)
Definition Module.h:26
std::unique_ptr< ParticleEffect > effect
std::uint64_t revision
std::string script
Definition PlayHost.cpp:116
std::string id
Definition PlayHost.cpp:108
std::function< eve::Result< void >()> release
Definition Procgen.cpp:86
float d
glm::mat4 view
int removed
double current
std::string string
The single Squirrel projection for common Result, Status and Value.
Json object
uint32_t index
const UnitySourceAsset & source
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Human-readable, scoped identifier in the form namespace:name.
Definition Identity.h:411
static std::optional< LogicalId > parse(std::string_view text)
Parses a scoped logical name.
Definition Identity.cpp:36
virtual std::string getName() const =0
Returns the name.
void ignore(std::string_view reason={}) const noexcept
Explicitly discard this result after documenting the reason.
Definition Result.h:537
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
One directed payload migration step.
Definition Snapshot.h:145
Result< void > add(LogicalId schema, SchemaVersion from, SchemaVersion to, Migration migration)
Register one schema-local migration edge.
Definition Snapshot.cpp:196
Structured status and zero or more diagnostics for an operation.
Definition Status.h:68
static Status failure(StatusCode code, Diagnostic diagnostic)
Construct a failed status with one diagnostic.
Definition Status.h:84
static Status success(StatusCode code=StatusCode::Ok)
Construct a successful status with an explicit non-error outcome.
Definition Status.h:81
The canonical owning dynamic value used by data-facing protocols.
Definition Value.h:31
static Result< Value > fromJson(std::string_view json)
Parse one strict JSON value into an owning Value.
Definition Value.cpp:57
std::map< std::string, Value > Object
Definition Value.h:34
Script module factory for generic authority stores.
Definition Authority.h:176
static eve::Result< void > release(AuthorityStoreHandleRef reference)
Releases a module-owned store.
static eve::Result< AuthorityStoreHandleRef > newStore()
Allocates an authority store and returns its ownership reference.
static bool isStale(AuthorityStoreHandleRef reference) noexcept
Reports whether a store reference is stale for the current module.
static eve::script::Borrowed< Store > resolve(AuthorityStoreHandleRef reference) noexcept
Resolves a live store as a non-owning observation.
Authority()=default
Constructs a Authority.
const Rule * queryAt(int index) const
Returns a rule from the latest query, or nullptr.
Store(eve::PersistentId instanceId={})
Creates an authority store with an optional persistent identity.
std::string deny(const std::string &actor, const std::string &scope, const std::string &capability, const std::string &source, int priority=0, double duration=0.0)
Adds a deny rule and returns its stable ID, or an empty string for invalid input.
void update(double dtSeconds)
Advances finite rule durations and expires rules reaching zero.
eve::Result< void > restoreSnapshot(const eve::SnapshotEnvelope &snapshot, const eve::SnapshotHashProvider &hashProvider)
Restores a verified or migratable snapshot transactionally.
std::string grant(const std::string &actor, const std::string &scope, const std::string &capability, const std::string &source, int priority=0, double duration=0.0)
Adds a grant rule and returns its stable ID, or an empty string for invalid input.
int revokeBySource(const std::string &source, const std::string &reason="source_revoked")
Revokes every rule from a source and returns the number removed.
int eventCount() const
Returns retained authority event count.
int queryScope(const std::string &scope)
Queries rules for a scope in deterministic creation order.
bool revoke(const std::string &id, const std::string &reason="revoked")
Revokes a rule by stable ID.
eve::Result< std::string > snapshotEnvelopeJson(const eve::SnapshotHashProvider &hashProvider) const
Serializes the common snapshot envelope as canonical JSON.
eve::Result< void > restoreSnapshotJson(std::string_view json, const eve::SnapshotHashProvider &hashProvider)
Parses and transactionally restores a common snapshot envelope.
int query(const std::string &actor, const std::string &scope, const std::string &capability)
Queries rules matching actor, scope, and capability. Empty fields act as wildcards.
void clearEvents()
Clears retained events without resetting sequence allocation.
const Event * eventAt(int index) const
Returns a retained event, or nullptr.
int queryActor(const std::string &actor)
Queries rules for an actor in deterministic creation order.
const Rule * find(const std::string &id) const
Returns a rule by stable ID, or nullptr.
bool can(const std::string &actor, const std::string &scope, const std::string &capability) const
Returns whether the winning rule allows a capability.
Decision explain(const std::string &actor, const std::string &scope, const std::string &capability) const
Returns an explainable decision and its winning rule.
eve::Result< eve::SnapshotEnvelope > snapshot(const eve::SnapshotHashProvider &hashProvider) const
Captures this store in the common versioned snapshot envelope.
eve::Result< void > restoreJson(const std::string &json)
Transactionally restores a snapshot produced by snapshotJson().
int queryCapability(const std::string &capability)
Queries rules for a capability in deterministic creation order.
std::string snapshotJson() const
Exports persistent state as deterministic compact JSON.
Id128 public API.
Definition Identity.h:112
constexpr bool isNil() const noexcept
Returns whether this value is the all-zero nil ID.
Definition Identity.h:176
static Document parse(const std::string &text, std::string *error=nullptr)
Parse.
Definition Json.cpp:581
EVENGINE_API_FOUNDATION public API.
Definition Json.h:38
Non-owning C++ observation with no implicit lifetime extension.
std::string effectName(RuleEffect effect)
Returns the stable lowercase name of a rule effect.
eve::script::RuntimeHandleRef< AuthorityStoreHandleTag > AuthorityStoreHandleRef
Generation- and module-epoch-qualified authority store reference.
Definition Authority.h:21
EventKind
Kind of authority lifecycle event.
Definition Authority.h:50
RuleEffect
Whether an authority rule grants or denies a capability.
Definition Authority.h:24
std::string eventKindName(EventKind kind)
Returns the stable lowercase name of an event kind.
std::variant< std::monostate, std::int64_t, double, std::string, bool > Value
Definition Database.h:26
eve::StatusCode Status
constexpr HexDirection next(HexDirection d) noexcept
The next direction clockwise (NW wraps to NE).
Definition HexMetrics.h:76
std::unordered_map< std::string, SkillDefinition > & table()
Definition Skill.cpp:65
ssq::Table projectStatusResult(HSQUIRRELVM vm, const Status &status)
Project a checked native status that carries no payload.
ssq::Table projectResult(HSQUIRRELVM vm, Result< void > &&result)
Consume and project a void native Result using the common schema.
Build metadata (engine git commit, build time, third-party version).
Definition Build.cpp:16
Result< void > validateSnapshotPayloadMetadata(const Value &payload, Revision revision, SimulationTick tick)
Validate optional payload copies of envelope revision and tick.
Definition Snapshot.cpp:80
StatusCode
Stable outcome category for an operation.
Definition Status.h:27
Result< SnapshotEnvelope > parseSnapshotEnvelope(std::string_view json, const SnapshotHashProvider &hashProvider)
Parse and verify an envelope from canonical or compatible JSON text.
Definition Snapshot.cpp:190
Result< std::string > serializeSnapshotEnvelope(const SnapshotEnvelope &snapshot)
Serialize an envelope as deterministic compact JSON.
Definition Snapshot.cpp:184
Result< SnapshotEnvelope > makeSnapshotEnvelope(std::string type, LogicalId schema, SchemaVersion schemaVersion, PersistentId instanceId, Revision revision, SimulationTick tick, Value payload, const SnapshotHashProvider &hashProvider)
Construct and seal a snapshot envelope.
Definition Snapshot.cpp:103
detail::StrongUint64< detail::RevisionTag > Revision
Monotonic content/state revision used for optimistic-concurrency checks.
Definition Revision.h:13
std::function< Result< ContentId >(std::string_view canonicalInput)> SnapshotHashProvider
Injected content-digest implementation used by snapshots.
Definition Snapshot.h:36
Stable outer format shared by persistence and cross-process snapshots.
Definition Snapshot.h:46
Explainable result of an authority decision.
Definition Authority.h:41
Deterministically sequenced authority lifecycle event.
Definition Authority.h:53
One stable authority rule over an opaque actor and scope.
Definition Authority.h:27
std::string scope
Definition Authority.h:30
RuleEffect effect
Definition Authority.h:33
std::string actor
Definition Authority.h:29
std::string capability
Definition Authority.h:31
std::string source
Definition Authority.h:32
Handle plus owner-lifetime epoch for a non-ECS registry object.
constexpr bool isValid() const noexcept
Returns whether both the handle and its registry epoch are set.