auths-python 0.1.0__cp38-abi3-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
auths/devices.py ADDED
@@ -0,0 +1,162 @@
1
+ """Device resource service — Stripe-style API."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from typing import TYPE_CHECKING
7
+
8
+ from auths._native import (
9
+ extend_device_authorization_ffi as _extend_device,
10
+ link_device_to_identity as _link_device,
11
+ revoke_device_from_identity as _revoke_device,
12
+ )
13
+
14
+ if TYPE_CHECKING:
15
+ from auths._client import Auths
16
+
17
+
18
+ @dataclass
19
+ class Device:
20
+ """A linked device."""
21
+
22
+ did: str
23
+ """The device's DID (`did:key:z...`)."""
24
+ attestation_id: str
25
+ """RID of the attestation linking this device to its identity."""
26
+
27
+
28
+ @dataclass
29
+ class DeviceExtension:
30
+ """Result of extending a device's authorization period."""
31
+
32
+ device_did: str
33
+ """The device's DID (`did:key:z...`)."""
34
+ new_expires_at: str
35
+ """ISO 8601 timestamp of the new expiry."""
36
+ previous_expires_at: str | None
37
+ """ISO 8601 timestamp of the previous expiry, or None if none was set."""
38
+
39
+ def __repr__(self) -> str:
40
+ return (
41
+ f"DeviceExtension(device='{self.device_did[:20]}...', "
42
+ f"expires='{self.new_expires_at}')"
43
+ )
44
+
45
+
46
+ class DeviceService:
47
+ """Resource service for device operations.
48
+
49
+ Examples:
50
+ ```python
51
+ device = auths.devices.link(identity_did="did:keri:...", capabilities=["sign"])
52
+ auths.devices.revoke(device.did, identity_did="did:keri:...")
53
+ ```
54
+ """
55
+
56
+ def __init__(self, client: Auths):
57
+ self._client = client
58
+
59
+ def link(
60
+ self,
61
+ identity_did: str,
62
+ capabilities: list[str] | None = None,
63
+ expires_in: int | None = None,
64
+ passphrase: str | None = None,
65
+ ) -> Device:
66
+ """Link a new device to an identity.
67
+
68
+ Args:
69
+ identity_did: The identity to link this device to.
70
+ capabilities: Device capabilities (default: []).
71
+ expires_in: Duration in seconds until expiration (per RFC 6749).
72
+ passphrase: Key passphrase override.
73
+
74
+ Returns:
75
+ Device with the device DID and attestation ID.
76
+
77
+ Raises:
78
+ IdentityError: If the identity doesn't exist.
79
+ StorageError: If writing the attestation fails.
80
+
81
+ Examples:
82
+ ```python
83
+ device = auths.devices.link(identity.did, capabilities=["sign"], expires_in=7_776_000)
84
+ ```
85
+ """
86
+ pp = passphrase or self._client._passphrase
87
+ device_did, attestation_id = _link_device(
88
+ identity_did,
89
+ capabilities or [],
90
+ self._client.repo_path,
91
+ pp,
92
+ expires_in,
93
+ )
94
+ return Device(did=device_did, attestation_id=attestation_id)
95
+
96
+ def extend(
97
+ self,
98
+ device_did: str,
99
+ identity_did: str,
100
+ *,
101
+ days: int = 90,
102
+ passphrase: str | None = None,
103
+ ) -> DeviceExtension:
104
+ """Extend a device's authorization period.
105
+
106
+ Renews the device's expiry without revoking and re-linking.
107
+ Expired devices can be extended (grace period). Revoked devices cannot.
108
+
109
+ Args:
110
+ device_did: The device's DID (`did:key:z...`).
111
+ identity_did: The identity key alias for signing.
112
+ days: Number of days to extend from now (default: 90).
113
+ passphrase: Optional passphrase for keychain access.
114
+
115
+ Returns:
116
+ DeviceExtension with the new and previous expiry timestamps.
117
+
118
+ Raises:
119
+ IdentityError: If the device or identity doesn't exist.
120
+ VerificationError: If the device has been revoked.
121
+
122
+ Examples:
123
+ ```python
124
+ ext = auths.devices.extend(device.did, identity.did, days=90)
125
+ print(f"Extended until: {ext.new_expires_at}")
126
+ ```
127
+ """
128
+ pp = passphrase or self._client._passphrase
129
+ result = _extend_device(
130
+ device_did, identity_did, days, self._client.repo_path, pp
131
+ )
132
+ return DeviceExtension(
133
+ device_did=result.device_did,
134
+ new_expires_at=result.new_expires_at,
135
+ previous_expires_at=result.previous_expires_at,
136
+ )
137
+
138
+ def revoke(
139
+ self,
140
+ device_did: str,
141
+ identity_did: str,
142
+ note: str | None = None,
143
+ passphrase: str | None = None,
144
+ ) -> None:
145
+ """Revoke a device.
146
+
147
+ Args:
148
+ device_did: The device DID to revoke.
149
+ identity_did: The parent identity's DID.
150
+ note: Optional revocation note.
151
+ passphrase: Key passphrase override.
152
+
153
+ Raises:
154
+ IdentityError: If the device or identity doesn't exist.
155
+
156
+ Examples:
157
+ ```python
158
+ auths.devices.revoke(device.did, identity_did=identity.did, note="lost laptop")
159
+ ```
160
+ """
161
+ pp = passphrase or self._client._passphrase
162
+ _revoke_device(device_did, identity_did, self._client.repo_path, pp, note)
auths/doctor.py ADDED
@@ -0,0 +1,109 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from dataclasses import dataclass
5
+ from typing import Optional
6
+
7
+ from auths._native import run_diagnostics as _run_diagnostics
8
+ from auths._client import _map_error
9
+ from auths._errors import AuthsError
10
+
11
+
12
+ @dataclass
13
+ class Check:
14
+ """A single diagnostic check result."""
15
+
16
+ name: str
17
+ passed: bool
18
+ message: str
19
+ fix_hint: Optional[str]
20
+
21
+
22
+ @dataclass
23
+ class DiagnosticReport:
24
+ """Full health check report.
25
+
26
+ Attributes:
27
+ checks: Individual check results.
28
+ all_passed: True if every check passed.
29
+ version: Auths CLI/SDK version string (e.g. ``"0.9.0"``).
30
+ Useful for support tickets and compatibility checks.
31
+ """
32
+
33
+ checks: list[Check]
34
+ all_passed: bool
35
+ version: str
36
+ """Auths CLI/SDK version string (e.g. ``"0.9.0"``)."""
37
+
38
+
39
+ class DoctorService:
40
+ """Resource service for system diagnostics."""
41
+
42
+ #: Known diagnostic check names.
43
+ AVAILABLE_CHECKS: list[str] = [
44
+ "git_version",
45
+ "ssh_keygen",
46
+ "git_signing_config",
47
+ ]
48
+
49
+ def __init__(self, client):
50
+ self._client = client
51
+
52
+ @classmethod
53
+ def available_checks(cls) -> list[str]:
54
+ """Return the list of known diagnostic check names.
55
+
56
+ Examples:
57
+ ```python
58
+ for name in DoctorService.available_checks():
59
+ result = client.doctor.check_one(name)
60
+ ```
61
+ """
62
+ return list(cls.AVAILABLE_CHECKS)
63
+
64
+ def check(
65
+ self,
66
+ repo_path: str | None = None,
67
+ ) -> DiagnosticReport:
68
+ """Run all diagnostic checks.
69
+
70
+ Usage:
71
+ report = client.doctor.check()
72
+ if not report.all_passed:
73
+ for c in report.checks:
74
+ if not c.passed:
75
+ print(f"FAIL: {c.name} - {c.fix_hint}")
76
+ """
77
+ rp = repo_path or self._client.repo_path
78
+ try:
79
+ raw = _run_diagnostics(rp)
80
+ data = json.loads(raw)
81
+ checks = [
82
+ Check(
83
+ name=c["name"],
84
+ passed=c["passed"],
85
+ message=c.get("message", ""),
86
+ fix_hint=c.get("fix_hint"),
87
+ )
88
+ for c in data["checks"]
89
+ ]
90
+ return DiagnosticReport(
91
+ checks=checks,
92
+ all_passed=data["all_passed"],
93
+ version=data.get("version", ""),
94
+ )
95
+ except (ValueError, RuntimeError) as exc:
96
+ raise _map_error(exc, default_cls=AuthsError) from exc
97
+
98
+ def check_one(
99
+ self,
100
+ name: str,
101
+ repo_path: str | None = None,
102
+ ) -> Check | None:
103
+ """Run a single named diagnostic check.
104
+
105
+ Usage:
106
+ git_check = client.doctor.check_one("Git installed")
107
+ """
108
+ report = self.check(repo_path=repo_path)
109
+ return next((c for c in report.checks if c.name == name), None)