flwr 1.13.1__py3-none-any.whl → 1.15.0__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 (158) hide show
  1. flwr/cli/app.py +5 -0
  2. flwr/cli/auth_plugin/__init__.py +31 -0
  3. flwr/cli/auth_plugin/oidc_cli_plugin.py +150 -0
  4. flwr/cli/build.py +1 -0
  5. flwr/cli/cli_user_auth_interceptor.py +90 -0
  6. flwr/cli/config_utils.py +43 -149
  7. flwr/cli/constant.py +27 -0
  8. flwr/cli/example.py +1 -0
  9. flwr/cli/install.py +2 -1
  10. flwr/cli/log.py +34 -37
  11. flwr/cli/login/__init__.py +22 -0
  12. flwr/cli/login/login.py +116 -0
  13. flwr/cli/ls.py +214 -106
  14. flwr/cli/new/__init__.py +1 -0
  15. flwr/cli/new/new.py +2 -1
  16. flwr/cli/new/templates/app/.gitignore.tpl +3 -0
  17. flwr/cli/new/templates/app/README.md.tpl +3 -2
  18. flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +4 -4
  19. flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +4 -4
  20. flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +4 -4
  21. flwr/cli/new/templates/app/pyproject.jax.toml.tpl +2 -2
  22. flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +3 -4
  23. flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +2 -2
  24. flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +4 -4
  25. flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +3 -3
  26. flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +2 -2
  27. flwr/cli/run/__init__.py +1 -0
  28. flwr/cli/run/run.py +103 -43
  29. flwr/cli/stop.py +139 -0
  30. flwr/cli/utils.py +186 -8
  31. flwr/client/app.py +49 -50
  32. flwr/client/client.py +1 -32
  33. flwr/client/clientapp/app.py +23 -26
  34. flwr/client/clientapp/utils.py +2 -1
  35. flwr/client/grpc_adapter_client/connection.py +1 -1
  36. flwr/client/grpc_client/connection.py +2 -13
  37. flwr/client/grpc_rere_client/client_interceptor.py +19 -119
  38. flwr/client/grpc_rere_client/connection.py +59 -43
  39. flwr/client/grpc_rere_client/grpc_adapter.py +12 -12
  40. flwr/client/message_handler/message_handler.py +1 -2
  41. flwr/client/message_handler/task_handler.py +0 -17
  42. flwr/client/mod/comms_mods.py +1 -0
  43. flwr/client/mod/localdp_mod.py +1 -1
  44. flwr/client/nodestate/__init__.py +1 -0
  45. flwr/client/nodestate/nodestate.py +1 -0
  46. flwr/client/nodestate/nodestate_factory.py +1 -0
  47. flwr/client/numpy_client.py +0 -44
  48. flwr/client/rest_client/connection.py +37 -29
  49. flwr/client/supernode/app.py +20 -74
  50. flwr/common/address.py +1 -0
  51. flwr/common/args.py +26 -47
  52. flwr/common/auth_plugin/__init__.py +24 -0
  53. flwr/common/auth_plugin/auth_plugin.py +122 -0
  54. flwr/common/config.py +169 -17
  55. flwr/common/constant.py +38 -9
  56. flwr/common/differential_privacy.py +2 -1
  57. flwr/common/exit/__init__.py +24 -0
  58. flwr/common/exit/exit.py +99 -0
  59. flwr/common/exit/exit_code.py +93 -0
  60. flwr/common/exit_handlers.py +24 -10
  61. flwr/common/grpc.py +167 -4
  62. flwr/common/logger.py +66 -7
  63. flwr/common/message.py +1 -0
  64. flwr/common/object_ref.py +57 -54
  65. flwr/common/pyproject.py +1 -0
  66. flwr/common/record/__init__.py +1 -0
  67. flwr/common/record/parametersrecord.py +1 -0
  68. flwr/common/record/recordset.py +1 -1
  69. flwr/common/retry_invoker.py +77 -0
  70. flwr/common/secure_aggregation/crypto/symmetric_encryption.py +45 -0
  71. flwr/common/secure_aggregation/secaggplus_utils.py +2 -2
  72. flwr/common/serde.py +6 -4
  73. flwr/common/telemetry.py +15 -4
  74. flwr/common/typing.py +32 -0
  75. flwr/common/version.py +1 -0
  76. flwr/proto/clientappio_pb2.py +1 -1
  77. flwr/proto/error_pb2.py +1 -1
  78. flwr/proto/exec_pb2.py +27 -15
  79. flwr/proto/exec_pb2.pyi +80 -2
  80. flwr/proto/exec_pb2_grpc.py +102 -0
  81. flwr/proto/exec_pb2_grpc.pyi +39 -0
  82. flwr/proto/fab_pb2.py +5 -5
  83. flwr/proto/fab_pb2.pyi +4 -1
  84. flwr/proto/fleet_pb2.py +31 -31
  85. flwr/proto/fleet_pb2.pyi +23 -23
  86. flwr/proto/fleet_pb2_grpc.py +30 -30
  87. flwr/proto/fleet_pb2_grpc.pyi +20 -20
  88. flwr/proto/grpcadapter_pb2.py +1 -1
  89. flwr/proto/log_pb2.py +1 -1
  90. flwr/proto/message_pb2.py +1 -1
  91. flwr/proto/node_pb2.py +3 -3
  92. flwr/proto/node_pb2.pyi +1 -4
  93. flwr/proto/recordset_pb2.py +1 -1
  94. flwr/proto/run_pb2.py +1 -1
  95. flwr/proto/serverappio_pb2.py +24 -25
  96. flwr/proto/serverappio_pb2.pyi +32 -32
  97. flwr/proto/serverappio_pb2_grpc.py +62 -28
  98. flwr/proto/serverappio_pb2_grpc.pyi +29 -16
  99. flwr/proto/simulationio_pb2.py +3 -3
  100. flwr/proto/simulationio_pb2_grpc.py +34 -0
  101. flwr/proto/simulationio_pb2_grpc.pyi +13 -0
  102. flwr/proto/task_pb2.py +1 -1
  103. flwr/proto/transport_pb2.py +1 -1
  104. flwr/server/app.py +152 -112
  105. flwr/server/compat/app_utils.py +7 -2
  106. flwr/server/compat/driver_client_proxy.py +1 -2
  107. flwr/server/driver/grpc_driver.py +38 -85
  108. flwr/server/driver/inmemory_driver.py +7 -2
  109. flwr/server/run_serverapp.py +8 -9
  110. flwr/server/serverapp/app.py +37 -13
  111. flwr/server/strategy/dpfedavg_fixed.py +1 -0
  112. flwr/server/superlink/driver/serverappio_grpc.py +2 -1
  113. flwr/server/superlink/driver/serverappio_servicer.py +148 -63
  114. flwr/server/superlink/ffs/disk_ffs.py +1 -0
  115. flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +20 -87
  116. flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +1 -0
  117. flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +2 -165
  118. flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +56 -35
  119. flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +99 -169
  120. flwr/server/superlink/fleet/message_handler/message_handler.py +69 -29
  121. flwr/server/superlink/fleet/rest_rere/rest_api.py +20 -19
  122. flwr/server/superlink/fleet/vce/__init__.py +1 -0
  123. flwr/server/superlink/fleet/vce/backend/__init__.py +1 -0
  124. flwr/server/superlink/fleet/vce/backend/raybackend.py +1 -0
  125. flwr/server/superlink/fleet/vce/vce_api.py +2 -2
  126. flwr/server/superlink/linkstate/in_memory_linkstate.py +60 -99
  127. flwr/server/superlink/linkstate/linkstate.py +30 -36
  128. flwr/server/superlink/linkstate/sqlite_linkstate.py +105 -188
  129. flwr/server/superlink/linkstate/utils.py +18 -8
  130. flwr/server/superlink/simulation/simulationio_grpc.py +1 -1
  131. flwr/server/superlink/simulation/simulationio_servicer.py +33 -0
  132. flwr/server/superlink/utils.py +65 -0
  133. flwr/server/utils/validator.py +9 -34
  134. flwr/simulation/app.py +20 -10
  135. flwr/simulation/legacy_app.py +4 -2
  136. flwr/simulation/ray_transport/ray_actor.py +1 -0
  137. flwr/simulation/ray_transport/utils.py +1 -0
  138. flwr/simulation/run_simulation.py +36 -22
  139. flwr/simulation/simulationio_connection.py +5 -1
  140. flwr/superexec/app.py +1 -0
  141. flwr/superexec/deployment.py +1 -0
  142. flwr/superexec/exec_grpc.py +20 -2
  143. flwr/superexec/exec_servicer.py +97 -2
  144. flwr/superexec/exec_user_auth_interceptor.py +101 -0
  145. flwr/superexec/executor.py +1 -0
  146. {flwr-1.13.1.dist-info → flwr-1.15.0.dist-info}/METADATA +14 -13
  147. {flwr-1.13.1.dist-info → flwr-1.15.0.dist-info}/RECORD +150 -144
  148. flwr/proto/common_pb2.py +0 -36
  149. flwr/proto/common_pb2.pyi +0 -121
  150. flwr/proto/common_pb2_grpc.py +0 -4
  151. flwr/proto/common_pb2_grpc.pyi +0 -4
  152. flwr/proto/control_pb2.py +0 -27
  153. flwr/proto/control_pb2.pyi +0 -7
  154. flwr/proto/control_pb2_grpc.py +0 -135
  155. flwr/proto/control_pb2_grpc.pyi +0 -53
  156. {flwr-1.13.1.dist-info → flwr-1.15.0.dist-info}/LICENSE +0 -0
  157. {flwr-1.13.1.dist-info → flwr-1.15.0.dist-info}/WHEEL +0 -0
  158. {flwr-1.13.1.dist-info → flwr-1.15.0.dist-info}/entry_points.txt +0 -0
@@ -14,8 +14,8 @@
14
14
  # ==============================================================================
15
15
  """Flower SuperNode."""
16
16
 
17
+
17
18
  import argparse
18
- import sys
19
19
  from logging import DEBUG, ERROR, INFO, WARN
20
20
  from pathlib import Path
21
21
  from typing import Optional
@@ -39,8 +39,9 @@ from flwr.common.constant import (
39
39
  TRANSPORT_TYPE_GRPC_RERE,
40
40
  TRANSPORT_TYPE_REST,
41
41
  )
42
+ from flwr.common.exit import ExitCode, flwr_exit
42
43
  from flwr.common.exit_handlers import register_exit_handlers
43
- from flwr.common.logger import log, warn_deprecated_feature
44
+ from flwr.common.logger import log
44
45
 
45
46
  from ..app import start_client_internal
46
47
  from ..clientapp.utils import get_load_client_app_fn
@@ -49,7 +50,6 @@ from ..clientapp.utils import get_load_client_app_fn
49
50
  def run_supernode() -> None:
50
51
  """Run Flower SuperNode."""
51
52
  args = _parse_args_run_supernode().parse_args()
52
- _warn_deprecated_server_arg(args)
53
53
 
54
54
  log(INFO, "Starting Flower SuperNode")
55
55
 
@@ -63,17 +63,6 @@ def run_supernode() -> None:
63
63
  "Ignoring `--flwr-dir`.",
64
64
  )
65
65
 
66
- # Exit if unsupported argument is passed by the user
67
- if args.app is not None:
68
- log(
69
- ERROR,
70
- "The `app` argument is deprecated. The SuperNode now automatically "
71
- "uses the ClientApp delivered from the SuperLink. Providing the app "
72
- "directory manually is no longer supported. Please remove the `app` "
73
- "argument from your command.",
74
- )
75
- sys.exit(1)
76
-
77
66
  root_certificates = try_obtain_root_certificates(args, args.superlink)
78
67
  load_fn = get_load_client_app_fn(
79
68
  default_app_ref="",
@@ -85,6 +74,12 @@ def run_supernode() -> None:
85
74
 
86
75
  log(DEBUG, "Isolation mode: %s", args.isolation)
87
76
 
77
+ # Register handlers for graceful shutdown
78
+ register_exit_handlers(
79
+ event_type=EventType.RUN_SUPERNODE_LEAVE,
80
+ exit_message="SuperNode terminated gracefully.",
81
+ )
82
+
88
83
  start_client_internal(
89
84
  server_address=args.superlink,
90
85
  load_client_app_fn=load_fn,
@@ -102,60 +97,22 @@ def run_supernode() -> None:
102
97
  clientappio_api_address=args.clientappio_api_address,
103
98
  )
104
99
 
105
- # Graceful shutdown
106
- register_exit_handlers(
107
- event_type=EventType.RUN_SUPERNODE_LEAVE,
108
- )
109
-
110
100
 
111
101
  def run_client_app() -> None:
112
102
  """Run Flower client app."""
113
103
  event(EventType.RUN_CLIENT_APP_ENTER)
114
104
  log(
115
105
  ERROR,
116
- "The command `flower-client-app` has been replaced by `flower-supernode`.",
106
+ "The command `flower-client-app` has been replaced by `flwr run`.",
117
107
  )
118
- log(INFO, "Execute `flower-supernode --help` to learn how to use it.")
119
108
  register_exit_handlers(event_type=EventType.RUN_CLIENT_APP_LEAVE)
120
109
 
121
110
 
122
- def _warn_deprecated_server_arg(args: argparse.Namespace) -> None:
123
- """Warn about the deprecated argument `--server`."""
124
- if args.server != FLEET_API_GRPC_RERE_DEFAULT_ADDRESS:
125
- warn = "Passing flag --server is deprecated. Use --superlink instead."
126
- warn_deprecated_feature(warn)
127
-
128
- if args.superlink != FLEET_API_GRPC_RERE_DEFAULT_ADDRESS:
129
- # if `--superlink` also passed, then
130
- # warn user that this argument overrides what was passed with `--server`
131
- log(
132
- WARN,
133
- "Both `--server` and `--superlink` were passed. "
134
- "`--server` will be ignored. Connecting to the Superlink Fleet API "
135
- "at %s.",
136
- args.superlink,
137
- )
138
- else:
139
- args.superlink = args.server
140
-
141
-
142
111
  def _parse_args_run_supernode() -> argparse.ArgumentParser:
143
112
  """Parse flower-supernode command line arguments."""
144
113
  parser = argparse.ArgumentParser(
145
114
  description="Start a Flower SuperNode",
146
115
  )
147
-
148
- parser.add_argument(
149
- "app",
150
- nargs="?",
151
- default=None,
152
- help=(
153
- "(REMOVED) This argument is removed. The SuperNode now automatically "
154
- "uses the ClientApp delivered from the SuperLink, so there is no need to "
155
- "provide the app directory manually. This argument will be removed in a "
156
- "future version."
157
- ),
158
- )
159
116
  _parse_args_common(parser)
160
117
  parser.add_argument(
161
118
  "--flwr-dir",
@@ -228,15 +185,12 @@ def _parse_args_common(parser: argparse.ArgumentParser) -> None:
228
185
  help="Specifies the path to the PEM-encoded root certificate file for "
229
186
  "establishing secure HTTPS connections.",
230
187
  )
231
- parser.add_argument(
232
- "--server",
233
- default=FLEET_API_GRPC_RERE_DEFAULT_ADDRESS,
234
- help="Server address",
235
- )
236
188
  parser.add_argument(
237
189
  "--superlink",
238
190
  default=FLEET_API_GRPC_RERE_DEFAULT_ADDRESS,
239
- help="SuperLink Fleet API (gRPC-rere) address (IPv4, IPv6, or a domain name)",
191
+ help="SuperLink Fleet API address (IPv4, IPv6, or a domain name). If using the "
192
+ "REST (experimental) transport, ensure your address is in the form "
193
+ "`http://...` or `https://...` when TLS is enabled.",
240
194
  )
241
195
  parser.add_argument(
242
196
  "--max-retries",
@@ -280,11 +234,7 @@ def _try_setup_client_authentication(
280
234
  return None
281
235
 
282
236
  if not args.auth_supernode_private_key or not args.auth_supernode_public_key:
283
- sys.exit(
284
- "Authentication requires file paths to both "
285
- "'--auth-supernode-private-key' and '--auth-supernode-public-key'"
286
- "to be provided (providing only one of them is not sufficient)."
287
- )
237
+ flwr_exit(ExitCode.SUPERNODE_NODE_AUTH_KEYS_REQUIRED)
288
238
 
289
239
  try:
290
240
  ssh_private_key = load_ssh_private_key(
@@ -294,11 +244,9 @@ def _try_setup_client_authentication(
294
244
  if not isinstance(ssh_private_key, ec.EllipticCurvePrivateKey):
295
245
  raise ValueError()
296
246
  except (ValueError, UnsupportedAlgorithm):
297
- sys.exit(
298
- "Error: Unable to parse the private key file in "
299
- "'--auth-supernode-private-key'. Authentication requires elliptic "
300
- "curve private and public key pair. Please ensure that the file "
301
- "path points to a valid private key file and try again."
247
+ flwr_exit(
248
+ ExitCode.SUPERNODE_NODE_AUTH_KEYS_INVALID,
249
+ "Unable to parse the private key file.",
302
250
  )
303
251
 
304
252
  try:
@@ -308,11 +256,9 @@ def _try_setup_client_authentication(
308
256
  if not isinstance(ssh_public_key, ec.EllipticCurvePublicKey):
309
257
  raise ValueError()
310
258
  except (ValueError, UnsupportedAlgorithm):
311
- sys.exit(
312
- "Error: Unable to parse the public key file in "
313
- "'--auth-supernode-public-key'. Authentication requires elliptic "
314
- "curve private and public key pair. Please ensure that the file "
315
- "path points to a valid public key file and try again."
259
+ flwr_exit(
260
+ ExitCode.SUPERNODE_NODE_AUTH_KEYS_INVALID,
261
+ "Unable to parse the public key file.",
316
262
  )
317
263
 
318
264
  return (
flwr/common/address.py CHANGED
@@ -14,6 +14,7 @@
14
14
  # ==============================================================================
15
15
  """Flower IP address utils."""
16
16
 
17
+
17
18
  import socket
18
19
  from ipaddress import ip_address
19
20
  from typing import Optional
flwr/common/args.py CHANGED
@@ -14,18 +14,15 @@
14
14
  # ==============================================================================
15
15
  """Common Flower arguments."""
16
16
 
17
+
17
18
  import argparse
18
19
  import sys
19
20
  from logging import DEBUG, ERROR, WARN
20
21
  from os.path import isfile
21
22
  from pathlib import Path
22
- from typing import Optional
23
+ from typing import Optional, Union
23
24
 
24
- from flwr.common.constant import (
25
- TRANSPORT_TYPE_GRPC_ADAPTER,
26
- TRANSPORT_TYPE_GRPC_RERE,
27
- TRANSPORT_TYPE_REST,
28
- )
25
+ from flwr.common.constant import TRANSPORT_TYPE_REST
29
26
  from flwr.common.logger import log
30
27
 
31
28
 
@@ -54,9 +51,9 @@ def add_args_flwr_app_common(parser: argparse.ArgumentParser) -> None:
54
51
  def try_obtain_root_certificates(
55
52
  args: argparse.Namespace,
56
53
  grpc_server_address: str,
57
- ) -> Optional[bytes]:
54
+ ) -> Optional[Union[bytes, str]]:
58
55
  """Validate and return the root certificates."""
59
- root_cert_path = args.root_certificates
56
+ root_cert_path: Optional[str] = args.root_certificates
60
57
  if args.insecure:
61
58
  if root_cert_path is not None:
62
59
  sys.exit(
@@ -92,56 +89,38 @@ def try_obtain_root_certificates(
92
89
  grpc_server_address,
93
90
  root_cert_path,
94
91
  )
92
+ if args.transport == TRANSPORT_TYPE_REST:
93
+ return root_cert_path
95
94
  return root_certificates
96
95
 
97
96
 
98
97
  def try_obtain_server_certificates(
99
98
  args: argparse.Namespace,
100
- transport_type: str,
101
99
  ) -> Optional[tuple[bytes, bytes, bytes]]:
102
100
  """Validate and return the CA cert, server cert, and server private key."""
103
101
  if args.insecure:
104
102
  log(WARN, "Option `--insecure` was set. Starting insecure HTTP server.")
105
103
  return None
106
104
  # Check if certificates are provided
107
- if transport_type in [TRANSPORT_TYPE_GRPC_RERE, TRANSPORT_TYPE_GRPC_ADAPTER]:
108
- if args.ssl_certfile and args.ssl_keyfile and args.ssl_ca_certfile:
109
- if not isfile(args.ssl_ca_certfile):
110
- sys.exit("Path argument `--ssl-ca-certfile` does not point to a file.")
111
- if not isfile(args.ssl_certfile):
112
- sys.exit("Path argument `--ssl-certfile` does not point to a file.")
113
- if not isfile(args.ssl_keyfile):
114
- sys.exit("Path argument `--ssl-keyfile` does not point to a file.")
115
- certificates = (
116
- Path(args.ssl_ca_certfile).read_bytes(), # CA certificate
117
- Path(args.ssl_certfile).read_bytes(), # server certificate
118
- Path(args.ssl_keyfile).read_bytes(), # server private key
119
- )
120
- return certificates
121
- if args.ssl_certfile or args.ssl_keyfile or args.ssl_ca_certfile:
122
- sys.exit(
123
- "You need to provide valid file paths to `--ssl-certfile`, "
124
- "`--ssl-keyfile`, and `—-ssl-ca-certfile` to create a secure "
125
- "connection in Fleet API server (gRPC-rere)."
126
- )
127
- if transport_type == TRANSPORT_TYPE_REST:
128
- if args.ssl_certfile and args.ssl_keyfile:
129
- if not isfile(args.ssl_certfile):
130
- sys.exit("Path argument `--ssl-certfile` does not point to a file.")
131
- if not isfile(args.ssl_keyfile):
132
- sys.exit("Path argument `--ssl-keyfile` does not point to a file.")
133
- certificates = (
134
- b"",
135
- Path(args.ssl_certfile).read_bytes(), # server certificate
136
- Path(args.ssl_keyfile).read_bytes(), # server private key
137
- )
138
- return certificates
139
- if args.ssl_certfile or args.ssl_keyfile:
140
- sys.exit(
141
- "You need to provide valid file paths to `--ssl-certfile` "
142
- "and `--ssl-keyfile` to create a secure connection "
143
- "in Fleet API server (REST, experimental)."
144
- )
105
+ if args.ssl_certfile and args.ssl_keyfile and args.ssl_ca_certfile:
106
+ if not isfile(args.ssl_ca_certfile):
107
+ sys.exit("Path argument `--ssl-ca-certfile` does not point to a file.")
108
+ if not isfile(args.ssl_certfile):
109
+ sys.exit("Path argument `--ssl-certfile` does not point to a file.")
110
+ if not isfile(args.ssl_keyfile):
111
+ sys.exit("Path argument `--ssl-keyfile` does not point to a file.")
112
+ certificates = (
113
+ Path(args.ssl_ca_certfile).read_bytes(), # CA certificate
114
+ Path(args.ssl_certfile).read_bytes(), # server certificate
115
+ Path(args.ssl_keyfile).read_bytes(), # server private key
116
+ )
117
+ return certificates
118
+ if args.ssl_certfile or args.ssl_keyfile or args.ssl_ca_certfile:
119
+ sys.exit(
120
+ "You need to provide valid file paths to `--ssl-certfile`, "
121
+ "`--ssl-keyfile`, and `—-ssl-ca-certfile` to create a secure "
122
+ "connection in Fleet API server (gRPC-rere)."
123
+ )
145
124
  log(
146
125
  ERROR,
147
126
  "Certificates are required unless running in insecure mode. "
@@ -0,0 +1,24 @@
1
+ # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+ """Auth plugin components."""
16
+
17
+
18
+ from .auth_plugin import CliAuthPlugin as CliAuthPlugin
19
+ from .auth_plugin import ExecAuthPlugin as ExecAuthPlugin
20
+
21
+ __all__ = [
22
+ "CliAuthPlugin",
23
+ "ExecAuthPlugin",
24
+ ]
@@ -0,0 +1,122 @@
1
+ # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+ """Abstract classes for Flower User Auth Plugin."""
16
+
17
+
18
+ from abc import ABC, abstractmethod
19
+ from collections.abc import Sequence
20
+ from pathlib import Path
21
+ from typing import Optional, Union
22
+
23
+ from flwr.proto.exec_pb2_grpc import ExecStub
24
+
25
+ from ..typing import UserAuthCredentials, UserAuthLoginDetails
26
+
27
+
28
+ class ExecAuthPlugin(ABC):
29
+ """Abstract Flower Auth Plugin class for ExecServicer.
30
+
31
+ Parameters
32
+ ----------
33
+ user_auth_config_path : Path
34
+ Path to the YAML file containing the authentication configuration.
35
+ """
36
+
37
+ @abstractmethod
38
+ def __init__(
39
+ self,
40
+ user_auth_config_path: Path,
41
+ verify_tls_cert: bool,
42
+ ):
43
+ """Abstract constructor."""
44
+
45
+ @abstractmethod
46
+ def get_login_details(self) -> Optional[UserAuthLoginDetails]:
47
+ """Get the login details."""
48
+
49
+ @abstractmethod
50
+ def validate_tokens_in_metadata(
51
+ self, metadata: Sequence[tuple[str, Union[str, bytes]]]
52
+ ) -> bool:
53
+ """Validate authentication tokens in the provided metadata."""
54
+
55
+ @abstractmethod
56
+ def get_auth_tokens(self, device_code: str) -> Optional[UserAuthCredentials]:
57
+ """Get authentication tokens."""
58
+
59
+ @abstractmethod
60
+ def refresh_tokens(
61
+ self, metadata: Sequence[tuple[str, Union[str, bytes]]]
62
+ ) -> Optional[Sequence[tuple[str, Union[str, bytes]]]]:
63
+ """Refresh authentication tokens in the provided metadata."""
64
+
65
+
66
+ class CliAuthPlugin(ABC):
67
+ """Abstract Flower Auth Plugin class for CLI.
68
+
69
+ Parameters
70
+ ----------
71
+ credentials_path : Path
72
+ Path to the user's authentication credentials file.
73
+ """
74
+
75
+ @staticmethod
76
+ @abstractmethod
77
+ def login(
78
+ login_details: UserAuthLoginDetails,
79
+ exec_stub: ExecStub,
80
+ ) -> UserAuthCredentials:
81
+ """Authenticate the user and retrieve authentication credentials.
82
+
83
+ Parameters
84
+ ----------
85
+ login_details : UserAuthLoginDetails
86
+ An object containing the user's login details.
87
+ exec_stub : ExecStub
88
+ A stub for executing RPC calls to the server.
89
+
90
+ Returns
91
+ -------
92
+ UserAuthCredentials
93
+ The authentication credentials obtained after login.
94
+ """
95
+
96
+ @abstractmethod
97
+ def __init__(self, credentials_path: Path):
98
+ """Abstract constructor."""
99
+
100
+ @abstractmethod
101
+ def store_tokens(self, credentials: UserAuthCredentials) -> None:
102
+ """Store authentication tokens to the `credentials_path`.
103
+
104
+ The credentials, including tokens, will be saved as a JSON file
105
+ at `credentials_path`.
106
+ """
107
+
108
+ @abstractmethod
109
+ def load_tokens(self) -> None:
110
+ """Load authentication tokens from the `credentials_path`."""
111
+
112
+ @abstractmethod
113
+ def write_tokens_to_metadata(
114
+ self, metadata: Sequence[tuple[str, Union[str, bytes]]]
115
+ ) -> Sequence[tuple[str, Union[str, bytes]]]:
116
+ """Write authentication tokens to the provided metadata."""
117
+
118
+ @abstractmethod
119
+ def read_tokens_from_metadata(
120
+ self, metadata: Sequence[tuple[str, Union[str, bytes]]]
121
+ ) -> Optional[UserAuthCredentials]:
122
+ """Read authentication tokens from the provided metadata."""