network-ai 3.3.0 → 3.3.2

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.
@@ -1,243 +1,243 @@
1
- #!/usr/bin/env python3
2
- """
3
- Revoke Grant Token & TTL Enforcement
4
-
5
- Revoke permission tokens and automatically cleanup expired grants.
6
-
7
- Usage:
8
- python revoke_token.py TOKEN # Revoke specific token
9
- python revoke_token.py --cleanup # Remove all expired tokens
10
- python revoke_token.py --list-expired # List expired tokens without removing
11
-
12
- Example:
13
- python revoke_token.py grant_a1b2c3d4e5f6
14
- python revoke_token.py --cleanup --json
15
- """
16
-
17
- import argparse
18
- import json
19
- import sys
20
- from datetime import datetime, timezone
21
- from pathlib import Path
22
- from typing import Any
23
-
24
- GRANTS_FILE = Path(__file__).parent.parent / "data" / "active_grants.json"
25
- AUDIT_LOG = Path(__file__).parent.parent / "data" / "audit_log.jsonl"
26
-
27
-
28
- def log_audit(action: str, details: dict[str, Any]) -> None:
29
- """Append entry to audit log."""
30
- AUDIT_LOG.parent.mkdir(exist_ok=True)
31
- entry: dict[str, Any] = {
32
- "timestamp": datetime.now(timezone.utc).isoformat(),
33
- "action": action,
34
- "details": details
35
- }
36
- with open(AUDIT_LOG, "a") as f:
37
- f.write(json.dumps(entry) + "\n")
38
-
39
-
40
- def revoke_token(token: str) -> dict[str, Any]:
41
- """Revoke a grant token."""
42
- if not GRANTS_FILE.exists():
43
- return {
44
- "revoked": False,
45
- "reason": "No grants file found"
46
- }
47
-
48
- try:
49
- grants = json.loads(GRANTS_FILE.read_text())
50
- except json.JSONDecodeError:
51
- return {
52
- "revoked": False,
53
- "reason": "Invalid grants file"
54
- }
55
-
56
- if token not in grants:
57
- return {
58
- "revoked": False,
59
- "reason": "Token not found"
60
- }
61
-
62
- grant = grants.pop(token)
63
- GRANTS_FILE.write_text(json.dumps(grants, indent=2))
64
-
65
- log_audit("permission_revoked", {
66
- "token": token,
67
- "original_grant": grant
68
- })
69
-
70
- return {
71
- "revoked": True,
72
- "grant": grant
73
- }
74
-
75
-
76
- def is_token_expired(grant: dict[str, Any]) -> bool:
77
- """Check if a grant token has expired."""
78
- expires_at = grant.get("expires_at")
79
- if not expires_at:
80
- return False
81
- try:
82
- expiry_time = datetime.fromisoformat(expires_at.replace("Z", "+00:00"))
83
- return datetime.now(timezone.utc) > expiry_time
84
- except (ValueError, AttributeError):
85
- return False
86
-
87
-
88
- def list_expired_tokens() -> dict[str, Any]:
89
- """List all expired tokens without removing them."""
90
- if not GRANTS_FILE.exists():
91
- return {"expired_tokens": [], "total_grants": 0}
92
-
93
- try:
94
- grants = json.loads(GRANTS_FILE.read_text())
95
- except json.JSONDecodeError:
96
- return {"error": "Invalid grants file"}
97
-
98
- expired: list[dict[str, Any]] = []
99
- now = datetime.now(timezone.utc)
100
-
101
- for token, grant in grants.items():
102
- if is_token_expired(grant):
103
- expired.append({
104
- "token": token,
105
- "agent": grant.get("agent_id"),
106
- "resource": grant.get("resource_type"),
107
- "expired_at": grant.get("expires_at")
108
- })
109
-
110
- return {
111
- "expired_tokens": expired,
112
- "expired_count": len(expired),
113
- "total_grants": len(grants),
114
- "checked_at": now.isoformat()
115
- }
116
-
117
-
118
- def cleanup_expired_tokens() -> dict[str, Any]:
119
- """Remove all expired tokens from active grants (TTL enforcement)."""
120
- if not GRANTS_FILE.exists():
121
- return {
122
- "cleaned": 0,
123
- "remaining": 0,
124
- "message": "No grants file found"
125
- }
126
-
127
- try:
128
- grants = json.loads(GRANTS_FILE.read_text())
129
- except json.JSONDecodeError:
130
- return {
131
- "cleaned": 0,
132
- "error": "Invalid grants file"
133
- }
134
-
135
- original_count = len(grants)
136
- expired_tokens: list[dict[str, Any]] = []
137
-
138
- # Find and remove expired tokens
139
- tokens_to_remove: list[str] = []
140
- for token, grant in grants.items():
141
- if is_token_expired(grant):
142
- tokens_to_remove.append(token)
143
- expired_tokens.append({
144
- "token": token,
145
- "agent": grant.get("agent_id"),
146
- "resource": grant.get("resource_type"),
147
- "expired_at": grant.get("expires_at")
148
- })
149
-
150
- for token in tokens_to_remove:
151
- grants.pop(token)
152
-
153
- # Write back cleaned grants
154
- GRANTS_FILE.write_text(json.dumps(grants, indent=2))
155
-
156
- # Log the cleanup
157
- if expired_tokens:
158
- log_audit("ttl_cleanup", {
159
- "expired_count": len(expired_tokens),
160
- "expired_tokens": expired_tokens,
161
- "remaining_grants": len(grants)
162
- })
163
-
164
- return {
165
- "cleaned": len(expired_tokens),
166
- "expired_tokens": expired_tokens,
167
- "remaining": len(grants),
168
- "original_count": original_count,
169
- "cleaned_at": datetime.now(timezone.utc).isoformat()
170
- }
171
-
172
-
173
- def main():
174
- parser = argparse.ArgumentParser(
175
- description="Revoke permission grant tokens and enforce TTL cleanup"
176
- )
177
- parser.add_argument("token", nargs="?", help="Grant token to revoke")
178
- parser.add_argument("--cleanup", action="store_true",
179
- help="Remove all expired tokens (TTL enforcement)")
180
- parser.add_argument("--list-expired", action="store_true",
181
- help="List expired tokens without removing")
182
- parser.add_argument("--json", action="store_true", help="Output as JSON")
183
-
184
- args = parser.parse_args()
185
-
186
- # Handle --list-expired
187
- if args.list_expired:
188
- result = list_expired_tokens()
189
- if args.json:
190
- print(json.dumps(result, indent=2))
191
- else:
192
- expired = result.get("expired_tokens", [])
193
- if expired:
194
- print(f"⏰ Found {len(expired)} expired token(s):")
195
- for t in expired:
196
- print(f" • {t['token'][:20]}... ({t['agent']} → {t['resource']})")
197
- print(f" Expired: {t['expired_at']}")
198
- else:
199
- print("✅ No expired tokens found")
200
- print(f"\n Total grants: {result.get('total_grants', 0)}")
201
- sys.exit(0)
202
-
203
- # Handle --cleanup
204
- if args.cleanup:
205
- result = cleanup_expired_tokens()
206
- if args.json:
207
- print(json.dumps(result, indent=2))
208
- else:
209
- cleaned = result.get("cleaned", 0)
210
- if cleaned > 0:
211
- print(f"🧹 TTL Cleanup Complete")
212
- print(f" Removed: {cleaned} expired token(s)")
213
- for t in result.get("expired_tokens", []):
214
- print(f" • {t['token'][:20]}... ({t['agent']})")
215
- else:
216
- print("✅ No expired tokens to clean")
217
- print(f" Remaining active grants: {result.get('remaining', 0)}")
218
- sys.exit(0)
219
-
220
- # Handle single token revocation
221
- if not args.token:
222
- parser.print_help()
223
- sys.exit(1)
224
-
225
- result = revoke_token(args.token)
226
-
227
- if args.json:
228
- print(json.dumps(result, indent=2))
229
- else:
230
- if result["revoked"]:
231
- grant = result["grant"]
232
- print("✅ Token REVOKED")
233
- print(f" Agent: {grant.get('agent_id')}")
234
- print(f" Resource: {grant.get('resource_type')}")
235
- else:
236
- print("❌ Revocation FAILED")
237
- print(f" Reason: {result.get('reason')}")
238
-
239
- sys.exit(0 if result.get("revoked") or result.get("cleaned", 0) >= 0 else 1)
240
-
241
-
242
- if __name__ == "__main__":
243
- main()
1
+ #!/usr/bin/env python3
2
+ """
3
+ Revoke Grant Token & TTL Enforcement
4
+
5
+ Revoke permission tokens and automatically cleanup expired grants.
6
+
7
+ Usage:
8
+ python revoke_token.py TOKEN # Revoke specific token
9
+ python revoke_token.py --cleanup # Remove all expired tokens
10
+ python revoke_token.py --list-expired # List expired tokens without removing
11
+
12
+ Example:
13
+ python revoke_token.py grant_a1b2c3d4e5f6
14
+ python revoke_token.py --cleanup --json
15
+ """
16
+
17
+ import argparse
18
+ import json
19
+ import sys
20
+ from datetime import datetime, timezone
21
+ from pathlib import Path
22
+ from typing import Any
23
+
24
+ GRANTS_FILE = Path(__file__).parent.parent / "data" / "active_grants.json"
25
+ AUDIT_LOG = Path(__file__).parent.parent / "data" / "audit_log.jsonl"
26
+
27
+
28
+ def log_audit(action: str, details: dict[str, Any]) -> None:
29
+ """Append entry to audit log."""
30
+ AUDIT_LOG.parent.mkdir(exist_ok=True)
31
+ entry: dict[str, Any] = {
32
+ "timestamp": datetime.now(timezone.utc).isoformat(),
33
+ "action": action,
34
+ "details": details
35
+ }
36
+ with open(AUDIT_LOG, "a") as f:
37
+ f.write(json.dumps(entry) + "\n")
38
+
39
+
40
+ def revoke_token(token: str) -> dict[str, Any]:
41
+ """Revoke a grant token."""
42
+ if not GRANTS_FILE.exists():
43
+ return {
44
+ "revoked": False,
45
+ "reason": "No grants file found"
46
+ }
47
+
48
+ try:
49
+ grants = json.loads(GRANTS_FILE.read_text())
50
+ except json.JSONDecodeError:
51
+ return {
52
+ "revoked": False,
53
+ "reason": "Invalid grants file"
54
+ }
55
+
56
+ if token not in grants:
57
+ return {
58
+ "revoked": False,
59
+ "reason": "Token not found"
60
+ }
61
+
62
+ grant = grants.pop(token)
63
+ GRANTS_FILE.write_text(json.dumps(grants, indent=2))
64
+
65
+ log_audit("permission_revoked", {
66
+ "token": token,
67
+ "original_grant": grant
68
+ })
69
+
70
+ return {
71
+ "revoked": True,
72
+ "grant": grant
73
+ }
74
+
75
+
76
+ def is_token_expired(grant: dict[str, Any]) -> bool:
77
+ """Check if a grant token has expired."""
78
+ expires_at = grant.get("expires_at")
79
+ if not expires_at:
80
+ return False
81
+ try:
82
+ expiry_time = datetime.fromisoformat(expires_at.replace("Z", "+00:00"))
83
+ return datetime.now(timezone.utc) > expiry_time
84
+ except (ValueError, AttributeError):
85
+ return False
86
+
87
+
88
+ def list_expired_tokens() -> dict[str, Any]:
89
+ """List all expired tokens without removing them."""
90
+ if not GRANTS_FILE.exists():
91
+ return {"expired_tokens": [], "total_grants": 0}
92
+
93
+ try:
94
+ grants = json.loads(GRANTS_FILE.read_text())
95
+ except json.JSONDecodeError:
96
+ return {"error": "Invalid grants file"}
97
+
98
+ expired: list[dict[str, Any]] = []
99
+ now = datetime.now(timezone.utc)
100
+
101
+ for token, grant in grants.items():
102
+ if is_token_expired(grant):
103
+ expired.append({
104
+ "token": token,
105
+ "agent": grant.get("agent_id"),
106
+ "resource": grant.get("resource_type"),
107
+ "expired_at": grant.get("expires_at")
108
+ })
109
+
110
+ return {
111
+ "expired_tokens": expired,
112
+ "expired_count": len(expired),
113
+ "total_grants": len(grants),
114
+ "checked_at": now.isoformat()
115
+ }
116
+
117
+
118
+ def cleanup_expired_tokens() -> dict[str, Any]:
119
+ """Remove all expired tokens from active grants (TTL enforcement)."""
120
+ if not GRANTS_FILE.exists():
121
+ return {
122
+ "cleaned": 0,
123
+ "remaining": 0,
124
+ "message": "No grants file found"
125
+ }
126
+
127
+ try:
128
+ grants = json.loads(GRANTS_FILE.read_text())
129
+ except json.JSONDecodeError:
130
+ return {
131
+ "cleaned": 0,
132
+ "error": "Invalid grants file"
133
+ }
134
+
135
+ original_count = len(grants)
136
+ expired_tokens: list[dict[str, Any]] = []
137
+
138
+ # Find and remove expired tokens
139
+ tokens_to_remove: list[str] = []
140
+ for token, grant in grants.items():
141
+ if is_token_expired(grant):
142
+ tokens_to_remove.append(token)
143
+ expired_tokens.append({
144
+ "token": token,
145
+ "agent": grant.get("agent_id"),
146
+ "resource": grant.get("resource_type"),
147
+ "expired_at": grant.get("expires_at")
148
+ })
149
+
150
+ for token in tokens_to_remove:
151
+ grants.pop(token)
152
+
153
+ # Write back cleaned grants
154
+ GRANTS_FILE.write_text(json.dumps(grants, indent=2))
155
+
156
+ # Log the cleanup
157
+ if expired_tokens:
158
+ log_audit("ttl_cleanup", {
159
+ "expired_count": len(expired_tokens),
160
+ "expired_tokens": expired_tokens,
161
+ "remaining_grants": len(grants)
162
+ })
163
+
164
+ return {
165
+ "cleaned": len(expired_tokens),
166
+ "expired_tokens": expired_tokens,
167
+ "remaining": len(grants),
168
+ "original_count": original_count,
169
+ "cleaned_at": datetime.now(timezone.utc).isoformat()
170
+ }
171
+
172
+
173
+ def main():
174
+ parser = argparse.ArgumentParser(
175
+ description="Revoke permission grant tokens and enforce TTL cleanup"
176
+ )
177
+ parser.add_argument("token", nargs="?", help="Grant token to revoke")
178
+ parser.add_argument("--cleanup", action="store_true",
179
+ help="Remove all expired tokens (TTL enforcement)")
180
+ parser.add_argument("--list-expired", action="store_true",
181
+ help="List expired tokens without removing")
182
+ parser.add_argument("--json", action="store_true", help="Output as JSON")
183
+
184
+ args = parser.parse_args()
185
+
186
+ # Handle --list-expired
187
+ if args.list_expired:
188
+ result = list_expired_tokens()
189
+ if args.json:
190
+ print(json.dumps(result, indent=2))
191
+ else:
192
+ expired = result.get("expired_tokens", [])
193
+ if expired:
194
+ print(f"⏰ Found {len(expired)} expired token(s):")
195
+ for t in expired:
196
+ print(f" • {t['token'][:20]}... ({t['agent']} → {t['resource']})")
197
+ print(f" Expired: {t['expired_at']}")
198
+ else:
199
+ print("✅ No expired tokens found")
200
+ print(f"\n Total grants: {result.get('total_grants', 0)}")
201
+ sys.exit(0)
202
+
203
+ # Handle --cleanup
204
+ if args.cleanup:
205
+ result = cleanup_expired_tokens()
206
+ if args.json:
207
+ print(json.dumps(result, indent=2))
208
+ else:
209
+ cleaned = result.get("cleaned", 0)
210
+ if cleaned > 0:
211
+ print(f"🧹 TTL Cleanup Complete")
212
+ print(f" Removed: {cleaned} expired token(s)")
213
+ for t in result.get("expired_tokens", []):
214
+ print(f" • {t['token'][:20]}... ({t['agent']})")
215
+ else:
216
+ print("✅ No expired tokens to clean")
217
+ print(f" Remaining active grants: {result.get('remaining', 0)}")
218
+ sys.exit(0)
219
+
220
+ # Handle single token revocation
221
+ if not args.token:
222
+ parser.print_help()
223
+ sys.exit(1)
224
+
225
+ result = revoke_token(args.token)
226
+
227
+ if args.json:
228
+ print(json.dumps(result, indent=2))
229
+ else:
230
+ if result["revoked"]:
231
+ grant = result["grant"]
232
+ print("✅ Token REVOKED")
233
+ print(f" Agent: {grant.get('agent_id')}")
234
+ print(f" Resource: {grant.get('resource_type')}")
235
+ else:
236
+ print("❌ Revocation FAILED")
237
+ print(f" Reason: {result.get('reason')}")
238
+
239
+ sys.exit(0 if result.get("revoked") or result.get("cleaned", 0) >= 0 else 1)
240
+
241
+
242
+ if __name__ == "__main__":
243
+ main()