Catalyst / admin/Syanpse-Vanguard 14.8 GB / 57.8 GB 40.0 GB free
Help Sign in

admin / Syanpse-Vanguard

public
Code Issues Pull requests Pipelines Packages Security Insights Wiki Settings
Syanpse-Vanguard / synapse-vanguard-v3 / sv / ingest / registry.py 2581 B · main
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
"""Agent registry — enrollment, identity binding, revocation, replay bookkeeping."""
from __future__ import annotations

import hashlib
import secrets
import uuid
from datetime import datetime, timezone

from sv.config import DEFAULT_TENANT_ID
from sv.models.agent import AgentRegisterRequest
from sv.storage.base import Query, StorageEngine


def _now() -> datetime:
    return datetime.now(timezone.utc).replace(tzinfo=None)


def hash_token(token: str) -> str:
    return hashlib.sha256(token.encode()).hexdigest()


async def enroll(engine: StorageEngine, req: AgentRegisterRequest,
                 cert_cn: str | None, ip: str | None = None) -> tuple[str, str | None]:
    """Create an agent row. Returns (agent_id, ingest_token). In mTLS mode the
    token is None and identity is the cert CN; in dev mode a bearer token is
    issued and only its hash is stored."""
    import json

    agent_id = str(uuid.uuid4())
    ingest_token = None if cert_cn else secrets.token_urlsafe(32)
    await engine.run(Query(
        "INSERT INTO agents (id, tenant_id, hostname, platform, agent_version, "
        "channels, cert_cn, ingest_token_hash, last_seq, last_ip, revoked, enrolled_ts) "
        "VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0, ?, FALSE, ?)",
        [agent_id, str(DEFAULT_TENANT_ID), req.hostname, req.platform,
         req.agent_version, json.dumps(req.channels), cert_cn,
         hash_token(ingest_token) if ingest_token else None, ip, _now()],
    ))
    return agent_id, ingest_token


async def get_agent(engine: StorageEngine, agent_id: str) -> dict | None:
    return await engine.fetchone(Query(
        "SELECT id, cert_cn, ingest_token_hash, last_seq, revoked "
        "FROM agents WHERE id = ?", [agent_id],
    ))


async def find_by_cert_cn(engine: StorageEngine, cert_cn: str) -> dict | None:
    return await engine.fetchone(Query(
        "SELECT id, revoked FROM agents WHERE cert_cn = ?", [cert_cn],
    ))


async def touch_seq(engine: StorageEngine, agent_id: str, seq: int,
                    ip: str | None = None) -> None:
    if ip:
        await engine.run(Query(
            "UPDATE agents SET last_seq = ?, last_seen_ts = ?, last_ip = ? WHERE id = ?",
            [seq, _now(), ip, agent_id],
        ))
    else:
        await engine.run(Query(
            "UPDATE agents SET last_seq = ?, last_seen_ts = ? WHERE id = ?",
            [seq, _now(), agent_id],
        ))


async def revoke(engine: StorageEngine, agent_id: str) -> None:
    await engine.run(Query(
        "UPDATE agents SET revoked = TRUE WHERE id = ?", [agent_id],
    ))