flwr-nightly 1.14.0.dev20241209__py3-none-any.whl → 1.14.0.dev20241210__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.
Potentially problematic release.
This version of flwr-nightly might be problematic. Click here for more details.
- flwr/common/auth_plugin/__init__.py +24 -0
- flwr/common/auth_plugin/auth_plugin.py +111 -0
- flwr/common/constant.py +2 -0
- flwr/server/app.py +52 -1
- flwr/superexec/exec_grpc.py +18 -1
- flwr/superexec/exec_servicer.py +22 -3
- flwr/superexec/exec_user_auth_interceptor.py +101 -0
- {flwr_nightly-1.14.0.dev20241209.dist-info → flwr_nightly-1.14.0.dev20241210.dist-info}/METADATA +3 -2
- {flwr_nightly-1.14.0.dev20241209.dist-info → flwr_nightly-1.14.0.dev20241210.dist-info}/RECORD +12 -9
- {flwr_nightly-1.14.0.dev20241209.dist-info → flwr_nightly-1.14.0.dev20241210.dist-info}/LICENSE +0 -0
- {flwr_nightly-1.14.0.dev20241209.dist-info → flwr_nightly-1.14.0.dev20241210.dist-info}/WHEEL +0 -0
- {flwr_nightly-1.14.0.dev20241209.dist-info → flwr_nightly-1.14.0.dev20241210.dist-info}/entry_points.txt +0 -0
|
@@ -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,111 @@
|
|
|
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 Any, Optional, Union
|
|
22
|
+
|
|
23
|
+
from flwr.proto.exec_pb2_grpc import ExecStub
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ExecAuthPlugin(ABC):
|
|
27
|
+
"""Abstract Flower Auth Plugin class for ExecServicer.
|
|
28
|
+
|
|
29
|
+
Parameters
|
|
30
|
+
----------
|
|
31
|
+
config : dict[str, Any]
|
|
32
|
+
The authentication configuration loaded from a YAML file.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
@abstractmethod
|
|
36
|
+
def __init__(self, config: dict[str, Any]):
|
|
37
|
+
"""Abstract constructor."""
|
|
38
|
+
|
|
39
|
+
@abstractmethod
|
|
40
|
+
def get_login_details(self) -> dict[str, str]:
|
|
41
|
+
"""Get the login details."""
|
|
42
|
+
|
|
43
|
+
@abstractmethod
|
|
44
|
+
def validate_tokens_in_metadata(
|
|
45
|
+
self, metadata: Sequence[tuple[str, Union[str, bytes]]]
|
|
46
|
+
) -> bool:
|
|
47
|
+
"""Validate authentication tokens in the provided metadata."""
|
|
48
|
+
|
|
49
|
+
@abstractmethod
|
|
50
|
+
def get_auth_tokens(self, auth_details: dict[str, str]) -> dict[str, str]:
|
|
51
|
+
"""Get authentication tokens."""
|
|
52
|
+
|
|
53
|
+
@abstractmethod
|
|
54
|
+
def refresh_tokens(
|
|
55
|
+
self, metadata: Sequence[tuple[str, Union[str, bytes]]]
|
|
56
|
+
) -> Optional[Sequence[tuple[str, Union[str, bytes]]]]:
|
|
57
|
+
"""Refresh authentication tokens in the provided metadata."""
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class CliAuthPlugin(ABC):
|
|
61
|
+
"""Abstract Flower Auth Plugin class for CLI.
|
|
62
|
+
|
|
63
|
+
Parameters
|
|
64
|
+
----------
|
|
65
|
+
user_auth_config_path : Path
|
|
66
|
+
The path to the user's authentication configuration file.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
@staticmethod
|
|
70
|
+
@abstractmethod
|
|
71
|
+
def login(
|
|
72
|
+
login_details: dict[str, str],
|
|
73
|
+
exec_stub: ExecStub,
|
|
74
|
+
) -> dict[str, Any]:
|
|
75
|
+
"""Authenticate the user with the SuperLink.
|
|
76
|
+
|
|
77
|
+
Parameters
|
|
78
|
+
----------
|
|
79
|
+
login_details : dict[str, str]
|
|
80
|
+
A dictionary containing the user's login details.
|
|
81
|
+
exec_stub : ExecStub
|
|
82
|
+
An instance of `ExecStub` used for communication with the SuperLink.
|
|
83
|
+
|
|
84
|
+
Returns
|
|
85
|
+
-------
|
|
86
|
+
user_auth_config : dict[str, Any]
|
|
87
|
+
A dictionary containing the user's authentication configuration
|
|
88
|
+
in JSON format.
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
@abstractmethod
|
|
92
|
+
def __init__(self, user_auth_config_path: Path):
|
|
93
|
+
"""Abstract constructor."""
|
|
94
|
+
|
|
95
|
+
@abstractmethod
|
|
96
|
+
def store_tokens(self, user_auth_config: dict[str, Any]) -> None:
|
|
97
|
+
"""Store authentication tokens from the provided user_auth_config.
|
|
98
|
+
|
|
99
|
+
The configuration, including tokens, will be saved as a JSON file
|
|
100
|
+
at `user_auth_config_path`.
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
@abstractmethod
|
|
104
|
+
def load_tokens(self) -> None:
|
|
105
|
+
"""Load authentication tokens from the user_auth_config_path."""
|
|
106
|
+
|
|
107
|
+
@abstractmethod
|
|
108
|
+
def write_tokens_to_metadata(
|
|
109
|
+
self, metadata: Sequence[tuple[str, Union[str, bytes]]]
|
|
110
|
+
) -> Sequence[tuple[str, Union[str, bytes]]]:
|
|
111
|
+
"""Write authentication tokens to the provided metadata."""
|
flwr/common/constant.py
CHANGED
|
@@ -110,6 +110,8 @@ LOG_UPLOAD_INTERVAL = 0.2 # Minimum interval between two log uploads
|
|
|
110
110
|
# Retry configurations
|
|
111
111
|
MAX_RETRY_DELAY = 20 # Maximum delay duration between two consecutive retries.
|
|
112
112
|
|
|
113
|
+
AUTH_TYPE = "auth_type"
|
|
114
|
+
|
|
113
115
|
|
|
114
116
|
class MessageType:
|
|
115
117
|
"""Message type."""
|
flwr/server/app.py
CHANGED
|
@@ -24,9 +24,10 @@ from collections.abc import Sequence
|
|
|
24
24
|
from logging import DEBUG, INFO, WARN
|
|
25
25
|
from pathlib import Path
|
|
26
26
|
from time import sleep
|
|
27
|
-
from typing import Optional
|
|
27
|
+
from typing import Any, Optional
|
|
28
28
|
|
|
29
29
|
import grpc
|
|
30
|
+
import yaml
|
|
30
31
|
from cryptography.exceptions import UnsupportedAlgorithm
|
|
31
32
|
from cryptography.hazmat.primitives.asymmetric import ec
|
|
32
33
|
from cryptography.hazmat.primitives.serialization import (
|
|
@@ -37,8 +38,10 @@ from cryptography.hazmat.primitives.serialization import (
|
|
|
37
38
|
from flwr.common import GRPC_MAX_MESSAGE_LENGTH, EventType, event
|
|
38
39
|
from flwr.common.address import parse_address
|
|
39
40
|
from flwr.common.args import try_obtain_server_certificates
|
|
41
|
+
from flwr.common.auth_plugin import ExecAuthPlugin
|
|
40
42
|
from flwr.common.config import get_flwr_dir, parse_config_args
|
|
41
43
|
from flwr.common.constant import (
|
|
44
|
+
AUTH_TYPE,
|
|
42
45
|
CLIENT_OCTET,
|
|
43
46
|
EXEC_API_DEFAULT_SERVER_ADDRESS,
|
|
44
47
|
FLEET_API_GRPC_BIDI_DEFAULT_ADDRESS,
|
|
@@ -88,6 +91,15 @@ DATABASE = ":flwr-in-memory-state:"
|
|
|
88
91
|
BASE_DIR = get_flwr_dir() / "superlink" / "ffs"
|
|
89
92
|
|
|
90
93
|
|
|
94
|
+
try:
|
|
95
|
+
from flwr.ee import get_exec_auth_plugins
|
|
96
|
+
except ImportError:
|
|
97
|
+
|
|
98
|
+
def get_exec_auth_plugins() -> dict[str, type[ExecAuthPlugin]]:
|
|
99
|
+
"""Return all Exec API authentication plugins."""
|
|
100
|
+
raise NotImplementedError("No authentication plugins are currently supported.")
|
|
101
|
+
|
|
102
|
+
|
|
91
103
|
def start_server( # pylint: disable=too-many-arguments,too-many-locals
|
|
92
104
|
*,
|
|
93
105
|
server_address: str = FLEET_API_GRPC_BIDI_DEFAULT_ADDRESS,
|
|
@@ -246,6 +258,12 @@ def run_superlink() -> None:
|
|
|
246
258
|
# Obtain certificates
|
|
247
259
|
certificates = try_obtain_server_certificates(args, args.fleet_api_type)
|
|
248
260
|
|
|
261
|
+
user_auth_config = _try_obtain_user_auth_config(args)
|
|
262
|
+
auth_plugin: Optional[ExecAuthPlugin] = None
|
|
263
|
+
# user_auth_config is None only if the args.user_auth_config is not provided
|
|
264
|
+
if user_auth_config is not None:
|
|
265
|
+
auth_plugin = _try_obtain_exec_auth_plugin(user_auth_config)
|
|
266
|
+
|
|
249
267
|
# Initialize StateFactory
|
|
250
268
|
state_factory = LinkStateFactory(args.database)
|
|
251
269
|
|
|
@@ -263,6 +281,7 @@ def run_superlink() -> None:
|
|
|
263
281
|
config=parse_config_args(
|
|
264
282
|
[args.executor_config] if args.executor_config else args.executor_config
|
|
265
283
|
),
|
|
284
|
+
auth_plugin=auth_plugin,
|
|
266
285
|
)
|
|
267
286
|
grpc_servers = [exec_server]
|
|
268
287
|
|
|
@@ -559,6 +578,32 @@ def _try_setup_node_authentication(
|
|
|
559
578
|
)
|
|
560
579
|
|
|
561
580
|
|
|
581
|
+
def _try_obtain_user_auth_config(args: argparse.Namespace) -> Optional[dict[str, Any]]:
|
|
582
|
+
if args.user_auth_config is not None:
|
|
583
|
+
with open(args.user_auth_config, encoding="utf-8") as file:
|
|
584
|
+
config: dict[str, Any] = yaml.safe_load(file)
|
|
585
|
+
return config
|
|
586
|
+
return None
|
|
587
|
+
|
|
588
|
+
|
|
589
|
+
def _try_obtain_exec_auth_plugin(config: dict[str, Any]) -> Optional[ExecAuthPlugin]:
|
|
590
|
+
auth_config: dict[str, Any] = config.get("authentication", {})
|
|
591
|
+
auth_type: str = auth_config.get(AUTH_TYPE, "")
|
|
592
|
+
try:
|
|
593
|
+
all_plugins: dict[str, type[ExecAuthPlugin]] = get_exec_auth_plugins()
|
|
594
|
+
auth_plugin_class = all_plugins[auth_type]
|
|
595
|
+
return auth_plugin_class(config=auth_config)
|
|
596
|
+
except KeyError:
|
|
597
|
+
if auth_type != "":
|
|
598
|
+
sys.exit(
|
|
599
|
+
f'Authentication type "{auth_type}" is not supported. '
|
|
600
|
+
"Please provide a valid authentication type in the configuration."
|
|
601
|
+
)
|
|
602
|
+
sys.exit("No authentication type is provided in the configuration.")
|
|
603
|
+
except NotImplementedError:
|
|
604
|
+
sys.exit("No authentication plugins are currently supported.")
|
|
605
|
+
|
|
606
|
+
|
|
562
607
|
def _run_fleet_api_grpc_rere(
|
|
563
608
|
address: str,
|
|
564
609
|
state_factory: LinkStateFactory,
|
|
@@ -746,6 +791,12 @@ def _add_args_common(parser: argparse.ArgumentParser) -> None:
|
|
|
746
791
|
type=str,
|
|
747
792
|
help="The SuperLink's public key (as a path str) to enable authentication.",
|
|
748
793
|
)
|
|
794
|
+
parser.add_argument(
|
|
795
|
+
"--user-auth-config",
|
|
796
|
+
help="The path to the user authentication configuration YAML file.",
|
|
797
|
+
type=str,
|
|
798
|
+
default=None,
|
|
799
|
+
)
|
|
749
800
|
|
|
750
801
|
|
|
751
802
|
def _add_args_serverappio_api(parser: argparse.ArgumentParser) -> None:
|
flwr/superexec/exec_grpc.py
CHANGED
|
@@ -14,18 +14,21 @@
|
|
|
14
14
|
# ==============================================================================
|
|
15
15
|
"""SuperExec gRPC API."""
|
|
16
16
|
|
|
17
|
+
from collections.abc import Sequence
|
|
17
18
|
from logging import INFO
|
|
18
19
|
from typing import Optional
|
|
19
20
|
|
|
20
21
|
import grpc
|
|
21
22
|
|
|
22
23
|
from flwr.common import GRPC_MAX_MESSAGE_LENGTH
|
|
24
|
+
from flwr.common.auth_plugin import ExecAuthPlugin
|
|
23
25
|
from flwr.common.logger import log
|
|
24
26
|
from flwr.common.typing import UserConfig
|
|
25
27
|
from flwr.proto.exec_pb2_grpc import add_ExecServicer_to_server
|
|
26
28
|
from flwr.server.superlink.ffs.ffs_factory import FfsFactory
|
|
27
29
|
from flwr.server.superlink.fleet.grpc_bidi.grpc_server import generic_create_grpc_server
|
|
28
30
|
from flwr.server.superlink.linkstate import LinkStateFactory
|
|
31
|
+
from flwr.superexec.exec_user_auth_interceptor import ExecUserAuthInterceptor
|
|
29
32
|
|
|
30
33
|
from .exec_servicer import ExecServicer
|
|
31
34
|
from .executor import Executor
|
|
@@ -39,6 +42,7 @@ def run_exec_api_grpc(
|
|
|
39
42
|
ffs_factory: FfsFactory,
|
|
40
43
|
certificates: Optional[tuple[bytes, bytes, bytes]],
|
|
41
44
|
config: UserConfig,
|
|
45
|
+
auth_plugin: Optional[ExecAuthPlugin] = None,
|
|
42
46
|
) -> grpc.Server:
|
|
43
47
|
"""Run Exec API (gRPC, request-response)."""
|
|
44
48
|
executor.set_config(config)
|
|
@@ -47,16 +51,29 @@ def run_exec_api_grpc(
|
|
|
47
51
|
linkstate_factory=state_factory,
|
|
48
52
|
ffs_factory=ffs_factory,
|
|
49
53
|
executor=executor,
|
|
54
|
+
auth_plugin=auth_plugin,
|
|
50
55
|
)
|
|
56
|
+
interceptors: Optional[Sequence[grpc.ServerInterceptor]] = None
|
|
57
|
+
if auth_plugin is not None:
|
|
58
|
+
interceptors = [ExecUserAuthInterceptor(auth_plugin)]
|
|
51
59
|
exec_add_servicer_to_server_fn = add_ExecServicer_to_server
|
|
52
60
|
exec_grpc_server = generic_create_grpc_server(
|
|
53
61
|
servicer_and_add_fn=(exec_servicer, exec_add_servicer_to_server_fn),
|
|
54
62
|
server_address=address,
|
|
55
63
|
max_message_length=GRPC_MAX_MESSAGE_LENGTH,
|
|
56
64
|
certificates=certificates,
|
|
65
|
+
interceptors=interceptors,
|
|
57
66
|
)
|
|
58
67
|
|
|
59
|
-
|
|
68
|
+
if auth_plugin is None:
|
|
69
|
+
log(INFO, "Flower Deployment Engine: Starting Exec API on %s", address)
|
|
70
|
+
else:
|
|
71
|
+
log(
|
|
72
|
+
INFO,
|
|
73
|
+
"Flower Deployment Engine: Starting Exec API with user "
|
|
74
|
+
"authentication on %s",
|
|
75
|
+
address,
|
|
76
|
+
)
|
|
60
77
|
exec_grpc_server.start()
|
|
61
78
|
|
|
62
79
|
return exec_grpc_server
|
flwr/superexec/exec_servicer.py
CHANGED
|
@@ -18,11 +18,12 @@
|
|
|
18
18
|
import time
|
|
19
19
|
from collections.abc import Generator
|
|
20
20
|
from logging import ERROR, INFO
|
|
21
|
-
from typing import Any
|
|
21
|
+
from typing import Any, Optional
|
|
22
22
|
|
|
23
23
|
import grpc
|
|
24
24
|
|
|
25
25
|
from flwr.common import now
|
|
26
|
+
from flwr.common.auth_plugin import ExecAuthPlugin
|
|
26
27
|
from flwr.common.constant import LOG_STREAM_INTERVAL, Status, SubStatus
|
|
27
28
|
from flwr.common.logger import log
|
|
28
29
|
from flwr.common.serde import (
|
|
@@ -60,11 +61,13 @@ class ExecServicer(exec_pb2_grpc.ExecServicer):
|
|
|
60
61
|
linkstate_factory: LinkStateFactory,
|
|
61
62
|
ffs_factory: FfsFactory,
|
|
62
63
|
executor: Executor,
|
|
64
|
+
auth_plugin: Optional[ExecAuthPlugin] = None,
|
|
63
65
|
) -> None:
|
|
64
66
|
self.linkstate_factory = linkstate_factory
|
|
65
67
|
self.ffs_factory = ffs_factory
|
|
66
68
|
self.executor = executor
|
|
67
69
|
self.executor.initialize(linkstate_factory, ffs_factory)
|
|
70
|
+
self.auth_plugin = auth_plugin
|
|
68
71
|
|
|
69
72
|
def StartRun(
|
|
70
73
|
self, request: StartRunRequest, context: grpc.ServicerContext
|
|
@@ -164,14 +167,30 @@ class ExecServicer(exec_pb2_grpc.ExecServicer):
|
|
|
164
167
|
) -> GetLoginDetailsResponse:
|
|
165
168
|
"""Start login."""
|
|
166
169
|
log(INFO, "ExecServicer.GetLoginDetails")
|
|
167
|
-
|
|
170
|
+
if self.auth_plugin is None:
|
|
171
|
+
context.abort(
|
|
172
|
+
grpc.StatusCode.UNIMPLEMENTED,
|
|
173
|
+
"ExecServicer initialized without user authentication",
|
|
174
|
+
)
|
|
175
|
+
raise grpc.RpcError() # This line is unreachable
|
|
176
|
+
return GetLoginDetailsResponse(
|
|
177
|
+
login_details=self.auth_plugin.get_login_details()
|
|
178
|
+
)
|
|
168
179
|
|
|
169
180
|
def GetAuthTokens(
|
|
170
181
|
self, request: GetAuthTokensRequest, context: grpc.ServicerContext
|
|
171
182
|
) -> GetAuthTokensResponse:
|
|
172
183
|
"""Get auth token."""
|
|
173
184
|
log(INFO, "ExecServicer.GetAuthTokens")
|
|
174
|
-
|
|
185
|
+
if self.auth_plugin is None:
|
|
186
|
+
context.abort(
|
|
187
|
+
grpc.StatusCode.UNIMPLEMENTED,
|
|
188
|
+
"ExecServicer initialized without user authentication",
|
|
189
|
+
)
|
|
190
|
+
raise grpc.RpcError() # This line is unreachable
|
|
191
|
+
return GetAuthTokensResponse(
|
|
192
|
+
auth_tokens=self.auth_plugin.get_auth_tokens(dict(request.auth_details))
|
|
193
|
+
)
|
|
175
194
|
|
|
176
195
|
|
|
177
196
|
def _create_list_runs_response(run_ids: set[int], state: LinkState) -> ListRunsResponse:
|
|
@@ -0,0 +1,101 @@
|
|
|
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
|
+
"""Flower Exec API interceptor."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
from typing import Any, Callable, Union
|
|
19
|
+
|
|
20
|
+
import grpc
|
|
21
|
+
|
|
22
|
+
from flwr.common.auth_plugin import ExecAuthPlugin
|
|
23
|
+
from flwr.proto.exec_pb2 import ( # pylint: disable=E0611
|
|
24
|
+
GetAuthTokensRequest,
|
|
25
|
+
GetAuthTokensResponse,
|
|
26
|
+
GetLoginDetailsRequest,
|
|
27
|
+
GetLoginDetailsResponse,
|
|
28
|
+
StartRunRequest,
|
|
29
|
+
StartRunResponse,
|
|
30
|
+
StreamLogsRequest,
|
|
31
|
+
StreamLogsResponse,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
Request = Union[
|
|
35
|
+
StartRunRequest,
|
|
36
|
+
StreamLogsRequest,
|
|
37
|
+
GetLoginDetailsRequest,
|
|
38
|
+
GetAuthTokensRequest,
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
Response = Union[
|
|
42
|
+
StartRunResponse, StreamLogsResponse, GetLoginDetailsResponse, GetAuthTokensResponse
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class ExecUserAuthInterceptor(grpc.ServerInterceptor): # type: ignore
|
|
47
|
+
"""Exec API interceptor for user authentication."""
|
|
48
|
+
|
|
49
|
+
def __init__(
|
|
50
|
+
self,
|
|
51
|
+
auth_plugin: ExecAuthPlugin,
|
|
52
|
+
):
|
|
53
|
+
self.auth_plugin = auth_plugin
|
|
54
|
+
|
|
55
|
+
def intercept_service(
|
|
56
|
+
self,
|
|
57
|
+
continuation: Callable[[Any], Any],
|
|
58
|
+
handler_call_details: grpc.HandlerCallDetails,
|
|
59
|
+
) -> grpc.RpcMethodHandler:
|
|
60
|
+
"""Flower server interceptor authentication logic.
|
|
61
|
+
|
|
62
|
+
Intercept all unary-unary/unary-stream calls from users and authenticate users
|
|
63
|
+
by validating auth metadata sent by the user. Continue RPC call if user is
|
|
64
|
+
authenticated, else, terminate RPC call by setting context to abort.
|
|
65
|
+
"""
|
|
66
|
+
# One of the method handlers in
|
|
67
|
+
# `flwr.superexec.exec_servicer.ExecServicer`
|
|
68
|
+
method_handler: grpc.RpcMethodHandler = continuation(handler_call_details)
|
|
69
|
+
return self._generic_auth_unary_method_handler(method_handler)
|
|
70
|
+
|
|
71
|
+
def _generic_auth_unary_method_handler(
|
|
72
|
+
self, method_handler: grpc.RpcMethodHandler
|
|
73
|
+
) -> grpc.RpcMethodHandler:
|
|
74
|
+
def _generic_method_handler(
|
|
75
|
+
request: Request,
|
|
76
|
+
context: grpc.ServicerContext,
|
|
77
|
+
) -> Response:
|
|
78
|
+
call = method_handler.unary_unary or method_handler.unary_stream
|
|
79
|
+
metadata = context.invocation_metadata()
|
|
80
|
+
if isinstance(
|
|
81
|
+
request, (GetLoginDetailsRequest, GetAuthTokensRequest)
|
|
82
|
+
) or self.auth_plugin.validate_tokens_in_metadata(metadata):
|
|
83
|
+
return call(request, context) # type: ignore
|
|
84
|
+
|
|
85
|
+
tokens = self.auth_plugin.refresh_tokens(context.invocation_metadata())
|
|
86
|
+
if tokens is not None:
|
|
87
|
+
context.send_initial_metadata(tokens)
|
|
88
|
+
return call(request, context) # type: ignore
|
|
89
|
+
|
|
90
|
+
context.abort(grpc.StatusCode.UNAUTHENTICATED, "Access denied")
|
|
91
|
+
raise grpc.RpcError() # This line is unreachable
|
|
92
|
+
|
|
93
|
+
if method_handler.unary_unary:
|
|
94
|
+
message_handler = grpc.unary_unary_rpc_method_handler
|
|
95
|
+
else:
|
|
96
|
+
message_handler = grpc.unary_stream_rpc_method_handler
|
|
97
|
+
return message_handler(
|
|
98
|
+
_generic_method_handler,
|
|
99
|
+
request_deserializer=method_handler.request_deserializer,
|
|
100
|
+
response_serializer=method_handler.response_serializer,
|
|
101
|
+
)
|
{flwr_nightly-1.14.0.dev20241209.dist-info → flwr_nightly-1.14.0.dev20241210.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: flwr-nightly
|
|
3
|
-
Version: 1.14.0.
|
|
3
|
+
Version: 1.14.0.dev20241210
|
|
4
4
|
Summary: Flower: A Friendly Federated AI Framework
|
|
5
5
|
Home-page: https://flower.ai
|
|
6
6
|
License: Apache-2.0
|
|
@@ -39,8 +39,9 @@ Requires-Dist: numpy (>=1.26.0,<3.0.0)
|
|
|
39
39
|
Requires-Dist: pathspec (>=0.12.1,<0.13.0)
|
|
40
40
|
Requires-Dist: protobuf (>=4.25.2,<5.0.0)
|
|
41
41
|
Requires-Dist: pycryptodome (>=3.18.0,<4.0.0)
|
|
42
|
+
Requires-Dist: pyyaml (>=6.0.2,<7.0.0)
|
|
42
43
|
Requires-Dist: ray (==2.10.0) ; (python_version >= "3.9" and python_version < "3.12") and (extra == "simulation")
|
|
43
|
-
Requires-Dist: requests (>=2.31.0,<3.0.0)
|
|
44
|
+
Requires-Dist: requests (>=2.31.0,<3.0.0)
|
|
44
45
|
Requires-Dist: rich (>=13.5.0,<14.0.0)
|
|
45
46
|
Requires-Dist: starlette (>=0.31.0,<0.32.0) ; extra == "rest"
|
|
46
47
|
Requires-Dist: tomli (>=2.0.1,<3.0.0)
|
{flwr_nightly-1.14.0.dev20241209.dist-info → flwr_nightly-1.14.0.dev20241210.dist-info}/RECORD
RENAMED
|
@@ -108,8 +108,10 @@ flwr/client/typing.py,sha256=dxoTBnTMfqXr5J7G3y-uNjqxYCddvxhu89spfj4Lm2U,1048
|
|
|
108
108
|
flwr/common/__init__.py,sha256=TVaoFEJE158aui1TPZQiJCDZX4RNHRyI8I55VC80HhI,3901
|
|
109
109
|
flwr/common/address.py,sha256=7kM2Rqjw86-c8aKwAvrXerWqznnVv4TFJ62aSAeTn10,3017
|
|
110
110
|
flwr/common/args.py,sha256=-KeQ6AZw1-G4Ifhsg4qlRnWhGH1m_OzUgxH7Z4j_0ns,6222
|
|
111
|
+
flwr/common/auth_plugin/__init__.py,sha256=1Y8Oj3iB49IHDu9tvDih1J74Ygu7k85V9s2A4WORPyA,887
|
|
112
|
+
flwr/common/auth_plugin/auth_plugin.py,sha256=6WEAVVPrS7LgSBpd4WyHYU4EsajT2nBGI_IN3mhYzoU,3567
|
|
111
113
|
flwr/common/config.py,sha256=qC1QvGAGr4faBtg3Y5dWhfyK5FggyWUMjPqg-Rx_FW4,8083
|
|
112
|
-
flwr/common/constant.py,sha256
|
|
114
|
+
flwr/common/constant.py,sha256=-HoTq6u_9VGbva21qTm_vfvQV9cxV7LwsvvlHEBjNwk,5817
|
|
113
115
|
flwr/common/context.py,sha256=uJ-mnoC_8y_udEb3kAX-r8CPphNTWM72z1AlsvQEu54,2403
|
|
114
116
|
flwr/common/date.py,sha256=NHHpESce5wYqEwoDXf09gp9U9l_5Bmlh2BsOcwS-kDM,1554
|
|
115
117
|
flwr/common/differential_privacy.py,sha256=XwcJ3rWr8S8BZUocc76vLSJAXIf6OHnWkBV6-xlIRuw,6106
|
|
@@ -206,7 +208,7 @@ flwr/proto/transport_pb2_grpc.py,sha256=vLN3EHtx2aEEMCO4f1Upu-l27BPzd3-5pV-u8wPc
|
|
|
206
208
|
flwr/proto/transport_pb2_grpc.pyi,sha256=AGXf8RiIiW2J5IKMlm_3qT3AzcDa4F3P5IqUjve_esA,766
|
|
207
209
|
flwr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
208
210
|
flwr/server/__init__.py,sha256=cEg1oecBu4cKB69iJCqWEylC8b5XW47bl7rQiJsdTvM,1528
|
|
209
|
-
flwr/server/app.py,sha256=
|
|
211
|
+
flwr/server/app.py,sha256=c9XHwSYuTL3xU9BKEbKeEJ0frWveectLjSr_EA4lnT8,30868
|
|
210
212
|
flwr/server/client_manager.py,sha256=7Ese0tgrH-i-ms363feYZJKwB8gWnXSmg_hYF2Bju4U,6227
|
|
211
213
|
flwr/server/client_proxy.py,sha256=4G-oTwhb45sfWLx2uZdcXD98IZwdTS6F88xe3akCdUg,2399
|
|
212
214
|
flwr/server/compat/__init__.py,sha256=VxnJtJyOjNFQXMNi9hIuzNlZM5n0Hj1p3aq_Pm2udw4,892
|
|
@@ -310,12 +312,13 @@ flwr/simulation/simulationio_connection.py,sha256=m31L9Iej-61va48E5x-wJypA6p5s82
|
|
|
310
312
|
flwr/superexec/__init__.py,sha256=fcj366jh4RFby_vDwLroU4kepzqbnJgseZD_jUr_Mko,715
|
|
311
313
|
flwr/superexec/app.py,sha256=Tt3GonnTwHrMmicwx9XaP-crP78-bf4DUWl-N5cG6zY,1841
|
|
312
314
|
flwr/superexec/deployment.py,sha256=7VYmmI12zEaTHp_cQtU1GLikmqhctUHhEdshBPRFHMs,6734
|
|
313
|
-
flwr/superexec/exec_grpc.py,sha256=
|
|
314
|
-
flwr/superexec/exec_servicer.py,sha256=
|
|
315
|
+
flwr/superexec/exec_grpc.py,sha256=hG1bxAbwB7Wt7R73931ib_UIcWvY628IIqk5rk3b25o,2896
|
|
316
|
+
flwr/superexec/exec_servicer.py,sha256=jEYcASzkQR1ftjzilmlcTPKXo8NSno9mSj_UbBvMjGM,7467
|
|
317
|
+
flwr/superexec/exec_user_auth_interceptor.py,sha256=K06OU-l4LnYhTDg071hGJuOaQWEJbZsYi5qxUmmtiG0,3704
|
|
315
318
|
flwr/superexec/executor.py,sha256=zH3_53il6Jh0ZscIVEB9f4GNnXMeBbCGyCoBCxLgiG0,3114
|
|
316
319
|
flwr/superexec/simulation.py,sha256=WQDon15oqpMopAZnwRZoTICYCfHqtkvFSqiTQ2hLD_g,4088
|
|
317
|
-
flwr_nightly-1.14.0.
|
|
318
|
-
flwr_nightly-1.14.0.
|
|
319
|
-
flwr_nightly-1.14.0.
|
|
320
|
-
flwr_nightly-1.14.0.
|
|
321
|
-
flwr_nightly-1.14.0.
|
|
320
|
+
flwr_nightly-1.14.0.dev20241210.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
|
321
|
+
flwr_nightly-1.14.0.dev20241210.dist-info/METADATA,sha256=3DjOUcyJbLgmX8glq_Tt7SV70giiQh5JP39zfpFyLjE,15700
|
|
322
|
+
flwr_nightly-1.14.0.dev20241210.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
|
323
|
+
flwr_nightly-1.14.0.dev20241210.dist-info/entry_points.txt,sha256=JlNxX3qhaV18_2yj5a3kJW1ESxm31cal9iS_N_pf1Rk,538
|
|
324
|
+
flwr_nightly-1.14.0.dev20241210.dist-info/RECORD,,
|
{flwr_nightly-1.14.0.dev20241209.dist-info → flwr_nightly-1.14.0.dev20241210.dist-info}/LICENSE
RENAMED
|
File without changes
|
{flwr_nightly-1.14.0.dev20241209.dist-info → flwr_nightly-1.14.0.dev20241210.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|