Skip to content

Commit ba206bb

Browse files
committed
Managed: Use the keyrings-cryptfile library for caching the JWT token
Also, implement token refresh after expiry.
1 parent 171de24 commit ba206bb

File tree

4 files changed

+27
-6
lines changed

4 files changed

+27
-6
lines changed

cratedb_toolkit/cluster/croud.py

+23
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import dataclasses
2+
import datetime as dt
23
import json
34
import logging
45
import os
@@ -7,6 +8,7 @@
78

89
from croud.clusters.commands import _wait_for_completed_operation
910
from croud.projects.commands import _transform_backup_location
11+
from keyrings.cryptfile.cryptfile import CryptFileKeyring
1012

1113
from cratedb_toolkit.exception import CroudException
1214
from cratedb_toolkit.meta.release import CrateDBRelease
@@ -432,9 +434,30 @@ def create_import_job(self, resource: InputOutputResource, target: TableAddress)
432434
return wr.invoke()
433435

434436
def get_jwt_token(self) -> t.Dict[str, str]:
437+
"""
438+
Retrieve per-cluster JWT token from keyring, falling back to API.
439+
"""
440+
kr = CryptFileKeyring()
441+
kr.keyring_key = os.getenv("CTK_KEYRING_CRYPTFILE_PASSWORD") or "TruaframEkEk"
442+
key = "ctk-cluster-jwt"
443+
data = None
444+
data_raw = kr.get_password(key, self.cluster_id)
445+
if data_raw is not None:
446+
data = json.loads(data_raw)
447+
now = (dt.datetime.now(dt.timezone.utc) - dt.timedelta(minutes=5)).isoformat()
448+
if now > data["expiry"]:
449+
logger.info("JWT token expired")
450+
data = None
451+
if data is None:
452+
data = self._get_jwt_token()
453+
kr.set_password(key, self.cluster_id, json.dumps(data))
454+
return data
455+
456+
def _get_jwt_token(self) -> t.Dict[str, str]:
435457
"""
436458
Retrieve per-cluster JWT token.
437459
"""
460+
logger.info("Retrieving JWT token from API")
438461
data, errors = self.client.get(self.url.jwt)
439462
if data is None:
440463
if not errors:

cratedb_toolkit/cluster/model.py

+2-5
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,6 @@ class JwtResponse:
2525
refresh: str
2626
token: str
2727

28-
def get_token(self):
29-
# TODO: Persist token across sessions.
30-
# TODO: Refresh automatically when expired.
31-
return self.token
32-
3328

3429
@dataclasses.dataclass
3530
class ClusterInformation:
@@ -144,6 +139,8 @@ def acquire_dynamic_content(self):
144139
cluster = ManagedCluster(cluster_id=self.cloud_id).probe()
145140
if not cluster.address:
146141
raise CroudException(f"Cluster not found: {self.cloud_name}")
142+
if not cluster.info:
143+
raise CroudException(f"Cluster information not available for: {self.cloud_name}")
147144
adapter = DatabaseAdapter(dburi=cluster.address.dburi, jwt=cluster.info.jwt)
148145
self.database = InfoContainer(adapter=adapter, scrub=True).database()
149146

doc/cluster/backlog.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ a patch for some item, it will be very much welcome.
1515
- CLI: Less verbosity by default for `ctk cluster` operations
1616
Possibly display cluster operation and job execution outcomes, and `last_async_operation` details
1717
- CLI: Implement `ctk cluster list`, `ctk cluster delete`
18-
- Managed: Use `keyring` for caching the JWT token, and compensate token expiry
1918

2019
## Iteration +2
2120
- Python API: Make `cluster.query` use the excellent `records` package
@@ -96,3 +95,4 @@ a patch for some item, it will be very much welcome.
9695
- CLI: Shrink address URLs to single parameter `--cluster-url`
9796
- Changelog: Notify about breaking change with input address parameter names
9897
- Docs: Update guidelines about input address parameter preferences
98+
- Managed: Use `keyring` for caching the JWT token, and compensate token expiry

pyproject.toml

+1
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ dependencies = [
102102
"croud==1.13.0",
103103
"importlib-metadata; python_version<'3.8'",
104104
"importlib-resources; python_version<'3.9'",
105+
"keyrings-cryptfile<2",
105106
"orjsonl<2",
106107
"pympler<1.2",
107108
"python-dateutil<3",

0 commit comments

Comments
 (0)