flwr 1.21.0__py3-none-any.whl → 1.23.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/cli/app.py +17 -1
- flwr/cli/auth_plugin/__init__.py +15 -6
- flwr/cli/auth_plugin/auth_plugin.py +95 -0
- flwr/cli/auth_plugin/noop_auth_plugin.py +58 -0
- flwr/cli/auth_plugin/oidc_cli_plugin.py +16 -25
- flwr/cli/build.py +118 -47
- flwr/cli/{cli_user_auth_interceptor.py → cli_account_auth_interceptor.py} +6 -5
- flwr/cli/log.py +2 -2
- flwr/cli/login/login.py +34 -23
- flwr/cli/ls.py +13 -9
- flwr/cli/new/new.py +196 -42
- flwr/cli/new/templates/app/README.flowertune.md.tpl +1 -1
- flwr/cli/new/templates/app/code/client.baseline.py.tpl +64 -47
- flwr/cli/new/templates/app/code/client.huggingface.py.tpl +68 -30
- flwr/cli/new/templates/app/code/client.jax.py.tpl +63 -42
- flwr/cli/new/templates/app/code/client.mlx.py.tpl +80 -51
- flwr/cli/new/templates/app/code/client.numpy.py.tpl +36 -13
- flwr/cli/new/templates/app/code/client.pytorch.py.tpl +71 -46
- flwr/cli/new/templates/app/code/client.pytorch_legacy_api.py.tpl +55 -0
- flwr/cli/new/templates/app/code/client.sklearn.py.tpl +75 -30
- flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +69 -44
- flwr/cli/new/templates/app/code/client.xgboost.py.tpl +110 -0
- flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl +56 -90
- flwr/cli/new/templates/app/code/flwr_tune/models.py.tpl +1 -23
- flwr/cli/new/templates/app/code/flwr_tune/server_app.py.tpl +37 -58
- flwr/cli/new/templates/app/code/flwr_tune/strategy.py.tpl +39 -44
- flwr/cli/new/templates/app/code/model.baseline.py.tpl +0 -14
- flwr/cli/new/templates/app/code/server.baseline.py.tpl +27 -29
- flwr/cli/new/templates/app/code/server.huggingface.py.tpl +23 -19
- flwr/cli/new/templates/app/code/server.jax.py.tpl +27 -14
- flwr/cli/new/templates/app/code/server.mlx.py.tpl +29 -19
- flwr/cli/new/templates/app/code/server.numpy.py.tpl +30 -17
- flwr/cli/new/templates/app/code/server.pytorch.py.tpl +36 -26
- flwr/cli/new/templates/app/code/server.pytorch_legacy_api.py.tpl +31 -0
- flwr/cli/new/templates/app/code/server.sklearn.py.tpl +29 -21
- flwr/cli/new/templates/app/code/server.tensorflow.py.tpl +28 -19
- flwr/cli/new/templates/app/code/server.xgboost.py.tpl +56 -0
- flwr/cli/new/templates/app/code/task.huggingface.py.tpl +16 -20
- flwr/cli/new/templates/app/code/task.jax.py.tpl +1 -1
- flwr/cli/new/templates/app/code/task.numpy.py.tpl +1 -1
- flwr/cli/new/templates/app/code/task.pytorch.py.tpl +14 -27
- flwr/cli/new/templates/app/code/{task.pytorch_msg_api.py.tpl → task.pytorch_legacy_api.py.tpl} +27 -14
- flwr/cli/new/templates/app/code/task.tensorflow.py.tpl +1 -2
- flwr/cli/new/templates/app/code/task.xgboost.py.tpl +67 -0
- flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +4 -4
- flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +2 -2
- flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +4 -4
- flwr/cli/new/templates/app/pyproject.jax.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +2 -2
- 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_msg_api.toml.tpl → pyproject.pytorch_legacy_api.toml.tpl} +3 -3
- flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.xgboost.toml.tpl +61 -0
- flwr/cli/pull.py +100 -0
- flwr/cli/run/run.py +11 -7
- flwr/cli/stop.py +2 -2
- flwr/cli/supernode/__init__.py +25 -0
- flwr/cli/supernode/ls.py +260 -0
- flwr/cli/supernode/register.py +185 -0
- flwr/cli/supernode/unregister.py +138 -0
- flwr/cli/utils.py +109 -69
- flwr/client/__init__.py +2 -1
- flwr/client/grpc_adapter_client/connection.py +6 -8
- flwr/client/grpc_rere_client/connection.py +59 -31
- flwr/client/grpc_rere_client/grpc_adapter.py +28 -12
- flwr/client/grpc_rere_client/{client_interceptor.py → node_auth_client_interceptor.py} +3 -6
- flwr/client/mod/secure_aggregation/secaggplus_mod.py +7 -5
- flwr/client/rest_client/connection.py +82 -37
- flwr/clientapp/__init__.py +1 -2
- flwr/clientapp/mod/__init__.py +4 -1
- flwr/clientapp/mod/centraldp_mods.py +156 -40
- flwr/clientapp/mod/localdp_mod.py +169 -0
- flwr/clientapp/typing.py +22 -0
- flwr/{client/clientapp → clientapp}/utils.py +1 -1
- flwr/common/constant.py +56 -13
- flwr/common/exit/exit_code.py +24 -10
- flwr/common/inflatable_utils.py +10 -10
- flwr/common/record/array.py +3 -3
- flwr/common/record/arrayrecord.py +10 -1
- flwr/common/record/typeddict.py +12 -0
- flwr/common/secure_aggregation/crypto/symmetric_encryption.py +1 -89
- flwr/common/serde.py +4 -2
- flwr/common/typing.py +7 -6
- flwr/compat/client/app.py +1 -1
- flwr/compat/client/grpc_client/connection.py +2 -2
- flwr/proto/control_pb2.py +48 -31
- flwr/proto/control_pb2.pyi +95 -5
- flwr/proto/control_pb2_grpc.py +136 -0
- flwr/proto/control_pb2_grpc.pyi +52 -0
- flwr/proto/fab_pb2.py +11 -7
- flwr/proto/fab_pb2.pyi +21 -1
- flwr/proto/fleet_pb2.py +31 -23
- flwr/proto/fleet_pb2.pyi +63 -23
- flwr/proto/fleet_pb2_grpc.py +98 -28
- flwr/proto/fleet_pb2_grpc.pyi +45 -13
- flwr/proto/node_pb2.py +3 -1
- flwr/proto/node_pb2.pyi +48 -0
- flwr/server/app.py +152 -114
- flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +17 -7
- flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +132 -38
- flwr/server/superlink/fleet/grpc_rere/{server_interceptor.py → node_auth_server_interceptor.py} +27 -51
- flwr/server/superlink/fleet/message_handler/message_handler.py +67 -22
- flwr/server/superlink/fleet/rest_rere/rest_api.py +52 -31
- flwr/server/superlink/fleet/vce/backend/backend.py +1 -1
- flwr/server/superlink/fleet/vce/backend/raybackend.py +1 -1
- flwr/server/superlink/fleet/vce/vce_api.py +18 -5
- flwr/server/superlink/linkstate/in_memory_linkstate.py +167 -73
- flwr/server/superlink/linkstate/linkstate.py +107 -24
- flwr/server/superlink/linkstate/linkstate_factory.py +2 -1
- flwr/server/superlink/linkstate/sqlite_linkstate.py +306 -255
- flwr/server/superlink/linkstate/utils.py +3 -54
- flwr/server/superlink/serverappio/serverappio_servicer.py +2 -2
- flwr/server/superlink/simulation/simulationio_servicer.py +1 -1
- flwr/server/utils/validator.py +2 -3
- flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +4 -2
- flwr/serverapp/strategy/__init__.py +26 -0
- flwr/serverapp/strategy/bulyan.py +238 -0
- flwr/serverapp/strategy/dp_adaptive_clipping.py +335 -0
- flwr/serverapp/strategy/dp_fixed_clipping.py +71 -49
- flwr/serverapp/strategy/fedadagrad.py +0 -3
- flwr/serverapp/strategy/fedadam.py +0 -3
- flwr/serverapp/strategy/fedavg.py +89 -64
- flwr/serverapp/strategy/fedavgm.py +198 -0
- flwr/serverapp/strategy/fedmedian.py +105 -0
- flwr/serverapp/strategy/fedprox.py +174 -0
- flwr/serverapp/strategy/fedtrimmedavg.py +176 -0
- flwr/serverapp/strategy/fedxgb_bagging.py +117 -0
- flwr/serverapp/strategy/fedxgb_cyclic.py +220 -0
- flwr/serverapp/strategy/fedyogi.py +0 -3
- flwr/serverapp/strategy/krum.py +112 -0
- flwr/serverapp/strategy/multikrum.py +247 -0
- flwr/serverapp/strategy/qfedavg.py +252 -0
- flwr/serverapp/strategy/strategy_utils.py +48 -0
- flwr/simulation/app.py +1 -1
- flwr/simulation/ray_transport/ray_actor.py +1 -1
- flwr/simulation/ray_transport/ray_client_proxy.py +1 -1
- flwr/simulation/run_simulation.py +28 -32
- flwr/supercore/cli/flower_superexec.py +26 -1
- flwr/supercore/constant.py +41 -0
- flwr/supercore/object_store/in_memory_object_store.py +0 -4
- flwr/supercore/object_store/object_store_factory.py +26 -6
- flwr/supercore/object_store/sqlite_object_store.py +252 -0
- flwr/{client/clientapp → supercore/primitives}/__init__.py +1 -1
- flwr/supercore/primitives/asymmetric.py +117 -0
- flwr/supercore/primitives/asymmetric_ed25519.py +165 -0
- flwr/supercore/sqlite_mixin.py +156 -0
- flwr/supercore/superexec/plugin/exec_plugin.py +11 -1
- flwr/supercore/superexec/run_superexec.py +16 -2
- flwr/supercore/utils.py +20 -0
- flwr/superlink/artifact_provider/__init__.py +22 -0
- flwr/superlink/artifact_provider/artifact_provider.py +37 -0
- flwr/{common → superlink}/auth_plugin/__init__.py +6 -6
- flwr/superlink/auth_plugin/auth_plugin.py +91 -0
- flwr/superlink/auth_plugin/noop_auth_plugin.py +87 -0
- flwr/superlink/servicer/control/{control_user_auth_interceptor.py → control_account_auth_interceptor.py} +19 -19
- flwr/superlink/servicer/control/control_event_log_interceptor.py +1 -1
- flwr/superlink/servicer/control/control_grpc.py +16 -11
- flwr/superlink/servicer/control/control_servicer.py +207 -58
- flwr/supernode/cli/flower_supernode.py +19 -26
- flwr/supernode/runtime/run_clientapp.py +2 -2
- flwr/supernode/servicer/clientappio/clientappio_servicer.py +1 -1
- flwr/supernode/start_client_internal.py +17 -9
- {flwr-1.21.0.dist-info → flwr-1.23.0.dist-info}/METADATA +6 -16
- {flwr-1.21.0.dist-info → flwr-1.23.0.dist-info}/RECORD +170 -140
- flwr/cli/new/templates/app/code/client.pytorch_msg_api.py.tpl +0 -80
- flwr/cli/new/templates/app/code/server.pytorch_msg_api.py.tpl +0 -41
- flwr/common/auth_plugin/auth_plugin.py +0 -149
- flwr/serverapp/dp_fixed_clipping.py +0 -352
- flwr/serverapp/strategy/strategy_utils_tests.py +0 -304
- /flwr/cli/new/templates/app/code/{__init__.pytorch_msg_api.py.tpl → __init__.pytorch_legacy_api.py.tpl} +0 -0
- /flwr/{client → clientapp}/client_app.py +0 -0
- {flwr-1.21.0.dist-info → flwr-1.23.0.dist-info}/WHEEL +0 -0
- {flwr-1.21.0.dist-info → flwr-1.23.0.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,91 @@
|
|
|
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 plugins."""
|
|
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.common.typing import (
|
|
24
|
+
AccountAuthCredentials,
|
|
25
|
+
AccountAuthLoginDetails,
|
|
26
|
+
AccountInfo,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class ControlAuthnPlugin(ABC):
|
|
31
|
+
"""Abstract Flower Authentication Plugin class for ControlServicer.
|
|
32
|
+
|
|
33
|
+
Parameters
|
|
34
|
+
----------
|
|
35
|
+
account_auth_config_path : Path
|
|
36
|
+
Path to the YAML file containing the authentication configuration.
|
|
37
|
+
verify_tls_cert : bool
|
|
38
|
+
Boolean indicating whether to verify the TLS certificate
|
|
39
|
+
when making requests to the server.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
@abstractmethod
|
|
43
|
+
def __init__(
|
|
44
|
+
self,
|
|
45
|
+
account_auth_config_path: Path,
|
|
46
|
+
verify_tls_cert: bool,
|
|
47
|
+
):
|
|
48
|
+
"""Abstract constructor."""
|
|
49
|
+
|
|
50
|
+
@abstractmethod
|
|
51
|
+
def get_login_details(self) -> Optional[AccountAuthLoginDetails]:
|
|
52
|
+
"""Get the login details."""
|
|
53
|
+
|
|
54
|
+
@abstractmethod
|
|
55
|
+
def validate_tokens_in_metadata(
|
|
56
|
+
self, metadata: Sequence[tuple[str, Union[str, bytes]]]
|
|
57
|
+
) -> tuple[bool, Optional[AccountInfo]]:
|
|
58
|
+
"""Validate authentication tokens in the provided metadata."""
|
|
59
|
+
|
|
60
|
+
@abstractmethod
|
|
61
|
+
def get_auth_tokens(self, device_code: str) -> Optional[AccountAuthCredentials]:
|
|
62
|
+
"""Get authentication tokens."""
|
|
63
|
+
|
|
64
|
+
@abstractmethod
|
|
65
|
+
def refresh_tokens(
|
|
66
|
+
self, metadata: Sequence[tuple[str, Union[str, bytes]]]
|
|
67
|
+
) -> tuple[
|
|
68
|
+
Optional[Sequence[tuple[str, Union[str, bytes]]]], Optional[AccountInfo]
|
|
69
|
+
]:
|
|
70
|
+
"""Refresh authentication tokens in the provided metadata."""
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class ControlAuthzPlugin(ABC): # pylint: disable=too-few-public-methods
|
|
74
|
+
"""Abstract Flower Authorization Plugin class for ControlServicer.
|
|
75
|
+
|
|
76
|
+
Parameters
|
|
77
|
+
----------
|
|
78
|
+
account_auth_config_path : Path
|
|
79
|
+
Path to the YAML file containing the authorization configuration.
|
|
80
|
+
verify_tls_cert : bool
|
|
81
|
+
Boolean indicating whether to verify the TLS certificate
|
|
82
|
+
when making requests to the server.
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
@abstractmethod
|
|
86
|
+
def __init__(self, account_auth_config_path: Path, verify_tls_cert: bool):
|
|
87
|
+
"""Abstract constructor."""
|
|
88
|
+
|
|
89
|
+
@abstractmethod
|
|
90
|
+
def authorize(self, account_info: AccountInfo) -> bool:
|
|
91
|
+
"""Verify account authorization request."""
|
|
@@ -0,0 +1,87 @@
|
|
|
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 Servicer-side account authentication and
|
|
16
|
+
authorization plugins."""
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
from collections.abc import Sequence
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from typing import Optional, Union
|
|
22
|
+
|
|
23
|
+
from flwr.common.constant import NOOP_ACCOUNT_NAME, NOOP_FLWR_AID, AuthnType
|
|
24
|
+
from flwr.common.typing import (
|
|
25
|
+
AccountAuthCredentials,
|
|
26
|
+
AccountAuthLoginDetails,
|
|
27
|
+
AccountInfo,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
from .auth_plugin import ControlAuthnPlugin, ControlAuthzPlugin
|
|
31
|
+
|
|
32
|
+
NOOP_ACCOUNT_INFO = AccountInfo(
|
|
33
|
+
flwr_aid=NOOP_FLWR_AID,
|
|
34
|
+
account_name=NOOP_ACCOUNT_NAME,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class NoOpControlAuthnPlugin(ControlAuthnPlugin):
|
|
39
|
+
"""No-operation implementation of ControlAuthnPlugin."""
|
|
40
|
+
|
|
41
|
+
def __init__(
|
|
42
|
+
self,
|
|
43
|
+
account_auth_config_path: Path,
|
|
44
|
+
verify_tls_cert: bool,
|
|
45
|
+
):
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
def get_login_details(self) -> Optional[AccountAuthLoginDetails]:
|
|
49
|
+
"""Get the login details."""
|
|
50
|
+
# This allows the `flwr login` command to load the NoOp plugin accordingly,
|
|
51
|
+
# which then raises a LoginError when attempting to login.
|
|
52
|
+
return AccountAuthLoginDetails(
|
|
53
|
+
authn_type=AuthnType.NOOP, # No operation authn type
|
|
54
|
+
device_code="",
|
|
55
|
+
verification_uri_complete="",
|
|
56
|
+
expires_in=0,
|
|
57
|
+
interval=0,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
def validate_tokens_in_metadata(
|
|
61
|
+
self, metadata: Sequence[tuple[str, Union[str, bytes]]]
|
|
62
|
+
) -> tuple[bool, Optional[AccountInfo]]:
|
|
63
|
+
"""Return valid for no-op plugin."""
|
|
64
|
+
return True, NOOP_ACCOUNT_INFO
|
|
65
|
+
|
|
66
|
+
def get_auth_tokens(self, device_code: str) -> Optional[AccountAuthCredentials]:
|
|
67
|
+
"""Get authentication tokens."""
|
|
68
|
+
raise RuntimeError("NoOp plugin does not support getting auth tokens.")
|
|
69
|
+
|
|
70
|
+
def refresh_tokens(
|
|
71
|
+
self, metadata: Sequence[tuple[str, Union[str, bytes]]]
|
|
72
|
+
) -> tuple[
|
|
73
|
+
Optional[Sequence[tuple[str, Union[str, bytes]]]], Optional[AccountInfo]
|
|
74
|
+
]:
|
|
75
|
+
"""Refresh authentication tokens in the provided metadata."""
|
|
76
|
+
return metadata, NOOP_ACCOUNT_INFO
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class NoOpControlAuthzPlugin(ControlAuthzPlugin):
|
|
80
|
+
"""No-operation implementation of ControlAuthzPlugin."""
|
|
81
|
+
|
|
82
|
+
def __init__(self, account_auth_config_path: Path, verify_tls_cert: bool):
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
def authorize(self, account_info: AccountInfo) -> bool:
|
|
86
|
+
"""Return True for no-op plugin."""
|
|
87
|
+
return True
|
|
@@ -20,7 +20,6 @@ from typing import Any, Callable, Union
|
|
|
20
20
|
|
|
21
21
|
import grpc
|
|
22
22
|
|
|
23
|
-
from flwr.common.auth_plugin import ControlAuthPlugin, ControlAuthzPlugin
|
|
24
23
|
from flwr.common.typing import AccountInfo
|
|
25
24
|
from flwr.proto.control_pb2 import ( # pylint: disable=E0611
|
|
26
25
|
GetAuthTokensRequest,
|
|
@@ -32,6 +31,7 @@ from flwr.proto.control_pb2 import ( # pylint: disable=E0611
|
|
|
32
31
|
StreamLogsRequest,
|
|
33
32
|
StreamLogsResponse,
|
|
34
33
|
)
|
|
34
|
+
from flwr.superlink.auth_plugin import ControlAuthnPlugin, ControlAuthzPlugin
|
|
35
35
|
|
|
36
36
|
Request = Union[
|
|
37
37
|
StartRunRequest,
|
|
@@ -50,15 +50,15 @@ shared_account_info: contextvars.ContextVar[AccountInfo] = contextvars.ContextVa
|
|
|
50
50
|
)
|
|
51
51
|
|
|
52
52
|
|
|
53
|
-
class
|
|
54
|
-
"""Control API interceptor for
|
|
53
|
+
class ControlAccountAuthInterceptor(grpc.ServerInterceptor): # type: ignore
|
|
54
|
+
"""Control API interceptor for account authentication."""
|
|
55
55
|
|
|
56
56
|
def __init__(
|
|
57
57
|
self,
|
|
58
|
-
|
|
58
|
+
authn_plugin: ControlAuthnPlugin,
|
|
59
59
|
authz_plugin: ControlAuthzPlugin,
|
|
60
60
|
):
|
|
61
|
-
self.
|
|
61
|
+
self.authn_plugin = authn_plugin
|
|
62
62
|
self.authz_plugin = authz_plugin
|
|
63
63
|
|
|
64
64
|
def intercept_service(
|
|
@@ -96,45 +96,45 @@ class ControlUserAuthInterceptor(grpc.ServerInterceptor): # type: ignore
|
|
|
96
96
|
if isinstance(request, (GetLoginDetailsRequest, GetAuthTokensRequest)):
|
|
97
97
|
return call(request, context) # type: ignore
|
|
98
98
|
|
|
99
|
-
# For other requests, check if the
|
|
100
|
-
valid_tokens, account_info = self.
|
|
99
|
+
# For other requests, check if the account is authenticated
|
|
100
|
+
valid_tokens, account_info = self.authn_plugin.validate_tokens_in_metadata(
|
|
101
101
|
metadata
|
|
102
102
|
)
|
|
103
103
|
if valid_tokens:
|
|
104
104
|
if account_info is None:
|
|
105
105
|
context.abort(
|
|
106
106
|
grpc.StatusCode.UNAUTHENTICATED,
|
|
107
|
-
"Tokens validated, but
|
|
107
|
+
"Tokens validated, but account info not found",
|
|
108
108
|
)
|
|
109
109
|
raise grpc.RpcError()
|
|
110
|
-
# Store
|
|
110
|
+
# Store account info in contextvars for authenticated accounts
|
|
111
111
|
shared_account_info.set(account_info)
|
|
112
|
-
# Check if the
|
|
113
|
-
if not self.authz_plugin.
|
|
112
|
+
# Check if the account is authorized
|
|
113
|
+
if not self.authz_plugin.authorize(account_info):
|
|
114
114
|
context.abort(
|
|
115
115
|
grpc.StatusCode.PERMISSION_DENIED,
|
|
116
|
-
"❗️
|
|
116
|
+
"❗️ Account not authorized. "
|
|
117
117
|
"Please contact the SuperLink administrator.",
|
|
118
118
|
)
|
|
119
119
|
raise grpc.RpcError()
|
|
120
120
|
return call(request, context) # type: ignore
|
|
121
121
|
|
|
122
|
-
# If the
|
|
123
|
-
tokens, account_info = self.
|
|
122
|
+
# If the account is not authenticated, refresh tokens
|
|
123
|
+
tokens, account_info = self.authn_plugin.refresh_tokens(metadata)
|
|
124
124
|
if tokens is not None:
|
|
125
125
|
if account_info is None:
|
|
126
126
|
context.abort(
|
|
127
127
|
grpc.StatusCode.UNAUTHENTICATED,
|
|
128
|
-
"Tokens refreshed, but
|
|
128
|
+
"Tokens refreshed, but account info not found",
|
|
129
129
|
)
|
|
130
130
|
raise grpc.RpcError()
|
|
131
|
-
# Store
|
|
131
|
+
# Store account info in contextvars for authenticated accounts
|
|
132
132
|
shared_account_info.set(account_info)
|
|
133
|
-
# Check if the
|
|
134
|
-
if not self.authz_plugin.
|
|
133
|
+
# Check if the account is authorized
|
|
134
|
+
if not self.authz_plugin.authorize(account_info):
|
|
135
135
|
context.abort(
|
|
136
136
|
grpc.StatusCode.PERMISSION_DENIED,
|
|
137
|
-
"❗️
|
|
137
|
+
"❗️ Account not authorized. "
|
|
138
138
|
"Please contact the SuperLink administrator.",
|
|
139
139
|
)
|
|
140
140
|
raise grpc.RpcError()
|
|
@@ -24,7 +24,7 @@ from google.protobuf.message import Message as GrpcMessage
|
|
|
24
24
|
from flwr.common.event_log_plugin.event_log_plugin import EventLogWriterPlugin
|
|
25
25
|
from flwr.common.typing import LogEntry
|
|
26
26
|
|
|
27
|
-
from .
|
|
27
|
+
from .control_account_auth_interceptor import shared_account_info
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
class ControlEventLogInterceptor(grpc.ServerInterceptor): # type: ignore
|
|
@@ -21,7 +21,6 @@ from typing import Optional
|
|
|
21
21
|
import grpc
|
|
22
22
|
|
|
23
23
|
from flwr.common import GRPC_MAX_MESSAGE_LENGTH
|
|
24
|
-
from flwr.common.auth_plugin import ControlAuthPlugin, ControlAuthzPlugin
|
|
25
24
|
from flwr.common.event_log_plugin import EventLogWriterPlugin
|
|
26
25
|
from flwr.common.exit import ExitCode, flwr_exit
|
|
27
26
|
from flwr.common.grpc import generic_create_grpc_server
|
|
@@ -31,11 +30,17 @@ from flwr.server.superlink.linkstate import LinkStateFactory
|
|
|
31
30
|
from flwr.supercore.ffs import FfsFactory
|
|
32
31
|
from flwr.supercore.license_plugin import LicensePlugin
|
|
33
32
|
from flwr.supercore.object_store import ObjectStoreFactory
|
|
33
|
+
from flwr.superlink.artifact_provider import ArtifactProvider
|
|
34
|
+
from flwr.superlink.auth_plugin import (
|
|
35
|
+
ControlAuthnPlugin,
|
|
36
|
+
ControlAuthzPlugin,
|
|
37
|
+
NoOpControlAuthnPlugin,
|
|
38
|
+
)
|
|
34
39
|
|
|
40
|
+
from .control_account_auth_interceptor import ControlAccountAuthInterceptor
|
|
35
41
|
from .control_event_log_interceptor import ControlEventLogInterceptor
|
|
36
42
|
from .control_license_interceptor import ControlLicenseInterceptor
|
|
37
43
|
from .control_servicer import ControlServicer
|
|
38
|
-
from .control_user_auth_interceptor import ControlUserAuthInterceptor
|
|
39
44
|
|
|
40
45
|
try:
|
|
41
46
|
from flwr.ee import get_license_plugin
|
|
@@ -53,9 +58,10 @@ def run_control_api_grpc(
|
|
|
53
58
|
objectstore_factory: ObjectStoreFactory,
|
|
54
59
|
certificates: Optional[tuple[bytes, bytes, bytes]],
|
|
55
60
|
is_simulation: bool,
|
|
56
|
-
|
|
57
|
-
authz_plugin:
|
|
61
|
+
authn_plugin: ControlAuthnPlugin,
|
|
62
|
+
authz_plugin: ControlAuthzPlugin,
|
|
58
63
|
event_log_plugin: Optional[EventLogWriterPlugin] = None,
|
|
64
|
+
artifact_provider: Optional[ArtifactProvider] = None,
|
|
59
65
|
) -> grpc.Server:
|
|
60
66
|
"""Run Control API (gRPC, request-response)."""
|
|
61
67
|
license_plugin: Optional[LicensePlugin] = get_license_plugin()
|
|
@@ -67,14 +73,13 @@ def run_control_api_grpc(
|
|
|
67
73
|
ffs_factory=ffs_factory,
|
|
68
74
|
objectstore_factory=objectstore_factory,
|
|
69
75
|
is_simulation=is_simulation,
|
|
70
|
-
|
|
76
|
+
authn_plugin=authn_plugin,
|
|
77
|
+
artifact_provider=artifact_provider,
|
|
71
78
|
)
|
|
72
|
-
interceptors
|
|
79
|
+
interceptors = [ControlAccountAuthInterceptor(authn_plugin, authz_plugin)]
|
|
73
80
|
if license_plugin is not None:
|
|
74
81
|
interceptors.append(ControlLicenseInterceptor(license_plugin))
|
|
75
|
-
|
|
76
|
-
interceptors.append(ControlUserAuthInterceptor(auth_plugin, authz_plugin))
|
|
77
|
-
# Event log interceptor must be added after user auth interceptor
|
|
82
|
+
# Event log interceptor must be added after account auth interceptor
|
|
78
83
|
if event_log_plugin is not None:
|
|
79
84
|
interceptors.append(ControlEventLogInterceptor(event_log_plugin))
|
|
80
85
|
log(INFO, "Flower event logging enabled")
|
|
@@ -87,12 +92,12 @@ def run_control_api_grpc(
|
|
|
87
92
|
interceptors=interceptors or None,
|
|
88
93
|
)
|
|
89
94
|
|
|
90
|
-
if
|
|
95
|
+
if isinstance(authn_plugin, NoOpControlAuthnPlugin):
|
|
91
96
|
log(INFO, "Flower Deployment Runtime: Starting Control API on %s", address)
|
|
92
97
|
else:
|
|
93
98
|
log(
|
|
94
99
|
INFO,
|
|
95
|
-
"Flower Deployment Runtime: Starting Control API with
|
|
100
|
+
"Flower Deployment Runtime: Starting Control API with account "
|
|
96
101
|
"authentication on %s",
|
|
97
102
|
address,
|
|
98
103
|
)
|