src/pqc_agent_wallet/credential.py
1.5 KB · 52 lines · python Raw
1 """Credential data structures."""
2
3 from __future__ import annotations
4
5 from dataclasses import asdict, dataclass, field
6 from typing import Any
7
8
9 @dataclass
10 class CredentialMetadata:
11 """Non-secret metadata about a credential."""
12
13 name: str
14 scheme: str = "api-key" # api-key | oauth | password | cert | token
15 service: str = "" # e.g. "openai", "anthropic", "postgres"
16 description: str = ""
17 created_at: str = ""
18 rotated_at: str = ""
19 expires_at: str = ""
20 tags: list[str] = field(default_factory=list)
21
22 def to_dict(self) -> dict[str, Any]:
23 return asdict(self)
24
25 @classmethod
26 def from_dict(cls, data: dict[str, Any]) -> CredentialMetadata:
27 return cls(
28 name=data["name"],
29 scheme=data.get("scheme", "api-key"),
30 service=data.get("service", ""),
31 description=data.get("description", ""),
32 created_at=data.get("created_at", ""),
33 rotated_at=data.get("rotated_at", ""),
34 expires_at=data.get("expires_at", ""),
35 tags=list(data.get("tags", [])),
36 )
37
38
39 @dataclass
40 class Credential:
41 """A stored credential. The `value` is plaintext only when wallet is unlocked."""
42
43 metadata: CredentialMetadata
44 value: str = "" # secret in plaintext (only when unlocked)
45
46 def to_safe_dict(self) -> dict[str, Any]:
47 """Serialize without the secret (for logging or public display)."""
48 return {
49 "metadata": self.metadata.to_dict(),
50 "value": "<redacted>",
51 }
52