Files
engine/modules/agent_auth/agent_auth.cpp
ozan d291dcdc74 feat: 9 agentic engine modules for agent-native Godot
agent_api (HTTP server), agent_log (structured logging), agent_events (event bus),
agent_console (GameConsole), agent_replay (snapshots), agent_vision (depth/segmentation),
agent_fbx (bone remapping), agent_auth (multi-agent), agent_analytics (feature flags + tracking)

All modules compile clean with mono. Binary uploaded to S3 v1.0.0.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 03:44:28 +01:00

177 lines
5.3 KiB
C++

#include "agent_auth.h"
#include "core/crypto/crypto_core.h"
#include "core/os/os.h"
#include "core/string/print_string.h"
AgentAuth *AgentAuth::singleton = nullptr;
AgentAuth::AgentAuth() {
singleton = this;
}
AgentAuth::~AgentAuth() {
singleton = nullptr;
}
void AgentAuth::_bind_methods() {
ClassDB::bind_method(D_METHOD("register_agent", "name", "permissions"), &AgentAuth::register_agent, DEFVAL(PERM_ALL));
ClassDB::bind_method(D_METHOD("unregister_agent", "token"), &AgentAuth::unregister_agent);
ClassDB::bind_method(D_METHOD("validate_token", "token"), &AgentAuth::validate_token);
ClassDB::bind_method(D_METHOD("check_permission", "token", "perm"), &AgentAuth::check_permission);
ClassDB::bind_method(D_METHOD("get_agents"), &AgentAuth::get_agents);
ClassDB::bind_method(D_METHOD("get_audit_log", "count"), &AgentAuth::get_audit_log, DEFVAL(100));
ClassDB::bind_method(D_METHOD("get_agent_info", "token"), &AgentAuth::get_agent_info);
ClassDB::bind_method(D_METHOD("set_max_requests_per_minute", "max"), &AgentAuth::set_max_requests_per_minute);
BIND_ENUM_CONSTANT(PERM_READ);
BIND_ENUM_CONSTANT(PERM_WRITE);
BIND_ENUM_CONSTANT(PERM_EXECUTE);
BIND_ENUM_CONSTANT(PERM_ALL);
}
Dictionary AgentAuth::register_agent(const String &p_name, uint32_t p_permissions) {
MutexLock lock(auth_mutex);
AgentSession session;
session.agent_id = vformat("agent_%d", OS::get_singleton()->get_ticks_msec());
session.agent_name = p_name;
session.token = _generate_token();
session.permissions = p_permissions;
session.connected_at = OS::get_singleton()->get_ticks_msec();
session.last_activity = session.connected_at;
sessions[session.token] = session;
Dictionary result;
result["agent_id"] = session.agent_id;
result["token"] = session.token;
result["permissions"] = session.permissions;
print_line(vformat("AgentAuth: Registered agent '%s' (id: %s)", p_name, session.agent_id));
return result;
}
void AgentAuth::unregister_agent(const String &p_token) {
MutexLock lock(auth_mutex);
if (sessions.has(p_token)) {
print_line(vformat("AgentAuth: Unregistered agent '%s'", sessions[p_token].agent_name));
sessions.erase(p_token);
}
}
bool AgentAuth::validate_token(const String &p_token) {
MutexLock lock(auth_mutex);
return sessions.has(p_token);
}
bool AgentAuth::check_permission(const String &p_token, Permission p_perm) {
MutexLock lock(auth_mutex);
if (!sessions.has(p_token)) {
return false;
}
return (sessions[p_token].permissions & (uint32_t)p_perm) != 0;
}
void AgentAuth::record_activity(const String &p_token, const String &p_method, const String &p_path, int p_status) {
MutexLock lock(auth_mutex);
// Update session activity.
if (sessions.has(p_token)) {
sessions[p_token].last_activity = OS::get_singleton()->get_ticks_msec();
sessions[p_token].request_count++;
}
// Add to audit log.
AuditEntry entry;
entry.timestamp_msec = OS::get_singleton()->get_ticks_msec();
entry.agent_id = sessions.has(p_token) ? sessions[p_token].agent_id : "unknown";
entry.method = p_method;
entry.path = p_path;
entry.status_code = p_status;
audit_log.push_back(entry);
if (audit_log.size() > MAX_AUDIT) {
audit_log.remove_at(0);
}
}
bool AgentAuth::check_rate_limit(const String &p_token) {
MutexLock lock(auth_mutex);
if (!sessions.has(p_token)) {
return false;
}
// Simple rate limiting: check requests in the last minute.
// For a proper implementation, use a sliding window counter.
// This simplified version just checks total requests.
const AgentSession &session = sessions[p_token];
uint64_t elapsed = OS::get_singleton()->get_ticks_msec() - session.connected_at;
if (elapsed == 0) {
return true;
}
float minutes = (float)elapsed / 60000.0f;
float rate = (float)session.request_count / MAX(minutes, 0.01f);
return rate <= (float)max_requests_per_minute;
}
Array AgentAuth::get_agents() const {
Array result;
for (const KeyValue<String, AgentSession> &kv : sessions) {
Dictionary dict;
dict["agent_id"] = kv.value.agent_id;
dict["name"] = kv.value.agent_name;
dict["permissions"] = kv.value.permissions;
dict["connected_at"] = kv.value.connected_at;
dict["last_activity"] = kv.value.last_activity;
dict["request_count"] = kv.value.request_count;
result.push_back(dict);
}
return result;
}
Array AgentAuth::get_audit_log(int p_count) const {
Array result;
int start = MAX(0, audit_log.size() - p_count);
for (int i = start; i < audit_log.size(); i++) {
Dictionary dict;
dict["timestamp_msec"] = audit_log[i].timestamp_msec;
dict["agent_id"] = audit_log[i].agent_id;
dict["method"] = audit_log[i].method;
dict["path"] = audit_log[i].path;
dict["status_code"] = audit_log[i].status_code;
result.push_back(dict);
}
return result;
}
Dictionary AgentAuth::get_agent_info(const String &p_token) const {
Dictionary dict;
if (sessions.has(p_token)) {
const AgentSession &s = sessions[p_token];
dict["agent_id"] = s.agent_id;
dict["name"] = s.agent_name;
dict["permissions"] = s.permissions;
dict["connected_at"] = s.connected_at;
dict["last_activity"] = s.last_activity;
dict["request_count"] = s.request_count;
}
return dict;
}
String AgentAuth::_generate_token() {
// Generate a random hex token.
uint8_t bytes[32];
for (int i = 0; i < 32; i++) {
bytes[i] = (uint8_t)(Math::rand() % 256);
}
String token;
for (int i = 0; i < 32; i++) {
token += vformat("%02x", bytes[i]);
}
return "agent_" + token;
}