kstlib 0.0.1a0__py3-none-any.whl → 1.0.1__py3-none-any.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.
Files changed (166) hide show
  1. kstlib/__init__.py +266 -1
  2. kstlib/__main__.py +16 -0
  3. kstlib/alerts/__init__.py +110 -0
  4. kstlib/alerts/channels/__init__.py +36 -0
  5. kstlib/alerts/channels/base.py +197 -0
  6. kstlib/alerts/channels/email.py +227 -0
  7. kstlib/alerts/channels/slack.py +389 -0
  8. kstlib/alerts/exceptions.py +72 -0
  9. kstlib/alerts/manager.py +651 -0
  10. kstlib/alerts/models.py +142 -0
  11. kstlib/alerts/throttle.py +263 -0
  12. kstlib/auth/__init__.py +139 -0
  13. kstlib/auth/callback.py +399 -0
  14. kstlib/auth/config.py +502 -0
  15. kstlib/auth/errors.py +127 -0
  16. kstlib/auth/models.py +316 -0
  17. kstlib/auth/providers/__init__.py +14 -0
  18. kstlib/auth/providers/base.py +393 -0
  19. kstlib/auth/providers/oauth2.py +645 -0
  20. kstlib/auth/providers/oidc.py +821 -0
  21. kstlib/auth/session.py +338 -0
  22. kstlib/auth/token.py +482 -0
  23. kstlib/cache/__init__.py +50 -0
  24. kstlib/cache/decorator.py +261 -0
  25. kstlib/cache/strategies.py +516 -0
  26. kstlib/cli/__init__.py +8 -0
  27. kstlib/cli/app.py +195 -0
  28. kstlib/cli/commands/__init__.py +5 -0
  29. kstlib/cli/commands/auth/__init__.py +39 -0
  30. kstlib/cli/commands/auth/common.py +122 -0
  31. kstlib/cli/commands/auth/login.py +325 -0
  32. kstlib/cli/commands/auth/logout.py +74 -0
  33. kstlib/cli/commands/auth/providers.py +57 -0
  34. kstlib/cli/commands/auth/status.py +291 -0
  35. kstlib/cli/commands/auth/token.py +199 -0
  36. kstlib/cli/commands/auth/whoami.py +106 -0
  37. kstlib/cli/commands/config.py +89 -0
  38. kstlib/cli/commands/ops/__init__.py +39 -0
  39. kstlib/cli/commands/ops/attach.py +49 -0
  40. kstlib/cli/commands/ops/common.py +269 -0
  41. kstlib/cli/commands/ops/list_sessions.py +252 -0
  42. kstlib/cli/commands/ops/logs.py +49 -0
  43. kstlib/cli/commands/ops/start.py +98 -0
  44. kstlib/cli/commands/ops/status.py +138 -0
  45. kstlib/cli/commands/ops/stop.py +60 -0
  46. kstlib/cli/commands/rapi/__init__.py +60 -0
  47. kstlib/cli/commands/rapi/call.py +341 -0
  48. kstlib/cli/commands/rapi/list.py +99 -0
  49. kstlib/cli/commands/rapi/show.py +206 -0
  50. kstlib/cli/commands/secrets/__init__.py +35 -0
  51. kstlib/cli/commands/secrets/common.py +425 -0
  52. kstlib/cli/commands/secrets/decrypt.py +88 -0
  53. kstlib/cli/commands/secrets/doctor.py +743 -0
  54. kstlib/cli/commands/secrets/encrypt.py +242 -0
  55. kstlib/cli/commands/secrets/shred.py +96 -0
  56. kstlib/cli/common.py +86 -0
  57. kstlib/config/__init__.py +76 -0
  58. kstlib/config/exceptions.py +110 -0
  59. kstlib/config/export.py +225 -0
  60. kstlib/config/loader.py +963 -0
  61. kstlib/config/sops.py +287 -0
  62. kstlib/db/__init__.py +54 -0
  63. kstlib/db/aiosqlcipher.py +137 -0
  64. kstlib/db/cipher.py +112 -0
  65. kstlib/db/database.py +367 -0
  66. kstlib/db/exceptions.py +25 -0
  67. kstlib/db/pool.py +302 -0
  68. kstlib/helpers/__init__.py +35 -0
  69. kstlib/helpers/exceptions.py +11 -0
  70. kstlib/helpers/time_trigger.py +396 -0
  71. kstlib/kstlib.conf.yml +890 -0
  72. kstlib/limits.py +963 -0
  73. kstlib/logging/__init__.py +108 -0
  74. kstlib/logging/manager.py +633 -0
  75. kstlib/mail/__init__.py +42 -0
  76. kstlib/mail/builder.py +626 -0
  77. kstlib/mail/exceptions.py +27 -0
  78. kstlib/mail/filesystem.py +248 -0
  79. kstlib/mail/transport.py +224 -0
  80. kstlib/mail/transports/__init__.py +19 -0
  81. kstlib/mail/transports/gmail.py +268 -0
  82. kstlib/mail/transports/resend.py +324 -0
  83. kstlib/mail/transports/smtp.py +326 -0
  84. kstlib/meta.py +72 -0
  85. kstlib/metrics/__init__.py +88 -0
  86. kstlib/metrics/decorators.py +1090 -0
  87. kstlib/metrics/exceptions.py +14 -0
  88. kstlib/monitoring/__init__.py +116 -0
  89. kstlib/monitoring/_styles.py +163 -0
  90. kstlib/monitoring/cell.py +57 -0
  91. kstlib/monitoring/config.py +424 -0
  92. kstlib/monitoring/delivery.py +579 -0
  93. kstlib/monitoring/exceptions.py +63 -0
  94. kstlib/monitoring/image.py +220 -0
  95. kstlib/monitoring/kv.py +79 -0
  96. kstlib/monitoring/list.py +69 -0
  97. kstlib/monitoring/metric.py +88 -0
  98. kstlib/monitoring/monitoring.py +341 -0
  99. kstlib/monitoring/renderer.py +139 -0
  100. kstlib/monitoring/service.py +392 -0
  101. kstlib/monitoring/table.py +129 -0
  102. kstlib/monitoring/types.py +56 -0
  103. kstlib/ops/__init__.py +86 -0
  104. kstlib/ops/base.py +148 -0
  105. kstlib/ops/container.py +577 -0
  106. kstlib/ops/exceptions.py +209 -0
  107. kstlib/ops/manager.py +407 -0
  108. kstlib/ops/models.py +176 -0
  109. kstlib/ops/tmux.py +372 -0
  110. kstlib/ops/validators.py +287 -0
  111. kstlib/py.typed +0 -0
  112. kstlib/rapi/__init__.py +118 -0
  113. kstlib/rapi/client.py +875 -0
  114. kstlib/rapi/config.py +861 -0
  115. kstlib/rapi/credentials.py +887 -0
  116. kstlib/rapi/exceptions.py +213 -0
  117. kstlib/resilience/__init__.py +101 -0
  118. kstlib/resilience/circuit_breaker.py +440 -0
  119. kstlib/resilience/exceptions.py +95 -0
  120. kstlib/resilience/heartbeat.py +491 -0
  121. kstlib/resilience/rate_limiter.py +506 -0
  122. kstlib/resilience/shutdown.py +417 -0
  123. kstlib/resilience/watchdog.py +637 -0
  124. kstlib/secrets/__init__.py +29 -0
  125. kstlib/secrets/exceptions.py +19 -0
  126. kstlib/secrets/models.py +62 -0
  127. kstlib/secrets/providers/__init__.py +79 -0
  128. kstlib/secrets/providers/base.py +58 -0
  129. kstlib/secrets/providers/environment.py +66 -0
  130. kstlib/secrets/providers/keyring.py +107 -0
  131. kstlib/secrets/providers/kms.py +223 -0
  132. kstlib/secrets/providers/kwargs.py +101 -0
  133. kstlib/secrets/providers/sops.py +209 -0
  134. kstlib/secrets/resolver.py +221 -0
  135. kstlib/secrets/sensitive.py +130 -0
  136. kstlib/secure/__init__.py +23 -0
  137. kstlib/secure/fs.py +194 -0
  138. kstlib/secure/permissions.py +70 -0
  139. kstlib/ssl.py +347 -0
  140. kstlib/ui/__init__.py +23 -0
  141. kstlib/ui/exceptions.py +26 -0
  142. kstlib/ui/panels.py +484 -0
  143. kstlib/ui/spinner.py +864 -0
  144. kstlib/ui/tables.py +382 -0
  145. kstlib/utils/__init__.py +48 -0
  146. kstlib/utils/dict.py +36 -0
  147. kstlib/utils/formatting.py +338 -0
  148. kstlib/utils/http_trace.py +237 -0
  149. kstlib/utils/lazy.py +49 -0
  150. kstlib/utils/secure_delete.py +205 -0
  151. kstlib/utils/serialization.py +247 -0
  152. kstlib/utils/text.py +56 -0
  153. kstlib/utils/validators.py +124 -0
  154. kstlib/websocket/__init__.py +97 -0
  155. kstlib/websocket/exceptions.py +214 -0
  156. kstlib/websocket/manager.py +1102 -0
  157. kstlib/websocket/models.py +361 -0
  158. kstlib-1.0.1.dist-info/METADATA +201 -0
  159. kstlib-1.0.1.dist-info/RECORD +163 -0
  160. {kstlib-0.0.1a0.dist-info → kstlib-1.0.1.dist-info}/WHEEL +1 -1
  161. kstlib-1.0.1.dist-info/entry_points.txt +2 -0
  162. kstlib-1.0.1.dist-info/licenses/LICENSE.md +9 -0
  163. kstlib-0.0.1a0.dist-info/METADATA +0 -29
  164. kstlib-0.0.1a0.dist-info/RECORD +0 -6
  165. kstlib-0.0.1a0.dist-info/licenses/LICENSE.md +0 -5
  166. {kstlib-0.0.1a0.dist-info → kstlib-1.0.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,142 @@
1
+ """Data models for the alerts module.
2
+
3
+ This module defines the core data structures for alert messages, levels,
4
+ and delivery results.
5
+
6
+ Examples:
7
+ Creating an alert message::
8
+
9
+ from kstlib.alerts.models import AlertLevel, AlertMessage
10
+
11
+ alert = AlertMessage(
12
+ title="Server Down",
13
+ body="Production server api-1 is not responding",
14
+ level=AlertLevel.CRITICAL,
15
+ )
16
+
17
+ Checking alert results::
18
+
19
+ from kstlib.alerts.models import AlertResult
20
+
21
+ result = AlertResult(channel="slack", success=True, message_id="12345")
22
+ if result.success:
23
+ print(f"Alert delivered: {result.message_id}")
24
+ """
25
+
26
+ from __future__ import annotations
27
+
28
+ from dataclasses import dataclass
29
+ from datetime import datetime, timezone
30
+ from enum import IntEnum
31
+
32
+
33
+ class AlertLevel(IntEnum):
34
+ """Severity level for alerts.
35
+
36
+ Values are ordered by severity (higher value = more severe).
37
+ Use these to filter which alerts go to which channels.
38
+
39
+ Attributes:
40
+ INFO: Informational messages (10).
41
+ WARNING: Warning conditions that need attention (20).
42
+ CRITICAL: Critical issues requiring immediate action (30).
43
+
44
+ Examples:
45
+ >>> AlertLevel.CRITICAL > AlertLevel.WARNING
46
+ True
47
+ >>> AlertLevel.INFO.name
48
+ 'INFO'
49
+ >>> int(AlertLevel.WARNING)
50
+ 20
51
+ """
52
+
53
+ INFO = 10
54
+ WARNING = 20
55
+ CRITICAL = 30
56
+
57
+
58
+ @dataclass(frozen=True, slots=True)
59
+ class AlertMessage:
60
+ """An alert message to be sent via one or more channels.
61
+
62
+ Attributes:
63
+ title: Short summary of the alert (max 150 chars for Slack).
64
+ body: Detailed message content (max 3000 chars for Slack).
65
+ level: Severity level of the alert.
66
+ timestamp: If True, prefix title with send datetime.
67
+
68
+ Examples:
69
+ >>> msg = AlertMessage(title="Disk Full", body="Server disk at 95%")
70
+ >>> msg.level
71
+ <AlertLevel.INFO: 10>
72
+ >>> msg = AlertMessage(
73
+ ... title="DB Connection Failed",
74
+ ... body="Cannot connect to primary database",
75
+ ... level=AlertLevel.CRITICAL,
76
+ ... )
77
+ >>> msg.level.name
78
+ 'CRITICAL'
79
+ >>> msg = AlertMessage(
80
+ ... title="Alert",
81
+ ... body="With timestamp",
82
+ ... timestamp=True,
83
+ ... )
84
+ >>> ":::" in msg.formatted_title
85
+ True
86
+ """
87
+
88
+ title: str
89
+ body: str
90
+ level: AlertLevel = AlertLevel.INFO
91
+ timestamp: bool = False
92
+
93
+ @property
94
+ def formatted_title(self) -> str:
95
+ """Return title with optional timestamp prefix.
96
+
97
+ If timestamp is True, prefixes the title with current datetime
98
+ in format "YYYY-MM-DD HH:MM:SS ::: ".
99
+
100
+ Returns:
101
+ Title string, optionally prefixed with timestamp.
102
+
103
+ Examples:
104
+ >>> msg = AlertMessage(title="Test", body="Body", timestamp=False)
105
+ >>> msg.formatted_title
106
+ 'Test'
107
+ >>> msg = AlertMessage(title="Test", body="Body", timestamp=True)
108
+ >>> "Test" in msg.formatted_title
109
+ True
110
+ """
111
+ if self.timestamp:
112
+ now = datetime.now(timezone.utc).astimezone().strftime("%Y-%m-%d %H:%M:%S")
113
+ return f"{now} ::: {self.title}"
114
+ return self.title
115
+
116
+
117
+ @dataclass(frozen=True, slots=True)
118
+ class AlertResult:
119
+ """Result of sending an alert to a channel.
120
+
121
+ Attributes:
122
+ channel: Name of the channel that processed this alert.
123
+ success: Whether the alert was delivered successfully.
124
+ message_id: ID assigned by the channel (if available).
125
+ error: Error message if delivery failed.
126
+
127
+ Examples:
128
+ >>> result = AlertResult(channel="slack", success=True, message_id="msg123")
129
+ >>> result.success
130
+ True
131
+ >>> result = AlertResult(channel="email", success=False, error="SMTP timeout")
132
+ >>> result.error
133
+ 'SMTP timeout'
134
+ """
135
+
136
+ channel: str
137
+ success: bool
138
+ message_id: str | None = None
139
+ error: str | None = None
140
+
141
+
142
+ __all__ = ["AlertLevel", "AlertMessage", "AlertResult"]
@@ -0,0 +1,263 @@
1
+ """Alert throttling using rate limiting.
2
+
3
+ Provides rate limiting for alerts to prevent flooding channels during
4
+ incident storms. Wraps :class:`kstlib.resilience.rate_limiter.RateLimiter`.
5
+
6
+ Configuration is read from ``kstlib.conf.yml`` under the ``alerts.throttle``
7
+ section, with hard limits enforced for deep defense.
8
+
9
+ Examples:
10
+ Config-driven (recommended)::
11
+
12
+ from kstlib.alerts.throttle import AlertThrottle
13
+
14
+ # Uses alerts.throttle from kstlib.conf.yml
15
+ throttle = AlertThrottle()
16
+
17
+ if throttle.try_acquire():
18
+ await channel.send(alert)
19
+
20
+ Explicit override::
21
+
22
+ # Override config values
23
+ throttle = AlertThrottle(rate=5, per=30.0)
24
+
25
+ With async context::
26
+
27
+ throttle = AlertThrottle()
28
+
29
+ async with throttle:
30
+ await channel.send(alert) # Waits if rate limit hit
31
+ """
32
+
33
+ from __future__ import annotations
34
+
35
+ from typing import TYPE_CHECKING
36
+
37
+ from kstlib.alerts.exceptions import AlertThrottledError
38
+ from kstlib.limits import (
39
+ HARD_MAX_THROTTLE_PER,
40
+ HARD_MAX_THROTTLE_RATE,
41
+ HARD_MIN_THROTTLE_PER,
42
+ HARD_MIN_THROTTLE_RATE,
43
+ clamp_with_limits,
44
+ get_alerts_limits,
45
+ )
46
+ from kstlib.resilience.rate_limiter import RateLimiter
47
+
48
+ if TYPE_CHECKING:
49
+ from typing_extensions import Self
50
+
51
+ __all__ = ["AlertThrottle"]
52
+
53
+
54
+ class AlertThrottle:
55
+ """Rate limiter for alert delivery.
56
+
57
+ Wraps :class:`kstlib.resilience.rate_limiter.RateLimiter` with
58
+ alert-specific behavior and config-driven defaults.
59
+
60
+ All parameters are optional. If not provided, values are read from
61
+ ``kstlib.conf.yml`` under ``alerts.throttle``. Hard limits are enforced
62
+ for deep defense against misconfiguration.
63
+
64
+ Args:
65
+ rate: Maximum alerts per period. If None, uses config value.
66
+ Hard limits: [1, 1000].
67
+ per: Period duration in seconds. If None, uses config value.
68
+ Hard limits: [1.0, 86400.0] (1 day).
69
+ burst: Initial capacity. If None, defaults to rate value.
70
+ Hard limits: [1, rate].
71
+ name: Optional name for identification.
72
+
73
+ Examples:
74
+ Config-driven (recommended)::
75
+
76
+ throttle = AlertThrottle() # Uses kstlib.conf.yml
77
+
78
+ Explicit values::
79
+
80
+ throttle = AlertThrottle(rate=10, per=60.0)
81
+
82
+ Per-hour limiting with burst::
83
+
84
+ throttle = AlertThrottle(rate=100, per=3600.0, burst=20)
85
+
86
+ Non-blocking check::
87
+
88
+ if throttle.try_acquire():
89
+ send_alert()
90
+ else:
91
+ log.warning("Alert throttled")
92
+ """
93
+
94
+ def __init__(
95
+ self,
96
+ rate: float | None = None,
97
+ per: float | None = None,
98
+ *,
99
+ burst: float | None = None,
100
+ name: str | None = None,
101
+ ) -> None:
102
+ """Initialize AlertThrottle.
103
+
104
+ Args:
105
+ rate: Maximum alerts per period. If None, uses config.
106
+ per: Period duration in seconds. If None, uses config.
107
+ burst: Initial token count. If None, defaults to rate.
108
+ name: Optional name for identification.
109
+ """
110
+ # Load config defaults
111
+ limits = get_alerts_limits()
112
+
113
+ # Apply kwargs > config > defaults pattern with hard limit clamping
114
+ resolved_rate = clamp_with_limits(
115
+ rate if rate is not None else limits.throttle_rate,
116
+ HARD_MIN_THROTTLE_RATE,
117
+ HARD_MAX_THROTTLE_RATE,
118
+ )
119
+ resolved_per = clamp_with_limits(
120
+ per if per is not None else limits.throttle_per,
121
+ HARD_MIN_THROTTLE_PER,
122
+ HARD_MAX_THROTTLE_PER,
123
+ )
124
+
125
+ # Burst: kwargs > config > rate, clamped to [1, rate]
126
+ if burst is not None:
127
+ resolved_burst = clamp_with_limits(burst, 1, resolved_rate)
128
+ else:
129
+ resolved_burst = clamp_with_limits(limits.throttle_burst, 1, resolved_rate)
130
+
131
+ self._limiter = RateLimiter(
132
+ rate=resolved_rate,
133
+ per=resolved_per,
134
+ burst=resolved_burst,
135
+ name=name,
136
+ )
137
+
138
+ @property
139
+ def rate(self) -> float:
140
+ """Maximum alerts per period."""
141
+ return self._limiter.rate
142
+
143
+ @property
144
+ def per(self) -> float:
145
+ """Period duration in seconds."""
146
+ return self._limiter.per
147
+
148
+ @property
149
+ def available(self) -> float:
150
+ """Current available tokens (alerts allowed)."""
151
+ return self._limiter.tokens
152
+
153
+ @property
154
+ def time_until_available(self) -> float:
155
+ """Seconds until next alert can be sent."""
156
+ return self._limiter.time_until_token()
157
+
158
+ def try_acquire(self) -> bool:
159
+ """Try to acquire permission to send an alert.
160
+
161
+ Returns:
162
+ True if alert can be sent, False if throttled.
163
+
164
+ Examples:
165
+ >>> throttle = AlertThrottle(rate=2, per=1.0)
166
+ >>> throttle.try_acquire()
167
+ True
168
+ >>> throttle.try_acquire()
169
+ True
170
+ >>> throttle.try_acquire() # Throttled
171
+ False
172
+ """
173
+ return self._limiter.try_acquire()
174
+
175
+ def acquire(self, *, timeout: float | None = None) -> None:
176
+ """Acquire permission to send an alert, blocking if needed.
177
+
178
+ Args:
179
+ timeout: Maximum time to wait in seconds.
180
+
181
+ Raises:
182
+ AlertThrottledError: If timeout exceeded.
183
+
184
+ Examples:
185
+ >>> throttle = AlertThrottle(rate=10, per=60.0)
186
+ >>> throttle.acquire() # Blocks if needed
187
+ """
188
+ try:
189
+ self._limiter.acquire(blocking=True, timeout=timeout)
190
+ except Exception:
191
+ raise AlertThrottledError(
192
+ "Alert rate limit exceeded",
193
+ retry_after=self.time_until_available,
194
+ ) from None
195
+
196
+ async def acquire_async(self, *, timeout: float | None = None) -> None:
197
+ """Acquire permission asynchronously, waiting if needed.
198
+
199
+ Args:
200
+ timeout: Maximum time to wait in seconds.
201
+
202
+ Raises:
203
+ AlertThrottledError: If timeout exceeded.
204
+
205
+ Examples:
206
+ >>> import asyncio
207
+ >>> throttle = AlertThrottle(rate=10, per=60.0)
208
+ >>> asyncio.run(throttle.acquire_async())
209
+ """
210
+ try:
211
+ await self._limiter.acquire_async(timeout=timeout)
212
+ except Exception:
213
+ raise AlertThrottledError(
214
+ "Alert rate limit exceeded",
215
+ retry_after=self.time_until_available,
216
+ ) from None
217
+
218
+ def reset(self) -> None:
219
+ """Reset throttle to full capacity.
220
+
221
+ Examples:
222
+ >>> throttle = AlertThrottle(rate=1, per=60.0)
223
+ >>> throttle.try_acquire()
224
+ True
225
+ >>> throttle.try_acquire()
226
+ False
227
+ >>> throttle.reset()
228
+ >>> throttle.try_acquire()
229
+ True
230
+ """
231
+ self._limiter.reset()
232
+
233
+ def __enter__(self) -> Self:
234
+ """Enter context manager, acquiring permission."""
235
+ self.acquire()
236
+ return self
237
+
238
+ def __exit__(
239
+ self,
240
+ exc_type: type[BaseException] | None,
241
+ exc_val: BaseException | None,
242
+ exc_tb: object,
243
+ ) -> None:
244
+ """Exit context manager."""
245
+ pass
246
+
247
+ async def __aenter__(self) -> Self:
248
+ """Enter async context manager, acquiring permission."""
249
+ await self.acquire_async()
250
+ return self
251
+
252
+ async def __aexit__(
253
+ self,
254
+ exc_type: type[BaseException] | None,
255
+ exc_val: BaseException | None,
256
+ exc_tb: object,
257
+ ) -> None:
258
+ """Exit async context manager."""
259
+ pass
260
+
261
+ def __repr__(self) -> str:
262
+ """Return string representation."""
263
+ return f"AlertThrottle(rate={self.rate}, per={self.per})"
@@ -0,0 +1,139 @@
1
+ # pylint: disable=duplicate-code
2
+ """OAuth2/OIDC authentication module for kstlib.
3
+
4
+ This module provides a config-driven authentication layer supporting:
5
+ - OAuth2 Authorization Code flow
6
+ - OIDC with PKCE extension
7
+ - Automatic token refresh
8
+ - Secure token storage (SOPS encrypted or memory)
9
+ - Preflight validation for IdP configuration
10
+
11
+ Example (explicit configuration):
12
+ >>> from kstlib.auth import AuthSession, OIDCProvider, AuthProviderConfig # doctest: +SKIP
13
+ >>> from kstlib.auth import MemoryTokenStorage # doctest: +SKIP
14
+ >>>
15
+ >>> config = AuthProviderConfig( # doctest: +SKIP
16
+ ... client_id="my-app",
17
+ ... issuer="https://auth.example.com",
18
+ ... scopes=["openid", "profile"],
19
+ ... )
20
+ >>> provider = OIDCProvider("example", config, MemoryTokenStorage()) # doctest: +SKIP
21
+ >>> with AuthSession(provider) as session: # doctest: +SKIP
22
+ ... resp = session.get("https://api.example.com/users/me")
23
+
24
+ Example (config-driven):
25
+ >>> # Configure in kstlib.conf.yml:
26
+ >>> # auth:
27
+ >>> # providers:
28
+ >>> # corporate:
29
+ >>> # type: oidc
30
+ >>> # issuer: https://idp.corp.local/realms/main
31
+ >>> # client_id: my-app
32
+ >>> # pkce: true
33
+ >>> from kstlib.auth import OIDCProvider, AuthSession
34
+ >>> provider = OIDCProvider.from_config("corporate") # doctest: +SKIP
35
+ >>> with AuthSession(provider) as session: # doctest: +SKIP
36
+ ... resp = session.get("https://api.corp.local/users/me")
37
+
38
+ See Also:
39
+ - :mod:`kstlib.auth.config` for config loading helpers
40
+ - :mod:`kstlib.auth.models` for data models
41
+ - :mod:`kstlib.auth.errors` for exception hierarchy
42
+ - :mod:`kstlib.auth.providers` for provider implementations
43
+ """
44
+
45
+ from __future__ import annotations
46
+
47
+ from kstlib.auth.callback import CallbackResult, CallbackServer
48
+ from kstlib.auth.config import (
49
+ build_provider_config,
50
+ get_auth_config,
51
+ get_callback_server_config,
52
+ get_default_provider_name,
53
+ get_provider_config,
54
+ get_token_storage_from_config,
55
+ list_configured_providers,
56
+ )
57
+ from kstlib.auth.errors import (
58
+ AuthError,
59
+ AuthorizationError,
60
+ CallbackServerError,
61
+ ConfigurationError,
62
+ DiscoveryError,
63
+ PreflightError,
64
+ ProviderNotFoundError,
65
+ TokenError,
66
+ TokenExchangeError,
67
+ TokenExpiredError,
68
+ TokenRefreshError,
69
+ TokenStorageError,
70
+ TokenValidationError,
71
+ )
72
+ from kstlib.auth.models import (
73
+ AuthFlow,
74
+ PreflightReport,
75
+ PreflightResult,
76
+ PreflightStatus,
77
+ Token,
78
+ TokenType,
79
+ )
80
+ from kstlib.auth.providers import (
81
+ AbstractAuthProvider,
82
+ AuthProviderConfig,
83
+ OAuth2Provider,
84
+ OIDCProvider,
85
+ )
86
+ from kstlib.auth.session import AuthSession
87
+ from kstlib.auth.token import (
88
+ AbstractTokenStorage,
89
+ MemoryTokenStorage,
90
+ SOPSTokenStorage,
91
+ get_token_storage,
92
+ )
93
+
94
+ __all__ = [
95
+ # Providers
96
+ "AbstractAuthProvider",
97
+ # Token storage
98
+ "AbstractTokenStorage",
99
+ # Errors
100
+ "AuthError",
101
+ # Models
102
+ "AuthFlow",
103
+ "AuthProviderConfig",
104
+ # Session
105
+ "AuthSession",
106
+ "AuthorizationError",
107
+ # Callback server
108
+ "CallbackResult",
109
+ "CallbackServer",
110
+ "CallbackServerError",
111
+ "ConfigurationError",
112
+ "DiscoveryError",
113
+ "MemoryTokenStorage",
114
+ "OAuth2Provider",
115
+ "OIDCProvider",
116
+ "PreflightError",
117
+ "PreflightReport",
118
+ "PreflightResult",
119
+ "PreflightStatus",
120
+ "ProviderNotFoundError",
121
+ "SOPSTokenStorage",
122
+ "Token",
123
+ "TokenError",
124
+ "TokenExchangeError",
125
+ "TokenExpiredError",
126
+ "TokenRefreshError",
127
+ "TokenStorageError",
128
+ "TokenType",
129
+ "TokenValidationError",
130
+ # Config helpers
131
+ "build_provider_config",
132
+ "get_auth_config",
133
+ "get_callback_server_config",
134
+ "get_default_provider_name",
135
+ "get_provider_config",
136
+ "get_token_storage",
137
+ "get_token_storage_from_config",
138
+ "list_configured_providers",
139
+ ]