File size: 2,464 Bytes
2f1e7ff
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""Report legacy credential status or atomically replace plaintext with a hash.

No credential plaintext is emitted. Migration retains the existing secret during
the compatibility window; operators must subsequently issue named replacements,
update agents, and revoke the migrated credential, particularly after log exposure.
"""

import argparse
import json
from uuid import uuid4
from src.services.auth import token_digest
from src.services.credentials import token_repository
from src.services.brain_identity import validate_brain_id
from src.services.data.main import data_adapter


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--migrate", metavar="BRAIN_ID", help="Move one legacy PAT to the hash-only registry (PostgreSQL)")
    args = parser.parse_args()
    if not args.migrate:
        for brain in data_adapter.get_brains_list():
            print(json.dumps({"brain_id": brain.name_key, "legacy_plaintext_present": bool(brain.pat),
                              "rotation_required": bool(brain.pat)}))
        return
    from src.config import config
    if config.data_db != "postgresql":
        parser.error("Atomic legacy migration requires the PostgreSQL data backend")
    brain_id = validate_brain_id(args.migrate)
    token_repository.initialize()
    # Lock the source and migrate both representations in ONE system transaction.
    with token_repository.connection() as conn:
        with conn.cursor() as cur:
            cur.execute("SELECT id,pat FROM data_brains WHERE name_key=%s FOR UPDATE", (brain_id,))
            row = cur.fetchone()
            if row is None:
                parser.error("Brain not found")
            if row[1]:
                cur.execute("""INSERT INTO access_tokens
                    (id,label,brain_id,brain_resource_id,token_prefix,token_hash,permissions,legacy,created_by)
                    VALUES (%s,%s,%s,%s,%s,%s,%s,TRUE,'legacy-migration')
                    ON CONFLICT (token_hash) DO NOTHING""",
                    (str(uuid4()), 'migrated-legacy', brain_id, row[0], 'legacy', token_digest(row[1]),
                     ['memory:read', 'memory:write', 'brain:read']))
                cur.execute("UPDATE data_brains SET pat='',document=document-'pat' WHERE name_key=%s", (brain_id,))
        conn.commit()
    print(json.dumps({"brain_id": brain_id, "status": "hash-only", "rotation_required": True}))


if __name__ == "__main__":
    main()