Skip to content

Commit 8e794f9

Browse files
committed
Support secrets & signing
Signed-off-by: Mathias L. Baumann <[email protected]>
1 parent 0e416ea commit 8e794f9

File tree

6 files changed

+41
-23
lines changed

6 files changed

+41
-23
lines changed

RELEASE_NOTES.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010

1111
## New Features
1212

13-
<!-- Here goes the main new features and examples or instructions on how to use them -->
13+
* Support secrets for signing and veryfying messages.
14+
* Use the new env variable `DISPATCH_API_SECRET` to set the secret key.
15+
* Use the new `sign_secret` parameter in the `DispatchClient` constructor to set the secret key.
1416

1517
## Bug Fixes
1618

mkdocs.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,14 +103,15 @@ plugins:
103103
paths: ["src"]
104104
docstring_section_style: spacy
105105
inherited_members: true
106-
merge_init_into_class: false
106+
merge_init_into_class: true
107107
separate_signature: true
108108
show_category_heading: true
109109
show_root_heading: true
110110
show_root_members_full_path: true
111111
show_signature_annotations: true
112112
show_source: true
113113
signature_crossrefs: true
114+
members_order: source
114115
import:
115116
- https://docs.python.org/3/objects.inv
116117
- https://typing-extensions.readthedocs.io/en/stable/objects.inv

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ requires-python = ">= 3.11, < 4"
3838
dependencies = [
3939
"typing-extensions >= 4.13.0, < 5",
4040
"frequenz-api-dispatch == 1.0.0-rc2",
41-
"frequenz-client-base >= 0.8.0, < 0.12.0",
41+
"frequenz-client-base >= 0.11.0, < 0.12.0",
4242
"frequenz-client-common >= 0.3.2, < 0.4.0",
4343
"frequenz-core >= 1.0.2, < 2.0.0",
4444
"grpcio >= 1.70.0, < 2",

src/frequenz/client/dispatch/__main__.py

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,14 @@ def format_line(key: str, value: str, color: str = "cyan") -> str:
177177
show_envvar=True,
178178
required=True,
179179
)
180+
@click.option(
181+
"--secret",
182+
help="API signing secret for authentication",
183+
envvar="DISPATCH_API_SECRET",
184+
show_envvar=True,
185+
required=False,
186+
default=None,
187+
)
180188
@click.option(
181189
"--raw",
182190
is_flag=True,
@@ -185,29 +193,36 @@ def format_line(key: str, value: str, color: str = "cyan") -> str:
185193
default=False,
186194
)
187195
@click.pass_context
188-
async def cli(ctx: click.Context, url: str, key: str, raw: bool) -> None:
196+
async def cli(
197+
ctx: click.Context, url: str, key: str, secret: str | None, raw: bool
198+
) -> None:
189199
"""Dispatch Service CLI."""
190200
if ctx.obj is None:
191201
ctx.obj = {}
192202

193203
click.echo(f"Using API URL: {url}", err=True)
204+
click.echo(f"Using API Key: {key}", err=True)
205+
if secret:
206+
click.echo(f"Using API Secret: {secret}", err=True)
194207

195208
ctx.obj["client"] = DispatchApiClient(
196209
server_url=url,
197-
key=key,
210+
auth_key=key,
211+
sign_secret=secret,
198212
connect=True,
199213
)
200214

201215
ctx.obj["params"] = {
202216
"url": url,
203217
"key": key,
218+
"secret": secret,
204219
}
205220

206221
ctx.obj["raw"] = raw
207222

208223
# Check if a subcommand was given
209224
if ctx.invoked_subcommand is None:
210-
await interactive_mode(url, key)
225+
await interactive_mode(url, key, secret)
211226

212227

213228
@cli.command("list")
@@ -533,8 +548,9 @@ async def repl(
533548
obj: dict[str, Any],
534549
) -> None:
535550
"""Start an interactive interface."""
536-
click.echo(f"Parameters: {obj}")
537-
await interactive_mode(obj["params"]["url"], obj["params"]["key"])
551+
await interactive_mode(
552+
obj["params"]["url"], obj["params"]["key"], obj["params"]["secret"]
553+
)
538554

539555

540556
@cli.command()
@@ -574,7 +590,7 @@ async def delete(
574590
raise click.ClickException("Some deletions failed.")
575591

576592

577-
async def interactive_mode(url: str, key: str) -> None:
593+
async def interactive_mode(url: str, key: str, secret: str | None) -> None:
578594
"""Interactive mode for the CLI."""
579595
hist_file = os.path.expanduser("~/.dispatch_cli_history.txt")
580596
session: PromptSession[str] = PromptSession(history=FileHistory(filename=hist_file))
@@ -619,6 +635,8 @@ async def display_help() -> None:
619635
url,
620636
"--key",
621637
key,
638+
"--secret",
639+
secret if secret else "",
622640
] + click.parser.split_arg_string(user_input)
623641

624642
try:

src/frequenz/client/dispatch/_client.py

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ def __init__(
6060
self,
6161
*,
6262
server_url: str,
63-
key: str,
63+
auth_key: str,
64+
sign_secret: str | None = None,
6465
connect: bool = True,
6566
call_timeout: timedelta = timedelta(seconds=60),
6667
stream_timeout: timedelta = timedelta(minutes=5),
@@ -69,7 +70,8 @@ def __init__(
6970
7071
Args:
7172
server_url: The URL of the server to connect to.
72-
key: API key to use for authentication.
73+
auth_key: API key to use for authentication.
74+
sign_secret: Optional secret for signing requests.
7375
connect: Whether to connect to the service immediately.
7476
call_timeout: Timeout for gRPC calls, default is 60 seconds.
7577
stream_timeout: Timeout for gRPC streams, default is 5 minutes.
@@ -82,8 +84,9 @@ def __init__(
8284
port=DEFAULT_DISPATCH_PORT,
8385
ssl=SslOptions(enabled=True),
8486
),
87+
auth_key=auth_key,
88+
sign_secret=sign_secret,
8589
)
86-
self._metadata = (("key", key),)
8790
self._streams: dict[
8891
MicrogridId,
8992
GrpcStreamBroadcaster[StreamMicrogridDispatchesResponse, DispatchEvent],
@@ -199,7 +202,7 @@ def to_interval(
199202
response = await cast(
200203
Awaitable[ListMicrogridDispatchesResponse],
201204
self.stub.ListMicrogridDispatches(
202-
request, metadata=self._metadata, timeout=self._call_timeout_seconds
205+
request, timeout=self._call_timeout_seconds
203206
),
204207
)
205208

@@ -256,7 +259,6 @@ def _get_stream(
256259
AsyncIterator[StreamMicrogridDispatchesResponse],
257260
self.stub.StreamMicrogridDispatches(
258261
request,
259-
metadata=self._metadata,
260262
timeout=self._stream_timeout_seconds,
261263
),
262264
),
@@ -327,7 +329,6 @@ async def create( # pylint: disable=too-many-positional-arguments
327329
Awaitable[CreateMicrogridDispatchResponse],
328330
self.stub.CreateMicrogridDispatch(
329331
request.to_protobuf(),
330-
metadata=self._metadata,
331332
timeout=self._call_timeout_seconds,
332333
),
333334
)
@@ -419,9 +420,7 @@ async def update(
419420

420421
response = await cast(
421422
Awaitable[UpdateMicrogridDispatchResponse],
422-
self.stub.UpdateMicrogridDispatch(
423-
msg, metadata=self._metadata, timeout=self._call_timeout_seconds
424-
),
423+
self.stub.UpdateMicrogridDispatch(msg, timeout=self._call_timeout_seconds),
425424
)
426425

427426
return Dispatch.from_protobuf(response.dispatch)
@@ -443,9 +442,7 @@ async def get(
443442
)
444443
response = await cast(
445444
Awaitable[GetMicrogridDispatchResponse],
446-
self.stub.GetMicrogridDispatch(
447-
request, metadata=self._metadata, timeout=self._call_timeout_seconds
448-
),
445+
self.stub.GetMicrogridDispatch(request, timeout=self._call_timeout_seconds),
449446
)
450447
return Dispatch.from_protobuf(response.dispatch)
451448

@@ -464,6 +461,6 @@ async def delete(
464461
await cast(
465462
Awaitable[None],
466463
self.stub.DeleteMicrogridDispatch(
467-
request, metadata=self._metadata, timeout=self._call_timeout_seconds
464+
request, timeout=self._call_timeout_seconds
468465
),
469466
)

src/frequenz/client/dispatch/test/client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def __init__(
2424
self,
2525
) -> None:
2626
"""Initialize the mock client."""
27-
super().__init__(server_url="mock", key=ALL_KEY, connect=False)
27+
super().__init__(server_url="mock", auth_key=ALL_KEY, connect=False)
2828
self._stuba: FakeService = FakeService()
2929

3030
@property

0 commit comments

Comments
 (0)