flwr 1.22.0__py3-none-any.whl → 1.24.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.
- flwr/__init__.py +16 -5
- flwr/app/error.py +2 -2
- flwr/app/exception.py +3 -3
- flwr/cli/app.py +34 -1
- flwr/cli/app_cmd/__init__.py +23 -0
- flwr/cli/app_cmd/publish.py +285 -0
- flwr/cli/app_cmd/review.py +252 -0
- flwr/cli/auth_plugin/__init__.py +15 -6
- flwr/cli/auth_plugin/auth_plugin.py +94 -0
- flwr/cli/auth_plugin/noop_auth_plugin.py +101 -0
- flwr/cli/auth_plugin/oidc_cli_plugin.py +46 -32
- flwr/cli/build.py +166 -53
- flwr/cli/{cli_user_auth_interceptor.py → cli_account_auth_interceptor.py} +29 -11
- flwr/cli/config_utils.py +101 -13
- flwr/cli/federation/__init__.py +24 -0
- flwr/cli/federation/ls.py +140 -0
- flwr/cli/federation/show.py +317 -0
- flwr/cli/install.py +91 -13
- flwr/cli/log.py +54 -11
- flwr/cli/login/login.py +41 -27
- flwr/cli/ls.py +177 -133
- flwr/cli/new/new.py +175 -40
- flwr/cli/new/templates/app/code/task.pytorch.py.tpl +1 -0
- flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.jax.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +3 -3
- flwr/cli/new/templates/app/pyproject.pytorch_legacy_api.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +2 -2
- flwr/cli/new/templates/app/pyproject.xgboost.toml.tpl +1 -1
- flwr/cli/pull.py +12 -7
- flwr/cli/run/run.py +82 -31
- flwr/cli/run_utils.py +130 -0
- flwr/cli/stop.py +27 -9
- flwr/cli/supernode/__init__.py +25 -0
- flwr/cli/supernode/ls.py +268 -0
- flwr/cli/supernode/register.py +190 -0
- flwr/cli/supernode/unregister.py +140 -0
- flwr/cli/utils.py +464 -81
- flwr/client/__init__.py +2 -1
- flwr/client/dpfedavg_numpy_client.py +4 -1
- flwr/client/grpc_adapter_client/connection.py +12 -15
- flwr/client/grpc_rere_client/connection.py +68 -41
- flwr/client/grpc_rere_client/grpc_adapter.py +34 -14
- flwr/client/grpc_rere_client/{client_interceptor.py → node_auth_client_interceptor.py} +5 -7
- flwr/client/message_handler/message_handler.py +2 -2
- flwr/client/mod/secure_aggregation/secaggplus_mod.py +10 -8
- flwr/client/numpy_client.py +1 -1
- flwr/client/rest_client/connection.py +94 -51
- flwr/client/run_info_store.py +4 -5
- flwr/client/typing.py +1 -1
- flwr/clientapp/__init__.py +1 -2
- flwr/{client → clientapp}/client_app.py +9 -10
- flwr/clientapp/mod/centraldp_mods.py +16 -17
- flwr/clientapp/mod/localdp_mod.py +8 -9
- flwr/clientapp/typing.py +1 -1
- flwr/{client/clientapp → clientapp}/utils.py +4 -4
- flwr/common/address.py +1 -2
- flwr/common/args.py +3 -4
- flwr/common/config.py +13 -16
- flwr/common/constant.py +56 -13
- flwr/common/differential_privacy.py +3 -4
- flwr/common/event_log_plugin/event_log_plugin.py +3 -4
- flwr/common/exit/exit.py +15 -2
- flwr/common/exit/exit_code.py +39 -10
- flwr/common/exit/exit_handler.py +6 -2
- flwr/common/exit/signal_handler.py +5 -5
- flwr/common/grpc.py +6 -6
- flwr/common/inflatable_protobuf_utils.py +1 -1
- flwr/common/inflatable_utils.py +48 -31
- flwr/common/logger.py +19 -19
- flwr/common/message.py +4 -4
- flwr/common/object_ref.py +7 -7
- flwr/common/record/array.py +6 -6
- flwr/common/record/arrayrecord.py +18 -21
- flwr/common/record/configrecord.py +3 -3
- flwr/common/record/recorddict.py +5 -5
- flwr/common/record/typeddict.py +9 -2
- flwr/common/recorddict_compat.py +7 -10
- flwr/common/retry_invoker.py +20 -20
- flwr/common/secure_aggregation/crypto/symmetric_encryption.py +1 -89
- flwr/common/secure_aggregation/ndarrays_arithmetic.py +3 -3
- flwr/common/serde.py +9 -6
- flwr/common/serde_utils.py +2 -2
- flwr/common/telemetry.py +9 -5
- flwr/common/typing.py +59 -43
- flwr/compat/client/app.py +39 -38
- flwr/compat/client/grpc_client/connection.py +13 -13
- flwr/compat/server/app.py +5 -6
- flwr/proto/appio_pb2.py +13 -3
- flwr/proto/appio_pb2.pyi +134 -65
- flwr/proto/appio_pb2_grpc.py +20 -0
- flwr/proto/appio_pb2_grpc.pyi +27 -0
- flwr/proto/clientappio_pb2.py +17 -7
- flwr/proto/clientappio_pb2.pyi +15 -0
- flwr/proto/clientappio_pb2_grpc.py +206 -40
- flwr/proto/clientappio_pb2_grpc.pyi +168 -53
- flwr/proto/control_pb2.py +72 -40
- flwr/proto/control_pb2.pyi +319 -87
- flwr/proto/control_pb2_grpc.py +339 -28
- flwr/proto/control_pb2_grpc.pyi +209 -37
- flwr/proto/error_pb2.py +13 -3
- flwr/proto/error_pb2.pyi +24 -6
- flwr/proto/error_pb2_grpc.py +20 -0
- flwr/proto/error_pb2_grpc.pyi +27 -0
- flwr/proto/fab_pb2.py +24 -10
- flwr/proto/fab_pb2.pyi +68 -20
- flwr/proto/fab_pb2_grpc.py +20 -0
- flwr/proto/fab_pb2_grpc.pyi +27 -0
- flwr/proto/federation_pb2.py +38 -0
- flwr/proto/federation_pb2.pyi +56 -0
- flwr/proto/federation_pb2_grpc.py +24 -0
- flwr/proto/federation_pb2_grpc.pyi +31 -0
- flwr/proto/fleet_pb2.py +45 -27
- flwr/proto/fleet_pb2.pyi +186 -70
- flwr/proto/fleet_pb2_grpc.py +277 -66
- flwr/proto/fleet_pb2_grpc.pyi +201 -55
- flwr/proto/grpcadapter_pb2.py +14 -4
- flwr/proto/grpcadapter_pb2.pyi +38 -16
- flwr/proto/grpcadapter_pb2_grpc.py +35 -4
- flwr/proto/grpcadapter_pb2_grpc.pyi +38 -7
- flwr/proto/heartbeat_pb2.py +17 -7
- flwr/proto/heartbeat_pb2.pyi +51 -22
- flwr/proto/heartbeat_pb2_grpc.py +20 -0
- flwr/proto/heartbeat_pb2_grpc.pyi +27 -0
- flwr/proto/log_pb2.py +13 -3
- flwr/proto/log_pb2.pyi +34 -11
- flwr/proto/log_pb2_grpc.py +20 -0
- flwr/proto/log_pb2_grpc.pyi +27 -0
- flwr/proto/message_pb2.py +15 -5
- flwr/proto/message_pb2.pyi +154 -86
- flwr/proto/message_pb2_grpc.py +20 -0
- flwr/proto/message_pb2_grpc.pyi +27 -0
- flwr/proto/node_pb2.py +16 -4
- flwr/proto/node_pb2.pyi +77 -4
- flwr/proto/node_pb2_grpc.py +20 -0
- flwr/proto/node_pb2_grpc.pyi +27 -0
- flwr/proto/recorddict_pb2.py +13 -3
- flwr/proto/recorddict_pb2.pyi +184 -107
- flwr/proto/recorddict_pb2_grpc.py +20 -0
- flwr/proto/recorddict_pb2_grpc.pyi +27 -0
- flwr/proto/run_pb2.py +40 -31
- flwr/proto/run_pb2.pyi +149 -84
- flwr/proto/run_pb2_grpc.py +20 -0
- flwr/proto/run_pb2_grpc.pyi +27 -0
- flwr/proto/serverappio_pb2.py +13 -3
- flwr/proto/serverappio_pb2.pyi +32 -8
- flwr/proto/serverappio_pb2_grpc.py +246 -65
- flwr/proto/serverappio_pb2_grpc.pyi +221 -85
- flwr/proto/simulationio_pb2.py +16 -8
- flwr/proto/simulationio_pb2.pyi +15 -0
- flwr/proto/simulationio_pb2_grpc.py +162 -41
- flwr/proto/simulationio_pb2_grpc.pyi +149 -55
- flwr/proto/transport_pb2.py +20 -10
- flwr/proto/transport_pb2.pyi +249 -160
- flwr/proto/transport_pb2_grpc.py +35 -4
- flwr/proto/transport_pb2_grpc.pyi +38 -8
- flwr/server/app.py +173 -127
- flwr/server/client_manager.py +4 -5
- flwr/server/client_proxy.py +10 -11
- flwr/server/compat/app.py +4 -5
- flwr/server/compat/app_utils.py +2 -1
- flwr/server/compat/grid_client_proxy.py +10 -12
- flwr/server/compat/legacy_context.py +3 -4
- flwr/server/fleet_event_log_interceptor.py +2 -1
- flwr/server/grid/grid.py +2 -3
- flwr/server/grid/grpc_grid.py +10 -8
- flwr/server/grid/inmemory_grid.py +4 -4
- flwr/server/run_serverapp.py +2 -3
- flwr/server/server.py +34 -39
- flwr/server/server_app.py +7 -8
- flwr/server/server_config.py +1 -2
- flwr/server/serverapp/app.py +34 -28
- flwr/server/serverapp_components.py +4 -5
- flwr/server/strategy/aggregate.py +9 -8
- flwr/server/strategy/bulyan.py +13 -11
- flwr/server/strategy/dp_adaptive_clipping.py +16 -20
- flwr/server/strategy/dp_fixed_clipping.py +12 -17
- flwr/server/strategy/dpfedavg_adaptive.py +3 -4
- flwr/server/strategy/dpfedavg_fixed.py +6 -10
- flwr/server/strategy/fault_tolerant_fedavg.py +14 -13
- flwr/server/strategy/fedadagrad.py +18 -14
- flwr/server/strategy/fedadam.py +16 -14
- flwr/server/strategy/fedavg.py +16 -17
- flwr/server/strategy/fedavg_android.py +15 -15
- flwr/server/strategy/fedavgm.py +21 -18
- flwr/server/strategy/fedmedian.py +2 -3
- flwr/server/strategy/fedopt.py +11 -10
- flwr/server/strategy/fedprox.py +10 -9
- flwr/server/strategy/fedtrimmedavg.py +12 -11
- flwr/server/strategy/fedxgb_bagging.py +13 -11
- flwr/server/strategy/fedxgb_cyclic.py +6 -6
- flwr/server/strategy/fedxgb_nn_avg.py +4 -4
- flwr/server/strategy/fedyogi.py +16 -14
- flwr/server/strategy/krum.py +12 -11
- flwr/server/strategy/qfedavg.py +16 -15
- flwr/server/strategy/strategy.py +6 -9
- flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +19 -8
- flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +1 -2
- flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py +3 -4
- flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py +10 -12
- flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +1 -3
- flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +136 -42
- flwr/server/superlink/fleet/grpc_rere/{server_interceptor.py → node_auth_server_interceptor.py} +28 -51
- flwr/server/superlink/fleet/message_handler/message_handler.py +100 -49
- flwr/server/superlink/fleet/rest_rere/rest_api.py +54 -33
- flwr/server/superlink/fleet/vce/backend/backend.py +2 -2
- flwr/server/superlink/fleet/vce/backend/raybackend.py +6 -6
- flwr/server/superlink/fleet/vce/vce_api.py +32 -13
- flwr/server/superlink/linkstate/in_memory_linkstate.py +266 -207
- flwr/server/superlink/linkstate/linkstate.py +161 -62
- flwr/server/superlink/linkstate/linkstate_factory.py +24 -6
- flwr/server/superlink/linkstate/sqlite_linkstate.py +698 -638
- flwr/server/superlink/linkstate/utils.py +9 -60
- flwr/server/superlink/serverappio/serverappio_grpc.py +1 -2
- flwr/server/superlink/serverappio/serverappio_servicer.py +28 -23
- flwr/server/superlink/simulation/simulationio_grpc.py +1 -2
- flwr/server/superlink/simulation/simulationio_servicer.py +19 -14
- flwr/server/superlink/utils.py +4 -6
- flwr/server/typing.py +1 -1
- flwr/server/utils/tensorboard.py +15 -8
- flwr/server/utils/validator.py +2 -3
- flwr/server/workflow/default_workflows.py +5 -5
- flwr/server/workflow/secure_aggregation/secagg_workflow.py +2 -4
- flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +12 -10
- flwr/serverapp/strategy/bulyan.py +16 -15
- flwr/serverapp/strategy/dp_adaptive_clipping.py +12 -11
- flwr/serverapp/strategy/dp_fixed_clipping.py +11 -14
- flwr/serverapp/strategy/fedadagrad.py +10 -11
- flwr/serverapp/strategy/fedadam.py +10 -11
- flwr/serverapp/strategy/fedavg.py +9 -10
- flwr/serverapp/strategy/fedavgm.py +17 -16
- flwr/serverapp/strategy/fedmedian.py +2 -2
- flwr/serverapp/strategy/fedopt.py +10 -11
- flwr/serverapp/strategy/fedprox.py +7 -8
- flwr/serverapp/strategy/fedtrimmedavg.py +9 -9
- flwr/serverapp/strategy/fedxgb_bagging.py +3 -3
- flwr/serverapp/strategy/fedxgb_cyclic.py +9 -9
- flwr/serverapp/strategy/fedyogi.py +9 -11
- flwr/serverapp/strategy/krum.py +7 -7
- flwr/serverapp/strategy/multikrum.py +9 -9
- flwr/serverapp/strategy/qfedavg.py +17 -16
- flwr/serverapp/strategy/strategy.py +6 -9
- flwr/serverapp/strategy/strategy_utils.py +7 -8
- flwr/simulation/app.py +46 -42
- flwr/simulation/legacy_app.py +12 -12
- flwr/simulation/ray_transport/ray_actor.py +11 -12
- flwr/simulation/ray_transport/ray_client_proxy.py +12 -13
- flwr/simulation/run_simulation.py +44 -43
- flwr/simulation/simulationio_connection.py +4 -4
- flwr/supercore/cli/flower_superexec.py +3 -4
- flwr/supercore/constant.py +52 -0
- flwr/supercore/corestate/corestate.py +24 -3
- flwr/supercore/corestate/in_memory_corestate.py +138 -0
- flwr/supercore/corestate/sqlite_corestate.py +157 -0
- flwr/supercore/ffs/disk_ffs.py +1 -2
- flwr/supercore/ffs/ffs.py +1 -2
- flwr/supercore/ffs/ffs_factory.py +1 -2
- flwr/{common → supercore}/heartbeat.py +20 -25
- flwr/supercore/object_store/in_memory_object_store.py +1 -6
- flwr/supercore/object_store/object_store.py +1 -2
- flwr/supercore/object_store/object_store_factory.py +27 -8
- flwr/supercore/object_store/sqlite_object_store.py +253 -0
- flwr/{client/clientapp → supercore/primitives}/__init__.py +1 -1
- flwr/supercore/primitives/asymmetric.py +117 -0
- flwr/supercore/primitives/asymmetric_ed25519.py +175 -0
- flwr/supercore/sqlite_mixin.py +159 -0
- flwr/supercore/superexec/plugin/base_exec_plugin.py +1 -2
- flwr/supercore/superexec/plugin/exec_plugin.py +3 -3
- flwr/supercore/superexec/run_superexec.py +9 -13
- flwr/supercore/utils.py +20 -0
- flwr/superlink/artifact_provider/artifact_provider.py +1 -2
- flwr/{common → superlink}/auth_plugin/__init__.py +6 -6
- flwr/superlink/auth_plugin/auth_plugin.py +88 -0
- flwr/superlink/auth_plugin/noop_auth_plugin.py +84 -0
- flwr/superlink/federation/__init__.py +24 -0
- flwr/superlink/federation/federation_manager.py +64 -0
- flwr/superlink/federation/noop_federation_manager.py +71 -0
- flwr/superlink/servicer/control/{control_user_auth_interceptor.py → control_account_auth_interceptor.py} +41 -32
- flwr/superlink/servicer/control/control_event_log_interceptor.py +7 -7
- flwr/superlink/servicer/control/control_grpc.py +18 -17
- flwr/superlink/servicer/control/control_license_interceptor.py +3 -3
- flwr/superlink/servicer/control/control_servicer.py +239 -63
- flwr/supernode/cli/flower_supernode.py +74 -26
- flwr/supernode/nodestate/in_memory_nodestate.py +60 -49
- flwr/supernode/nodestate/nodestate.py +7 -8
- flwr/supernode/nodestate/nodestate_factory.py +7 -4
- flwr/supernode/runtime/run_clientapp.py +43 -24
- flwr/supernode/servicer/clientappio/clientappio_servicer.py +40 -10
- flwr/supernode/start_client_internal.py +175 -51
- {flwr-1.22.0.dist-info → flwr-1.24.0.dist-info}/METADATA +8 -8
- flwr-1.24.0.dist-info/RECORD +454 -0
- flwr/common/auth_plugin/auth_plugin.py +0 -149
- flwr/supercore/object_store/utils.py +0 -43
- flwr-1.22.0.dist-info/RECORD +0 -428
- {flwr-1.22.0.dist-info → flwr-1.24.0.dist-info}/WHEEL +0 -0
- {flwr-1.22.0.dist-info → flwr-1.24.0.dist-info}/entry_points.txt +0 -0
flwr/cli/auth_plugin/__init__.py
CHANGED
|
@@ -12,20 +12,29 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
# ==============================================================================
|
|
15
|
-
"""Flower
|
|
15
|
+
"""Flower account auth plugins."""
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
from flwr.common.
|
|
19
|
-
from flwr.common.constant import AuthType
|
|
18
|
+
from flwr.common.constant import AuthnType
|
|
20
19
|
|
|
20
|
+
from .auth_plugin import CliAuthPlugin, LoginError
|
|
21
|
+
from .noop_auth_plugin import NoOpCliAuthPlugin
|
|
21
22
|
from .oidc_cli_plugin import OidcCliPlugin
|
|
22
23
|
|
|
23
24
|
|
|
24
|
-
def
|
|
25
|
+
def get_cli_plugin_class(authn_type: str) -> type[CliAuthPlugin]:
|
|
25
26
|
"""Return all CLI authentication plugins."""
|
|
26
|
-
|
|
27
|
+
if authn_type == AuthnType.NOOP:
|
|
28
|
+
return NoOpCliAuthPlugin
|
|
29
|
+
if authn_type == AuthnType.OIDC:
|
|
30
|
+
return OidcCliPlugin
|
|
31
|
+
raise ValueError(f"Unsupported authentication type: {authn_type}")
|
|
27
32
|
|
|
28
33
|
|
|
29
34
|
__all__ = [
|
|
30
|
-
"
|
|
35
|
+
"CliAuthPlugin",
|
|
36
|
+
"LoginError",
|
|
37
|
+
"NoOpCliAuthPlugin",
|
|
38
|
+
"OidcCliPlugin",
|
|
39
|
+
"get_cli_plugin_class",
|
|
31
40
|
]
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Copyright 2025 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 account auth plugin."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
from abc import ABC, abstractmethod
|
|
19
|
+
from collections.abc import Sequence
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
|
|
22
|
+
from flwr.common.typing import AccountAuthCredentials, AccountAuthLoginDetails
|
|
23
|
+
from flwr.proto.control_pb2_grpc import ControlStub
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class LoginError(Exception):
|
|
27
|
+
"""Login error exception."""
|
|
28
|
+
|
|
29
|
+
def __init__(self, message: str):
|
|
30
|
+
self.message = message
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class CliAuthPlugin(ABC):
|
|
34
|
+
"""Abstract Flower Auth Plugin class for CLI.
|
|
35
|
+
|
|
36
|
+
Parameters
|
|
37
|
+
----------
|
|
38
|
+
credentials_path : Path
|
|
39
|
+
Path to the Flower account's authentication credentials file.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
@staticmethod
|
|
43
|
+
@abstractmethod
|
|
44
|
+
def login(
|
|
45
|
+
login_details: AccountAuthLoginDetails,
|
|
46
|
+
control_stub: ControlStub,
|
|
47
|
+
) -> AccountAuthCredentials:
|
|
48
|
+
"""Authenticate the account and retrieve authentication credentials.
|
|
49
|
+
|
|
50
|
+
Parameters
|
|
51
|
+
----------
|
|
52
|
+
login_details : AccountAuthLoginDetails
|
|
53
|
+
An object containing the account's login details.
|
|
54
|
+
control_stub : ControlStub
|
|
55
|
+
A stub for executing RPC calls to the server.
|
|
56
|
+
|
|
57
|
+
Returns
|
|
58
|
+
-------
|
|
59
|
+
AccountAuthCredentials
|
|
60
|
+
The authentication credentials obtained after login.
|
|
61
|
+
|
|
62
|
+
Raises
|
|
63
|
+
------
|
|
64
|
+
LoginError
|
|
65
|
+
If the login process fails.
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
@abstractmethod
|
|
69
|
+
def __init__(self, credentials_path: Path):
|
|
70
|
+
"""Abstract constructor."""
|
|
71
|
+
|
|
72
|
+
@abstractmethod
|
|
73
|
+
def store_tokens(self, credentials: AccountAuthCredentials) -> None:
|
|
74
|
+
"""Store authentication tokens to the `credentials_path`.
|
|
75
|
+
|
|
76
|
+
The credentials, including tokens, will be saved as a JSON file
|
|
77
|
+
at `credentials_path`.
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
@abstractmethod
|
|
81
|
+
def load_tokens(self) -> None:
|
|
82
|
+
"""Load authentication tokens from the `credentials_path`."""
|
|
83
|
+
|
|
84
|
+
@abstractmethod
|
|
85
|
+
def write_tokens_to_metadata(
|
|
86
|
+
self, metadata: Sequence[tuple[str, str | bytes]]
|
|
87
|
+
) -> Sequence[tuple[str, str | bytes]]:
|
|
88
|
+
"""Write authentication tokens to the provided metadata."""
|
|
89
|
+
|
|
90
|
+
@abstractmethod
|
|
91
|
+
def read_tokens_from_metadata(
|
|
92
|
+
self, metadata: Sequence[tuple[str, str | bytes]]
|
|
93
|
+
) -> AccountAuthCredentials | None:
|
|
94
|
+
"""Read authentication tokens from the provided metadata."""
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Copyright 2025 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
|
+
"""Concrete NoOp implementation for CLI-side account authentication plugin."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
from collections.abc import Sequence
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
from flwr.common.typing import AccountAuthCredentials, AccountAuthLoginDetails
|
|
22
|
+
from flwr.proto.control_pb2_grpc import ControlStub
|
|
23
|
+
|
|
24
|
+
from .auth_plugin import CliAuthPlugin, LoginError
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class NoOpCliAuthPlugin(CliAuthPlugin):
|
|
28
|
+
"""No-operation implementation of the CliAuthPlugin.
|
|
29
|
+
|
|
30
|
+
This plugin is used when account authentication is not enabled. It provides stub
|
|
31
|
+
implementations of all authentication methods that perform no actions.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
@staticmethod
|
|
35
|
+
def login(
|
|
36
|
+
login_details: AccountAuthLoginDetails,
|
|
37
|
+
control_stub: ControlStub,
|
|
38
|
+
) -> AccountAuthCredentials:
|
|
39
|
+
"""Raise LoginError as no-op plugin does not support login.
|
|
40
|
+
|
|
41
|
+
Parameters
|
|
42
|
+
----------
|
|
43
|
+
login_details : AccountAuthLoginDetails
|
|
44
|
+
Login details (unused).
|
|
45
|
+
control_stub : ControlStub
|
|
46
|
+
Control stub (unused).
|
|
47
|
+
|
|
48
|
+
Returns
|
|
49
|
+
-------
|
|
50
|
+
AccountAuthCredentials
|
|
51
|
+
This method never returns as it always raises an exception.
|
|
52
|
+
|
|
53
|
+
Raises
|
|
54
|
+
------
|
|
55
|
+
LoginError
|
|
56
|
+
Always raised to indicate authentication is not enabled.
|
|
57
|
+
"""
|
|
58
|
+
raise LoginError("Account authentication is not enabled on this SuperLink.")
|
|
59
|
+
|
|
60
|
+
def __init__(self, credentials_path: Path) -> None:
|
|
61
|
+
pass
|
|
62
|
+
|
|
63
|
+
def store_tokens(self, credentials: AccountAuthCredentials) -> None:
|
|
64
|
+
"""Do nothing (no-op implementation)."""
|
|
65
|
+
|
|
66
|
+
def load_tokens(self) -> None:
|
|
67
|
+
"""Do nothing (no-op implementation)."""
|
|
68
|
+
|
|
69
|
+
def write_tokens_to_metadata(
|
|
70
|
+
self, metadata: Sequence[tuple[str, str | bytes]]
|
|
71
|
+
) -> Sequence[tuple[str, str | bytes]]:
|
|
72
|
+
"""Return the metadata unchanged.
|
|
73
|
+
|
|
74
|
+
Parameters
|
|
75
|
+
----------
|
|
76
|
+
metadata : Sequence[tuple[str, str | bytes]]
|
|
77
|
+
The original metadata.
|
|
78
|
+
|
|
79
|
+
Returns
|
|
80
|
+
-------
|
|
81
|
+
Sequence[tuple[str, str | bytes]]
|
|
82
|
+
The same metadata, unmodified.
|
|
83
|
+
"""
|
|
84
|
+
return metadata
|
|
85
|
+
|
|
86
|
+
def read_tokens_from_metadata(
|
|
87
|
+
self, metadata: Sequence[tuple[str, str | bytes]]
|
|
88
|
+
) -> AccountAuthCredentials | None:
|
|
89
|
+
"""Return None (no tokens to read).
|
|
90
|
+
|
|
91
|
+
Parameters
|
|
92
|
+
----------
|
|
93
|
+
metadata : Sequence[tuple[str, str | bytes]]
|
|
94
|
+
The metadata to read from (unused).
|
|
95
|
+
|
|
96
|
+
Returns
|
|
97
|
+
-------
|
|
98
|
+
None
|
|
99
|
+
Always returns None as no authentication is performed.
|
|
100
|
+
"""
|
|
101
|
+
return None
|
|
@@ -12,48 +12,71 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
# ==============================================================================
|
|
15
|
-
"""Flower CLI
|
|
15
|
+
"""Flower CLI account auth plugin for OIDC."""
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
import json
|
|
19
19
|
import time
|
|
20
20
|
from collections.abc import Sequence
|
|
21
21
|
from pathlib import Path
|
|
22
|
-
from typing import Any
|
|
22
|
+
from typing import Any
|
|
23
23
|
|
|
24
24
|
import typer
|
|
25
25
|
|
|
26
|
-
from flwr.common.auth_plugin import CliAuthPlugin
|
|
27
26
|
from flwr.common.constant import (
|
|
28
27
|
ACCESS_TOKEN_KEY,
|
|
29
|
-
|
|
28
|
+
AUTHN_TYPE_JSON_KEY,
|
|
30
29
|
REFRESH_TOKEN_KEY,
|
|
31
|
-
|
|
30
|
+
AuthnType,
|
|
32
31
|
)
|
|
33
|
-
from flwr.common.typing import
|
|
32
|
+
from flwr.common.typing import AccountAuthCredentials, AccountAuthLoginDetails
|
|
34
33
|
from flwr.proto.control_pb2 import ( # pylint: disable=E0611
|
|
35
34
|
GetAuthTokensRequest,
|
|
36
35
|
GetAuthTokensResponse,
|
|
37
36
|
)
|
|
38
37
|
from flwr.proto.control_pb2_grpc import ControlStub
|
|
39
38
|
|
|
39
|
+
from .auth_plugin import CliAuthPlugin, LoginError
|
|
40
|
+
|
|
40
41
|
|
|
41
42
|
class OidcCliPlugin(CliAuthPlugin):
|
|
42
|
-
"""Flower OIDC
|
|
43
|
+
"""Flower OIDC authentication plugin for CLI.
|
|
44
|
+
|
|
45
|
+
This plugin implements OpenID Connect (OIDC) device flow authentication for CLI
|
|
46
|
+
access to Flower SuperLink.
|
|
47
|
+
"""
|
|
43
48
|
|
|
44
49
|
def __init__(self, credentials_path: Path):
|
|
45
|
-
self.access_token:
|
|
46
|
-
self.refresh_token:
|
|
50
|
+
self.access_token: str | None = None
|
|
51
|
+
self.refresh_token: str | None = None
|
|
47
52
|
self.credentials_path = credentials_path
|
|
48
53
|
|
|
49
54
|
@staticmethod
|
|
50
55
|
def login(
|
|
51
|
-
login_details:
|
|
56
|
+
login_details: AccountAuthLoginDetails,
|
|
52
57
|
control_stub: ControlStub,
|
|
53
|
-
) ->
|
|
54
|
-
"""Authenticate the
|
|
58
|
+
) -> AccountAuthCredentials:
|
|
59
|
+
"""Authenticate the account and retrieve authentication credentials.
|
|
60
|
+
|
|
61
|
+
Parameters
|
|
62
|
+
----------
|
|
63
|
+
login_details : AccountAuthLoginDetails
|
|
64
|
+
Login details containing device code and verification URI.
|
|
65
|
+
control_stub : ControlStub
|
|
66
|
+
Control stub for making authentication requests.
|
|
67
|
+
|
|
68
|
+
Returns
|
|
69
|
+
-------
|
|
70
|
+
AccountAuthCredentials
|
|
71
|
+
The access and refresh tokens.
|
|
72
|
+
|
|
73
|
+
Raises
|
|
74
|
+
------
|
|
75
|
+
LoginError
|
|
76
|
+
If authentication times out.
|
|
77
|
+
"""
|
|
55
78
|
typer.secho(
|
|
56
|
-
"Please
|
|
79
|
+
"Please log into your Flower account here: "
|
|
57
80
|
f"{login_details.verification_uri_complete}",
|
|
58
81
|
fg=typer.colors.BLUE,
|
|
59
82
|
)
|
|
@@ -69,26 +92,16 @@ class OidcCliPlugin(CliAuthPlugin):
|
|
|
69
92
|
refresh_token = res.refresh_token
|
|
70
93
|
|
|
71
94
|
if access_token and refresh_token:
|
|
72
|
-
|
|
73
|
-
"✅ Login successful.",
|
|
74
|
-
fg=typer.colors.GREEN,
|
|
75
|
-
bold=False,
|
|
76
|
-
)
|
|
77
|
-
return UserAuthCredentials(
|
|
95
|
+
return AccountAuthCredentials(
|
|
78
96
|
access_token=access_token,
|
|
79
97
|
refresh_token=refresh_token,
|
|
80
98
|
)
|
|
81
99
|
|
|
82
100
|
time.sleep(login_details.interval)
|
|
83
101
|
|
|
84
|
-
|
|
85
|
-
"❌ Timeout, failed to sign in.",
|
|
86
|
-
fg=typer.colors.RED,
|
|
87
|
-
bold=True,
|
|
88
|
-
)
|
|
89
|
-
raise typer.Exit(code=1)
|
|
102
|
+
raise LoginError("Process timed out.")
|
|
90
103
|
|
|
91
|
-
def store_tokens(self, credentials:
|
|
104
|
+
def store_tokens(self, credentials: AccountAuthCredentials) -> None:
|
|
92
105
|
"""Store authentication tokens to the `credentials_path`.
|
|
93
106
|
|
|
94
107
|
The credentials, including tokens, will be saved as a JSON file
|
|
@@ -97,7 +110,7 @@ class OidcCliPlugin(CliAuthPlugin):
|
|
|
97
110
|
self.access_token = credentials.access_token
|
|
98
111
|
self.refresh_token = credentials.refresh_token
|
|
99
112
|
json_dict = {
|
|
100
|
-
|
|
113
|
+
AUTHN_TYPE_JSON_KEY: AuthnType.OIDC,
|
|
101
114
|
ACCESS_TOKEN_KEY: credentials.access_token,
|
|
102
115
|
REFRESH_TOKEN_KEY: credentials.refresh_token,
|
|
103
116
|
}
|
|
@@ -117,14 +130,15 @@ class OidcCliPlugin(CliAuthPlugin):
|
|
|
117
130
|
self.refresh_token = refresh_token
|
|
118
131
|
|
|
119
132
|
def write_tokens_to_metadata(
|
|
120
|
-
self, metadata: Sequence[tuple[str,
|
|
121
|
-
) -> Sequence[tuple[str,
|
|
133
|
+
self, metadata: Sequence[tuple[str, str | bytes]]
|
|
134
|
+
) -> Sequence[tuple[str, str | bytes]]:
|
|
122
135
|
"""Write authentication tokens to the provided metadata."""
|
|
123
136
|
if self.access_token is None or self.refresh_token is None:
|
|
124
137
|
typer.secho(
|
|
125
138
|
"❌ Missing authentication tokens. Please login first.",
|
|
126
139
|
fg=typer.colors.RED,
|
|
127
140
|
bold=True,
|
|
141
|
+
err=True,
|
|
128
142
|
)
|
|
129
143
|
raise typer.Exit(code=1)
|
|
130
144
|
|
|
@@ -134,15 +148,15 @@ class OidcCliPlugin(CliAuthPlugin):
|
|
|
134
148
|
]
|
|
135
149
|
|
|
136
150
|
def read_tokens_from_metadata(
|
|
137
|
-
self, metadata: Sequence[tuple[str,
|
|
138
|
-
) ->
|
|
151
|
+
self, metadata: Sequence[tuple[str, str | bytes]]
|
|
152
|
+
) -> AccountAuthCredentials | None:
|
|
139
153
|
"""Read authentication tokens from the provided metadata."""
|
|
140
154
|
metadata_dict = dict(metadata)
|
|
141
155
|
access_token = metadata_dict.get(ACCESS_TOKEN_KEY)
|
|
142
156
|
refresh_token = metadata_dict.get(REFRESH_TOKEN_KEY)
|
|
143
157
|
|
|
144
158
|
if isinstance(access_token, str) and isinstance(refresh_token, str):
|
|
145
|
-
return
|
|
159
|
+
return AccountAuthCredentials(
|
|
146
160
|
access_token=access_token,
|
|
147
161
|
refresh_token=refresh_token,
|
|
148
162
|
)
|