flwr-nightly 1.15.0.dev20250106__py3-none-any.whl → 1.15.0.dev20250108__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.
@@ -54,8 +54,12 @@ class CliUserAuthInterceptor(
54
54
 
55
55
  response = continuation(details, request)
56
56
  if response.initial_metadata():
57
- retrieved_metadata = dict(response.initial_metadata())
58
- self.auth_plugin.store_tokens(retrieved_metadata)
57
+ credentials = self.auth_plugin.read_tokens_from_metadata(
58
+ response.initial_metadata()
59
+ )
60
+ # The metadata contains tokens only if they have been refreshed
61
+ if credentials is not None:
62
+ self.auth_plugin.store_tokens(credentials)
59
63
 
60
64
  return response
61
65
 
flwr/cli/login/login.py CHANGED
@@ -26,7 +26,7 @@ from flwr.cli.config_utils import (
26
26
  process_loaded_project_config,
27
27
  validate_federation_in_project_config,
28
28
  )
29
- from flwr.common.constant import AUTH_TYPE
29
+ from flwr.common.typing import UserAuthLoginDetails
30
30
  from flwr.proto.exec_pb2 import ( # pylint: disable=E0611
31
31
  GetLoginDetailsRequest,
32
32
  GetLoginDetailsResponse,
@@ -64,7 +64,7 @@ def login( # pylint: disable=R0914
64
64
  login_response: GetLoginDetailsResponse = stub.GetLoginDetails(login_request)
65
65
 
66
66
  # Get the auth plugin
67
- auth_type = login_response.login_details.get(AUTH_TYPE)
67
+ auth_type = login_response.auth_type
68
68
  auth_plugin = try_obtain_cli_auth_plugin(app, federation, auth_type)
69
69
  if auth_plugin is None:
70
70
  typer.secho(
@@ -75,7 +75,14 @@ def login( # pylint: disable=R0914
75
75
  raise typer.Exit(code=1)
76
76
 
77
77
  # Login
78
- auth_config = auth_plugin.login(dict(login_response.login_details), stub)
78
+ details = UserAuthLoginDetails(
79
+ auth_type=login_response.auth_type,
80
+ device_code=login_response.device_code,
81
+ verification_uri_complete=login_response.verification_uri_complete,
82
+ expires_in=login_response.expires_in,
83
+ interval=login_response.interval,
84
+ )
85
+ credentials = auth_plugin.login(details, stub)
79
86
 
80
87
  # Store the tokens
81
- auth_plugin.store_tokens(auth_config)
88
+ auth_plugin.store_tokens(credentials)
flwr/cli/utils.py CHANGED
@@ -223,19 +223,19 @@ def try_obtain_cli_auth_plugin(
223
223
  config_path = get_user_auth_config_path(root_dir, federation)
224
224
 
225
225
  # Load the config file if it exists
226
- config: dict[str, Any] = {}
226
+ json_file: dict[str, Any] = {}
227
227
  if config_path.exists():
228
228
  with config_path.open("r", encoding="utf-8") as file:
229
- config = json.load(file)
229
+ json_file = json.load(file)
230
230
  # This is the case when the user auth is not enabled
231
231
  elif auth_type is None:
232
232
  return None
233
233
 
234
234
  # Get the auth type from the config if not provided
235
235
  if auth_type is None:
236
- if AUTH_TYPE not in config:
236
+ if AUTH_TYPE not in json_file:
237
237
  return None
238
- auth_type = config[AUTH_TYPE]
238
+ auth_type = json_file[AUTH_TYPE]
239
239
 
240
240
  # Retrieve auth plugin class and instantiate it
241
241
  try:
@@ -18,26 +18,31 @@
18
18
  from abc import ABC, abstractmethod
19
19
  from collections.abc import Sequence
20
20
  from pathlib import Path
21
- from typing import Any, Optional, Union
21
+ from typing import Optional, Union
22
22
 
23
23
  from flwr.proto.exec_pb2_grpc import ExecStub
24
24
 
25
+ from ..typing import UserAuthCredentials, UserAuthLoginDetails
26
+
25
27
 
26
28
  class ExecAuthPlugin(ABC):
27
29
  """Abstract Flower Auth Plugin class for ExecServicer.
28
30
 
29
31
  Parameters
30
32
  ----------
31
- config : dict[str, Any]
32
- The authentication configuration loaded from a YAML file.
33
+ user_auth_config_path : Path
34
+ Path to the YAML file containing the authentication configuration.
33
35
  """
34
36
 
35
37
  @abstractmethod
36
- def __init__(self, config: dict[str, Any]):
38
+ def __init__(
39
+ self,
40
+ user_auth_config_path: Path,
41
+ ):
37
42
  """Abstract constructor."""
38
43
 
39
44
  @abstractmethod
40
- def get_login_details(self) -> dict[str, str]:
45
+ def get_login_details(self) -> Optional[UserAuthLoginDetails]:
41
46
  """Get the login details."""
42
47
 
43
48
  @abstractmethod
@@ -47,7 +52,7 @@ class ExecAuthPlugin(ABC):
47
52
  """Validate authentication tokens in the provided metadata."""
48
53
 
49
54
  @abstractmethod
50
- def get_auth_tokens(self, auth_details: dict[str, str]) -> dict[str, str]:
55
+ def get_auth_tokens(self, device_code: str) -> Optional[UserAuthCredentials]:
51
56
  """Get authentication tokens."""
52
57
 
53
58
  @abstractmethod
@@ -62,50 +67,55 @@ class CliAuthPlugin(ABC):
62
67
 
63
68
  Parameters
64
69
  ----------
65
- user_auth_config_path : Path
66
- The path to the user's authentication configuration file.
70
+ credentials_path : Path
71
+ Path to the user's authentication credentials file.
67
72
  """
68
73
 
69
74
  @staticmethod
70
75
  @abstractmethod
71
76
  def login(
72
- login_details: dict[str, str],
77
+ login_details: UserAuthLoginDetails,
73
78
  exec_stub: ExecStub,
74
- ) -> dict[str, Any]:
75
- """Authenticate the user with the SuperLink.
79
+ ) -> UserAuthCredentials:
80
+ """Authenticate the user and retrieve authentication credentials.
76
81
 
77
82
  Parameters
78
83
  ----------
79
- login_details : dict[str, str]
80
- A dictionary containing the user's login details.
84
+ login_details : UserAuthLoginDetails
85
+ An object containing the user's login details.
81
86
  exec_stub : ExecStub
82
- An instance of `ExecStub` used for communication with the SuperLink.
87
+ A stub for executing RPC calls to the server.
83
88
 
84
89
  Returns
85
90
  -------
86
- user_auth_config : dict[str, Any]
87
- A dictionary containing the user's authentication configuration
88
- in JSON format.
91
+ UserAuthCredentials
92
+ The authentication credentials obtained after login.
89
93
  """
90
94
 
91
95
  @abstractmethod
92
- def __init__(self, user_auth_config_path: Path):
96
+ def __init__(self, credentials_path: Path):
93
97
  """Abstract constructor."""
94
98
 
95
99
  @abstractmethod
96
- def store_tokens(self, user_auth_config: dict[str, Any]) -> None:
97
- """Store authentication tokens from the provided user_auth_config.
100
+ def store_tokens(self, credentials: UserAuthCredentials) -> None:
101
+ """Store authentication tokens to the `credentials_path`.
98
102
 
99
- The configuration, including tokens, will be saved as a JSON file
100
- at `user_auth_config_path`.
103
+ The credentials, including tokens, will be saved as a JSON file
104
+ at `credentials_path`.
101
105
  """
102
106
 
103
107
  @abstractmethod
104
108
  def load_tokens(self) -> None:
105
- """Load authentication tokens from the user_auth_config_path."""
109
+ """Load authentication tokens from the `credentials_path`."""
106
110
 
107
111
  @abstractmethod
108
112
  def write_tokens_to_metadata(
109
113
  self, metadata: Sequence[tuple[str, Union[str, bytes]]]
110
114
  ) -> Sequence[tuple[str, Union[str, bytes]]]:
111
115
  """Write authentication tokens to the provided metadata."""
116
+
117
+ @abstractmethod
118
+ def read_tokens_from_metadata(
119
+ self, metadata: Sequence[tuple[str, Union[str, bytes]]]
120
+ ) -> Optional[UserAuthCredentials]:
121
+ """Read authentication tokens from the provided metadata."""
flwr/common/constant.py CHANGED
@@ -114,6 +114,8 @@ MAX_RETRY_DELAY = 20 # Maximum delay duration between two consecutive retries.
114
114
  # Constants for user authentication
115
115
  CREDENTIALS_DIR = ".credentials"
116
116
  AUTH_TYPE = "auth_type"
117
+ ACCESS_TOKEN_KEY = "access_token"
118
+ REFRESH_TOKEN_KEY = "refresh_token"
117
119
 
118
120
 
119
121
  class MessageType:
@@ -117,3 +117,48 @@ def verify_hmac(key: bytes, message: bytes, hmac_value: bytes) -> bool:
117
117
  return True
118
118
  except InvalidSignature:
119
119
  return False
120
+
121
+
122
+ def sign_message(private_key: ec.EllipticCurvePrivateKey, message: bytes) -> bytes:
123
+ """Sign a message using the provided EC private key.
124
+
125
+ Parameters
126
+ ----------
127
+ private_key : ec.EllipticCurvePrivateKey
128
+ The EC private key to sign the message with.
129
+ message : bytes
130
+ The message to be signed.
131
+
132
+ Returns
133
+ -------
134
+ bytes
135
+ The signature of the message.
136
+ """
137
+ signature = private_key.sign(message, ec.ECDSA(hashes.SHA256()))
138
+ return signature
139
+
140
+
141
+ def verify_signature(
142
+ public_key: ec.EllipticCurvePublicKey, message: bytes, signature: bytes
143
+ ) -> bool:
144
+ """Verify a signature against a message using the provided EC public key.
145
+
146
+ Parameters
147
+ ----------
148
+ public_key : ec.EllipticCurvePublicKey
149
+ The EC public key to verify the signature.
150
+ message : bytes
151
+ The original message.
152
+ signature : bytes
153
+ The signature to verify.
154
+
155
+ Returns
156
+ -------
157
+ bool
158
+ True if the signature is valid, False otherwise.
159
+ """
160
+ try:
161
+ public_key.verify(signature, message, ec.ECDSA(hashes.SHA256()))
162
+ return True
163
+ except InvalidSignature:
164
+ return False
flwr/common/typing.py CHANGED
@@ -266,3 +266,23 @@ class InvalidRunStatusException(BaseException):
266
266
  def __init__(self, message: str) -> None:
267
267
  super().__init__(message)
268
268
  self.message = message
269
+
270
+
271
+ # OIDC user authentication types
272
+ @dataclass
273
+ class UserAuthLoginDetails:
274
+ """User authentication login details."""
275
+
276
+ auth_type: str
277
+ device_code: str
278
+ verification_uri_complete: str
279
+ expires_in: int
280
+ interval: int
281
+
282
+
283
+ @dataclass
284
+ class UserAuthCredentials:
285
+ """User authentication tokens."""
286
+
287
+ access_token: str
288
+ refresh_token: str
flwr/proto/exec_pb2.py CHANGED
@@ -18,7 +18,7 @@ from flwr.proto import recordset_pb2 as flwr_dot_proto_dot_recordset__pb2
18
18
  from flwr.proto import run_pb2 as flwr_dot_proto_dot_run__pb2
19
19
 
20
20
 
21
- DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x66lwr/proto/exec.proto\x12\nflwr.proto\x1a\x14\x66lwr/proto/fab.proto\x1a\x1a\x66lwr/proto/transport.proto\x1a\x1a\x66lwr/proto/recordset.proto\x1a\x14\x66lwr/proto/run.proto\"\xfb\x01\n\x0fStartRunRequest\x12\x1c\n\x03\x66\x61\x62\x18\x01 \x01(\x0b\x32\x0f.flwr.proto.Fab\x12H\n\x0foverride_config\x18\x02 \x03(\x0b\x32/.flwr.proto.StartRunRequest.OverrideConfigEntry\x12\x35\n\x12\x66\x65\x64\x65ration_options\x18\x03 \x01(\x0b\x32\x19.flwr.proto.ConfigsRecord\x1aI\n\x13OverrideConfigEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12!\n\x05value\x18\x02 \x01(\x0b\x32\x12.flwr.proto.Scalar:\x02\x38\x01\"2\n\x10StartRunResponse\x12\x13\n\x06run_id\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\t\n\x07_run_id\"<\n\x11StreamLogsRequest\x12\x0e\n\x06run_id\x18\x01 \x01(\x04\x12\x17\n\x0f\x61\x66ter_timestamp\x18\x02 \x01(\x01\"B\n\x12StreamLogsResponse\x12\x12\n\nlog_output\x18\x01 \x01(\t\x12\x18\n\x10latest_timestamp\x18\x02 \x01(\x01\"1\n\x0fListRunsRequest\x12\x13\n\x06run_id\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\t\n\x07_run_id\"\x9d\x01\n\x10ListRunsResponse\x12;\n\x08run_dict\x18\x01 \x03(\x0b\x32).flwr.proto.ListRunsResponse.RunDictEntry\x12\x0b\n\x03now\x18\x02 \x01(\t\x1a?\n\x0cRunDictEntry\x12\x0b\n\x03key\x18\x01 \x01(\x04\x12\x1e\n\x05value\x18\x02 \x01(\x0b\x32\x0f.flwr.proto.Run:\x02\x38\x01\"\x18\n\x16GetLoginDetailsRequest\"\x9c\x01\n\x17GetLoginDetailsResponse\x12L\n\rlogin_details\x18\x01 \x03(\x0b\x32\x35.flwr.proto.GetLoginDetailsResponse.LoginDetailsEntry\x1a\x33\n\x11LoginDetailsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x93\x01\n\x14GetAuthTokensRequest\x12G\n\x0c\x61uth_details\x18\x01 \x03(\x0b\x32\x31.flwr.proto.GetAuthTokensRequest.AuthDetailsEntry\x1a\x32\n\x10\x41uthDetailsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x92\x01\n\x15GetAuthTokensResponse\x12\x46\n\x0b\x61uth_tokens\x18\x01 \x03(\x0b\x32\x31.flwr.proto.GetAuthTokensResponse.AuthTokensEntry\x1a\x31\n\x0f\x41uthTokensEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\" \n\x0eStopRunRequest\x12\x0e\n\x06run_id\x18\x01 \x01(\x04\"\"\n\x0fStopRunResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x32\xe5\x03\n\x04\x45xec\x12G\n\x08StartRun\x12\x1b.flwr.proto.StartRunRequest\x1a\x1c.flwr.proto.StartRunResponse\"\x00\x12\x44\n\x07StopRun\x12\x1a.flwr.proto.StopRunRequest\x1a\x1b.flwr.proto.StopRunResponse\"\x00\x12O\n\nStreamLogs\x12\x1d.flwr.proto.StreamLogsRequest\x1a\x1e.flwr.proto.StreamLogsResponse\"\x00\x30\x01\x12G\n\x08ListRuns\x12\x1b.flwr.proto.ListRunsRequest\x1a\x1c.flwr.proto.ListRunsResponse\"\x00\x12\\\n\x0fGetLoginDetails\x12\".flwr.proto.GetLoginDetailsRequest\x1a#.flwr.proto.GetLoginDetailsResponse\"\x00\x12V\n\rGetAuthTokens\x12 .flwr.proto.GetAuthTokensRequest\x1a!.flwr.proto.GetAuthTokensResponse\"\x00\x62\x06proto3')
21
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x66lwr/proto/exec.proto\x12\nflwr.proto\x1a\x14\x66lwr/proto/fab.proto\x1a\x1a\x66lwr/proto/transport.proto\x1a\x1a\x66lwr/proto/recordset.proto\x1a\x14\x66lwr/proto/run.proto\"\xfb\x01\n\x0fStartRunRequest\x12\x1c\n\x03\x66\x61\x62\x18\x01 \x01(\x0b\x32\x0f.flwr.proto.Fab\x12H\n\x0foverride_config\x18\x02 \x03(\x0b\x32/.flwr.proto.StartRunRequest.OverrideConfigEntry\x12\x35\n\x12\x66\x65\x64\x65ration_options\x18\x03 \x01(\x0b\x32\x19.flwr.proto.ConfigsRecord\x1aI\n\x13OverrideConfigEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12!\n\x05value\x18\x02 \x01(\x0b\x32\x12.flwr.proto.Scalar:\x02\x38\x01\"2\n\x10StartRunResponse\x12\x13\n\x06run_id\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\t\n\x07_run_id\"<\n\x11StreamLogsRequest\x12\x0e\n\x06run_id\x18\x01 \x01(\x04\x12\x17\n\x0f\x61\x66ter_timestamp\x18\x02 \x01(\x01\"B\n\x12StreamLogsResponse\x12\x12\n\nlog_output\x18\x01 \x01(\t\x12\x18\n\x10latest_timestamp\x18\x02 \x01(\x01\"1\n\x0fListRunsRequest\x12\x13\n\x06run_id\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\t\n\x07_run_id\"\x9d\x01\n\x10ListRunsResponse\x12;\n\x08run_dict\x18\x01 \x03(\x0b\x32).flwr.proto.ListRunsResponse.RunDictEntry\x12\x0b\n\x03now\x18\x02 \x01(\t\x1a?\n\x0cRunDictEntry\x12\x0b\n\x03key\x18\x01 \x01(\x04\x12\x1e\n\x05value\x18\x02 \x01(\x0b\x32\x0f.flwr.proto.Run:\x02\x38\x01\"\x18\n\x16GetLoginDetailsRequest\"\x8a\x01\n\x17GetLoginDetailsResponse\x12\x11\n\tauth_type\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65vice_code\x18\x02 \x01(\t\x12!\n\x19verification_uri_complete\x18\x03 \x01(\t\x12\x12\n\nexpires_in\x18\x04 \x01(\x03\x12\x10\n\x08interval\x18\x05 \x01(\x03\"+\n\x14GetAuthTokensRequest\x12\x13\n\x0b\x64\x65vice_code\x18\x01 \x01(\t\"D\n\x15GetAuthTokensResponse\x12\x14\n\x0c\x61\x63\x63\x65ss_token\x18\x01 \x01(\t\x12\x15\n\rrefresh_token\x18\x02 \x01(\t\" \n\x0eStopRunRequest\x12\x0e\n\x06run_id\x18\x01 \x01(\x04\"\"\n\x0fStopRunResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x32\xe5\x03\n\x04\x45xec\x12G\n\x08StartRun\x12\x1b.flwr.proto.StartRunRequest\x1a\x1c.flwr.proto.StartRunResponse\"\x00\x12\x44\n\x07StopRun\x12\x1a.flwr.proto.StopRunRequest\x1a\x1b.flwr.proto.StopRunResponse\"\x00\x12O\n\nStreamLogs\x12\x1d.flwr.proto.StreamLogsRequest\x1a\x1e.flwr.proto.StreamLogsResponse\"\x00\x30\x01\x12G\n\x08ListRuns\x12\x1b.flwr.proto.ListRunsRequest\x1a\x1c.flwr.proto.ListRunsResponse\"\x00\x12\\\n\x0fGetLoginDetails\x12\".flwr.proto.GetLoginDetailsRequest\x1a#.flwr.proto.GetLoginDetailsResponse\"\x00\x12V\n\rGetAuthTokens\x12 .flwr.proto.GetAuthTokensRequest\x1a!.flwr.proto.GetAuthTokensResponse\"\x00\x62\x06proto3')
22
22
 
23
23
  _globals = globals()
24
24
  _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
@@ -29,12 +29,6 @@ if _descriptor._USE_C_DESCRIPTORS == False:
29
29
  _globals['_STARTRUNREQUEST_OVERRIDECONFIGENTRY']._serialized_options = b'8\001'
30
30
  _globals['_LISTRUNSRESPONSE_RUNDICTENTRY']._options = None
31
31
  _globals['_LISTRUNSRESPONSE_RUNDICTENTRY']._serialized_options = b'8\001'
32
- _globals['_GETLOGINDETAILSRESPONSE_LOGINDETAILSENTRY']._options = None
33
- _globals['_GETLOGINDETAILSRESPONSE_LOGINDETAILSENTRY']._serialized_options = b'8\001'
34
- _globals['_GETAUTHTOKENSREQUEST_AUTHDETAILSENTRY']._options = None
35
- _globals['_GETAUTHTOKENSREQUEST_AUTHDETAILSENTRY']._serialized_options = b'8\001'
36
- _globals['_GETAUTHTOKENSRESPONSE_AUTHTOKENSENTRY']._options = None
37
- _globals['_GETAUTHTOKENSRESPONSE_AUTHTOKENSENTRY']._serialized_options = b'8\001'
38
32
  _globals['_STARTRUNREQUEST']._serialized_start=138
39
33
  _globals['_STARTRUNREQUEST']._serialized_end=389
40
34
  _globals['_STARTRUNREQUEST_OVERRIDECONFIGENTRY']._serialized_start=316
@@ -54,21 +48,15 @@ if _descriptor._USE_C_DESCRIPTORS == False:
54
48
  _globals['_GETLOGINDETAILSREQUEST']._serialized_start=784
55
49
  _globals['_GETLOGINDETAILSREQUEST']._serialized_end=808
56
50
  _globals['_GETLOGINDETAILSRESPONSE']._serialized_start=811
57
- _globals['_GETLOGINDETAILSRESPONSE']._serialized_end=967
58
- _globals['_GETLOGINDETAILSRESPONSE_LOGINDETAILSENTRY']._serialized_start=916
59
- _globals['_GETLOGINDETAILSRESPONSE_LOGINDETAILSENTRY']._serialized_end=967
60
- _globals['_GETAUTHTOKENSREQUEST']._serialized_start=970
61
- _globals['_GETAUTHTOKENSREQUEST']._serialized_end=1117
62
- _globals['_GETAUTHTOKENSREQUEST_AUTHDETAILSENTRY']._serialized_start=1067
63
- _globals['_GETAUTHTOKENSREQUEST_AUTHDETAILSENTRY']._serialized_end=1117
64
- _globals['_GETAUTHTOKENSRESPONSE']._serialized_start=1120
65
- _globals['_GETAUTHTOKENSRESPONSE']._serialized_end=1266
66
- _globals['_GETAUTHTOKENSRESPONSE_AUTHTOKENSENTRY']._serialized_start=1217
67
- _globals['_GETAUTHTOKENSRESPONSE_AUTHTOKENSENTRY']._serialized_end=1266
68
- _globals['_STOPRUNREQUEST']._serialized_start=1268
69
- _globals['_STOPRUNREQUEST']._serialized_end=1300
70
- _globals['_STOPRUNRESPONSE']._serialized_start=1302
71
- _globals['_STOPRUNRESPONSE']._serialized_end=1336
72
- _globals['_EXEC']._serialized_start=1339
73
- _globals['_EXEC']._serialized_end=1824
51
+ _globals['_GETLOGINDETAILSRESPONSE']._serialized_end=949
52
+ _globals['_GETAUTHTOKENSREQUEST']._serialized_start=951
53
+ _globals['_GETAUTHTOKENSREQUEST']._serialized_end=994
54
+ _globals['_GETAUTHTOKENSRESPONSE']._serialized_start=996
55
+ _globals['_GETAUTHTOKENSRESPONSE']._serialized_end=1064
56
+ _globals['_STOPRUNREQUEST']._serialized_start=1066
57
+ _globals['_STOPRUNREQUEST']._serialized_end=1098
58
+ _globals['_STOPRUNRESPONSE']._serialized_start=1100
59
+ _globals['_STOPRUNRESPONSE']._serialized_end=1134
60
+ _globals['_EXEC']._serialized_start=1137
61
+ _globals['_EXEC']._serialized_end=1622
74
62
  # @@protoc_insertion_point(module_scope)
flwr/proto/exec_pb2.pyi CHANGED
@@ -143,77 +143,50 @@ global___GetLoginDetailsRequest = GetLoginDetailsRequest
143
143
 
144
144
  class GetLoginDetailsResponse(google.protobuf.message.Message):
145
145
  DESCRIPTOR: google.protobuf.descriptor.Descriptor
146
- class LoginDetailsEntry(google.protobuf.message.Message):
147
- DESCRIPTOR: google.protobuf.descriptor.Descriptor
148
- KEY_FIELD_NUMBER: builtins.int
149
- VALUE_FIELD_NUMBER: builtins.int
150
- key: typing.Text
151
- value: typing.Text
152
- def __init__(self,
153
- *,
154
- key: typing.Text = ...,
155
- value: typing.Text = ...,
156
- ) -> None: ...
157
- def ClearField(self, field_name: typing_extensions.Literal["key",b"key","value",b"value"]) -> None: ...
158
-
159
- LOGIN_DETAILS_FIELD_NUMBER: builtins.int
160
- @property
161
- def login_details(self) -> google.protobuf.internal.containers.ScalarMap[typing.Text, typing.Text]: ...
146
+ AUTH_TYPE_FIELD_NUMBER: builtins.int
147
+ DEVICE_CODE_FIELD_NUMBER: builtins.int
148
+ VERIFICATION_URI_COMPLETE_FIELD_NUMBER: builtins.int
149
+ EXPIRES_IN_FIELD_NUMBER: builtins.int
150
+ INTERVAL_FIELD_NUMBER: builtins.int
151
+ auth_type: typing.Text
152
+ device_code: typing.Text
153
+ verification_uri_complete: typing.Text
154
+ expires_in: builtins.int
155
+ interval: builtins.int
162
156
  def __init__(self,
163
157
  *,
164
- login_details: typing.Optional[typing.Mapping[typing.Text, typing.Text]] = ...,
158
+ auth_type: typing.Text = ...,
159
+ device_code: typing.Text = ...,
160
+ verification_uri_complete: typing.Text = ...,
161
+ expires_in: builtins.int = ...,
162
+ interval: builtins.int = ...,
165
163
  ) -> None: ...
166
- def ClearField(self, field_name: typing_extensions.Literal["login_details",b"login_details"]) -> None: ...
164
+ def ClearField(self, field_name: typing_extensions.Literal["auth_type",b"auth_type","device_code",b"device_code","expires_in",b"expires_in","interval",b"interval","verification_uri_complete",b"verification_uri_complete"]) -> None: ...
167
165
  global___GetLoginDetailsResponse = GetLoginDetailsResponse
168
166
 
169
167
  class GetAuthTokensRequest(google.protobuf.message.Message):
170
168
  DESCRIPTOR: google.protobuf.descriptor.Descriptor
171
- class AuthDetailsEntry(google.protobuf.message.Message):
172
- DESCRIPTOR: google.protobuf.descriptor.Descriptor
173
- KEY_FIELD_NUMBER: builtins.int
174
- VALUE_FIELD_NUMBER: builtins.int
175
- key: typing.Text
176
- value: typing.Text
177
- def __init__(self,
178
- *,
179
- key: typing.Text = ...,
180
- value: typing.Text = ...,
181
- ) -> None: ...
182
- def ClearField(self, field_name: typing_extensions.Literal["key",b"key","value",b"value"]) -> None: ...
183
-
184
- AUTH_DETAILS_FIELD_NUMBER: builtins.int
185
- @property
186
- def auth_details(self) -> google.protobuf.internal.containers.ScalarMap[typing.Text, typing.Text]: ...
169
+ DEVICE_CODE_FIELD_NUMBER: builtins.int
170
+ device_code: typing.Text
187
171
  def __init__(self,
188
172
  *,
189
- auth_details: typing.Optional[typing.Mapping[typing.Text, typing.Text]] = ...,
173
+ device_code: typing.Text = ...,
190
174
  ) -> None: ...
191
- def ClearField(self, field_name: typing_extensions.Literal["auth_details",b"auth_details"]) -> None: ...
175
+ def ClearField(self, field_name: typing_extensions.Literal["device_code",b"device_code"]) -> None: ...
192
176
  global___GetAuthTokensRequest = GetAuthTokensRequest
193
177
 
194
178
  class GetAuthTokensResponse(google.protobuf.message.Message):
195
179
  DESCRIPTOR: google.protobuf.descriptor.Descriptor
196
- class AuthTokensEntry(google.protobuf.message.Message):
197
- DESCRIPTOR: google.protobuf.descriptor.Descriptor
198
- KEY_FIELD_NUMBER: builtins.int
199
- VALUE_FIELD_NUMBER: builtins.int
200
- key: typing.Text
201
- value: typing.Text
202
- def __init__(self,
203
- *,
204
- key: typing.Text = ...,
205
- value: typing.Text = ...,
206
- ) -> None: ...
207
- def ClearField(self, field_name: typing_extensions.Literal["key",b"key","value",b"value"]) -> None: ...
208
-
209
- AUTH_TOKENS_FIELD_NUMBER: builtins.int
210
- @property
211
- def auth_tokens(self) -> google.protobuf.internal.containers.ScalarMap[typing.Text, typing.Text]: ...
180
+ ACCESS_TOKEN_FIELD_NUMBER: builtins.int
181
+ REFRESH_TOKEN_FIELD_NUMBER: builtins.int
182
+ access_token: typing.Text
183
+ refresh_token: typing.Text
212
184
  def __init__(self,
213
185
  *,
214
- auth_tokens: typing.Optional[typing.Mapping[typing.Text, typing.Text]] = ...,
186
+ access_token: typing.Text = ...,
187
+ refresh_token: typing.Text = ...,
215
188
  ) -> None: ...
216
- def ClearField(self, field_name: typing_extensions.Literal["auth_tokens",b"auth_tokens"]) -> None: ...
189
+ def ClearField(self, field_name: typing_extensions.Literal["access_token",b"access_token","refresh_token",b"refresh_token"]) -> None: ...
217
190
  global___GetAuthTokensResponse = GetAuthTokensResponse
218
191
 
219
192
  class StopRunRequest(google.protobuf.message.Message):
flwr/server/app.py CHANGED
@@ -263,11 +263,10 @@ def run_superlink() -> None:
263
263
  # Obtain certificates
264
264
  certificates = try_obtain_server_certificates(args, args.fleet_api_type)
265
265
 
266
- user_auth_config = _try_obtain_user_auth_config(args)
267
266
  auth_plugin: Optional[ExecAuthPlugin] = None
268
- # user_auth_config is None only if the args.user_auth_config is not provided
269
- if user_auth_config is not None:
270
- auth_plugin = _try_obtain_exec_auth_plugin(user_auth_config)
267
+ # Load the auth plugin if the args.user_auth_config is provided
268
+ if cfg_path := getattr(args, "user_auth_config", None):
269
+ auth_plugin = _try_obtain_exec_auth_plugin(Path(cfg_path))
271
270
 
272
271
  # Initialize StateFactory
273
272
  state_factory = LinkStateFactory(args.database)
@@ -584,21 +583,20 @@ def _try_setup_node_authentication(
584
583
  )
585
584
 
586
585
 
587
- def _try_obtain_user_auth_config(args: argparse.Namespace) -> Optional[dict[str, Any]]:
588
- if getattr(args, "user_auth_config", None) is not None:
589
- with open(args.user_auth_config, encoding="utf-8") as file:
590
- config: dict[str, Any] = yaml.safe_load(file)
591
- return config
592
- return None
586
+ def _try_obtain_exec_auth_plugin(config_path: Path) -> Optional[ExecAuthPlugin]:
587
+ # Load YAML file
588
+ with config_path.open("r", encoding="utf-8") as file:
589
+ config: dict[str, Any] = yaml.safe_load(file)
593
590
 
594
-
595
- def _try_obtain_exec_auth_plugin(config: dict[str, Any]) -> Optional[ExecAuthPlugin]:
591
+ # Load authentication configuration
596
592
  auth_config: dict[str, Any] = config.get("authentication", {})
597
593
  auth_type: str = auth_config.get(AUTH_TYPE, "")
594
+
595
+ # Load authentication plugin
598
596
  try:
599
597
  all_plugins: dict[str, type[ExecAuthPlugin]] = get_exec_auth_plugins()
600
598
  auth_plugin_class = all_plugins[auth_type]
601
- return auth_plugin_class(config=auth_config)
599
+ return auth_plugin_class(user_auth_config_path=config_path)
602
600
  except KeyError:
603
601
  if auth_type != "":
604
602
  sys.exit(
@@ -982,17 +982,16 @@ class SqliteLinkState(LinkState): # pylint: disable=R0904
982
982
  """Acknowledge a ping received from a node, serving as a heartbeat."""
983
983
  sint64_node_id = convert_uint64_to_sint64(node_id)
984
984
 
985
- # Update `online_until` and `ping_interval` for the given `node_id`
986
- query = "UPDATE node SET online_until = ?, ping_interval = ? WHERE node_id = ?;"
987
- try:
988
- self.query(
989
- query, (time.time() + ping_interval, ping_interval, sint64_node_id)
990
- )
991
- return True
992
- except sqlite3.IntegrityError:
993
- log(ERROR, "`node_id` does not exist.")
985
+ # Check if the node exists in the `node` table
986
+ query = "SELECT 1 FROM node WHERE node_id = ?"
987
+ if not self.query(query, (sint64_node_id,)):
994
988
  return False
995
989
 
990
+ # Update `online_until` and `ping_interval` for the given `node_id`
991
+ query = "UPDATE node SET online_until = ?, ping_interval = ? WHERE node_id = ?"
992
+ self.query(query, (time.time() + ping_interval, ping_interval, sint64_node_id))
993
+ return True
994
+
996
995
  def get_serverapp_context(self, run_id: int) -> Optional[Context]:
997
996
  """Get the context for the specified `run_id`."""
998
997
  # Retrieve context if any
@@ -181,8 +181,20 @@ class ExecServicer(exec_pb2_grpc.ExecServicer):
181
181
  "ExecServicer initialized without user authentication",
182
182
  )
183
183
  raise grpc.RpcError() # This line is unreachable
184
+
185
+ # Get login details
186
+ details = self.auth_plugin.get_login_details()
187
+
188
+ # Return empty response if details is None
189
+ if details is None:
190
+ return GetLoginDetailsResponse()
191
+
184
192
  return GetLoginDetailsResponse(
185
- login_details=self.auth_plugin.get_login_details()
193
+ auth_type=details.auth_type,
194
+ device_code=details.device_code,
195
+ verification_uri_complete=details.verification_uri_complete,
196
+ expires_in=details.expires_in,
197
+ interval=details.interval,
186
198
  )
187
199
 
188
200
  def GetAuthTokens(
@@ -196,8 +208,17 @@ class ExecServicer(exec_pb2_grpc.ExecServicer):
196
208
  "ExecServicer initialized without user authentication",
197
209
  )
198
210
  raise grpc.RpcError() # This line is unreachable
211
+
212
+ # Get auth tokens
213
+ credentials = self.auth_plugin.get_auth_tokens(request.device_code)
214
+
215
+ # Return empty response if credentials is None
216
+ if credentials is None:
217
+ return GetAuthTokensResponse()
218
+
199
219
  return GetAuthTokensResponse(
200
- auth_tokens=self.auth_plugin.get_auth_tokens(dict(request.auth_details))
220
+ access_token=credentials.access_token,
221
+ refresh_token=credentials.refresh_token,
201
222
  )
202
223
 
203
224
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: flwr-nightly
3
- Version: 1.15.0.dev20250106
3
+ Version: 1.15.0.dev20250108
4
4
  Summary: Flower: A Friendly Federated AI Framework
5
5
  Home-page: https://flower.ai
6
6
  License: Apache-2.0
@@ -43,11 +43,11 @@ Requires-Dist: pyyaml (>=6.0.2,<7.0.0)
43
43
  Requires-Dist: ray (==2.10.0) ; (python_version >= "3.9" and python_version < "3.12") and (extra == "simulation")
44
44
  Requires-Dist: requests (>=2.31.0,<3.0.0)
45
45
  Requires-Dist: rich (>=13.5.0,<14.0.0)
46
- Requires-Dist: starlette (>=0.31.0,<0.32.0) ; extra == "rest"
46
+ Requires-Dist: starlette (>=0.45.2,<0.46.0) ; extra == "rest"
47
47
  Requires-Dist: tomli (>=2.0.1,<3.0.0)
48
48
  Requires-Dist: tomli-w (>=1.0.0,<2.0.0)
49
49
  Requires-Dist: typer (>=0.12.5,<0.13.0)
50
- Requires-Dist: uvicorn[standard] (>=0.23.0,<0.24.0) ; extra == "rest"
50
+ Requires-Dist: uvicorn[standard] (>=0.34.0,<0.35.0) ; extra == "rest"
51
51
  Project-URL: Documentation, https://flower.ai
52
52
  Project-URL: Repository, https://github.com/adap/flower
53
53
  Description-Content-Type: text/markdown
@@ -2,13 +2,13 @@ flwr/__init__.py,sha256=VmBWedrCxqmt4QvUHBLqyVEH6p7zaFMD_oCHerXHSVw,937
2
2
  flwr/cli/__init__.py,sha256=cZJVgozlkC6Ni2Hd_FAIrqefrkCGOV18fikToq-6iLw,720
3
3
  flwr/cli/app.py,sha256=UeXrW5gxrUnFViDjAMIxGNZZKwu3a1oAj83v53IWIWM,1382
4
4
  flwr/cli/build.py,sha256=4P70i_FnUs0P21aTwjTXtFQSAfY-C04hUDF-2npfJdo,6345
5
- flwr/cli/cli_user_auth_interceptor.py,sha256=rEjgAZmzHO0GjwdyZib6bkTI2X59ErJAZlutqpqZGF0,2952
5
+ flwr/cli/cli_user_auth_interceptor.py,sha256=aZepPA298s-HjGmkJGMvI_uZe72O5aLC3jri-ilG53o,3126
6
6
  flwr/cli/config_utils.py,sha256=I4_EMv2f68mfrL_QuOYoAG--yDfKisE7tGiIg09G2YQ,12079
7
7
  flwr/cli/example.py,sha256=uk5CoD0ZITgpY_ffsTbEKf8XOOCSUzByjHPcMSPqV18,2216
8
8
  flwr/cli/install.py,sha256=0AD0qJD79SKgBnWOQlphcubfr4zHk8jTpFgwZbJBI_g,8180
9
9
  flwr/cli/log.py,sha256=O7MBpsJp114PIZb-7Cru-KM6fqyneFQkqoQbQsqQmZU,6121
10
10
  flwr/cli/login/__init__.py,sha256=6_9zOzbPOAH72K2wX3-9dXTAbS7Mjpa5sEn2lA6eHHI,800
11
- flwr/cli/login/login.py,sha256=bZZ3hVeGpF5805R0Eg_SBZUGwrLAWmyaoLhLw6vQFcg,2764
11
+ flwr/cli/login/login.py,sha256=VaBPQBdLYmSfxXEJWVyu8U5dXztQgIv6rfTJkvz3zV8,3025
12
12
  flwr/cli/ls.py,sha256=K_3Bt2RfETw4V7J4qgo8_Wx-Y_bWZqttuO879Ppxo5Y,11056
13
13
  flwr/cli/new/__init__.py,sha256=pOQtPT9W4kCIttcKne5m-FtJbvTqdjTVJxzQ9AUYK8I,790
14
14
  flwr/cli/new/new.py,sha256=scyyKt8mzkc3El1bypgkHjKwVQEc2-q4I50PxriPFdI,9922
@@ -67,7 +67,7 @@ flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl,sha256=r0SZnvoR5a5mEWKJ
67
67
  flwr/cli/run/__init__.py,sha256=cCsKVB0SFzh2b3QmGba6BHckB85xlhjh3mh4pBpACtY,790
68
68
  flwr/cli/run/run.py,sha256=BvpjYyUvDhVMvO5cG711ihtdeSbls9p8zVAuFGETLA8,7893
69
69
  flwr/cli/stop.py,sha256=1T9RNRCH8dxjmBT38hFtKAWY9Gb7RMCMCML7kex9WzE,4613
70
- flwr/cli/utils.py,sha256=d15VkwxVK-NC1X-LVXXW3_O4-A38ZrlevwSNKNYpJCY,10592
70
+ flwr/cli/utils.py,sha256=RXozds-T7HN8yH0mMj67q-0YktdNNm83N9Ptu_pdhsc,10604
71
71
  flwr/client/__init__.py,sha256=DGDoO0AEAfz-0CUFmLdyUUweAS64-07AOnmDfWUefK4,1192
72
72
  flwr/client/app.py,sha256=XJWu-kPswM52oLYXaOLKr0gj87KPNRI7M0Na9oBsDK4,34784
73
73
  flwr/client/client.py,sha256=8o58nd9o6ZFcMIaVYPGcV4MSjBG4H0oFgWiv8ZEO3oA,7895
@@ -112,9 +112,9 @@ flwr/common/__init__.py,sha256=TVaoFEJE158aui1TPZQiJCDZX4RNHRyI8I55VC80HhI,3901
112
112
  flwr/common/address.py,sha256=9KNYE69WW_QVcyumsux3Qn1wmn4J7f13Y9nHASpvzbA,3018
113
113
  flwr/common/args.py,sha256=bCvtG0hhh_hVjl9NoWsY_g7kLMIN3jCN7B883HvZ7hg,6223
114
114
  flwr/common/auth_plugin/__init__.py,sha256=1Y8Oj3iB49IHDu9tvDih1J74Ygu7k85V9s2A4WORPyA,887
115
- flwr/common/auth_plugin/auth_plugin.py,sha256=6WEAVVPrS7LgSBpd4WyHYU4EsajT2nBGI_IN3mhYzoU,3567
115
+ flwr/common/auth_plugin/auth_plugin.py,sha256=p_3oDuKiHtMMLLCyEe_fuRi_g5GwRKb4HJCnBkVK74I,3840
116
116
  flwr/common/config.py,sha256=vmPwtRu7JIoGCke03pJlsyrA6zTlN43flzQx-4AX1mE,8099
117
- flwr/common/constant.py,sha256=9HwFVxFWbLTzMetIffUT3gAC9nPtqzBNxrKWr5A0oSI,5996
117
+ flwr/common/constant.py,sha256=0H9SBSnQ78ZK4KPIKCIxTQZps2jU_pea8KMX8Hw22bQ,6066
118
118
  flwr/common/context.py,sha256=uJ-mnoC_8y_udEb3kAX-r8CPphNTWM72z1AlsvQEu54,2403
119
119
  flwr/common/date.py,sha256=NHHpESce5wYqEwoDXf09gp9U9l_5Bmlh2BsOcwS-kDM,1554
120
120
  flwr/common/differential_privacy.py,sha256=XwcJ3rWr8S8BZUocc76vLSJAXIf6OHnWkBV6-xlIRuw,6106
@@ -139,14 +139,14 @@ flwr/common/retry_invoker.py,sha256=UIDKsn0AitS3fOr43WTqZAdD-TaHkBeTj1QxD7SGba0,
139
139
  flwr/common/secure_aggregation/__init__.py,sha256=erPnTWdOfMH0K0HQTmj5foDJ6t3iYcExy2aACy8iZNQ,731
140
140
  flwr/common/secure_aggregation/crypto/__init__.py,sha256=nlHesCWy8xxE5s6qHWnauCtyClcMQ2K0CEXAHakY5n0,738
141
141
  flwr/common/secure_aggregation/crypto/shamir.py,sha256=wCSfEfeaPgJ9Om580-YPUF2ljiyRhq33TRC4HtwxYl8,2779
142
- flwr/common/secure_aggregation/crypto/symmetric_encryption.py,sha256=wTDbOaMGZwT0nR4cAPZ0XrGulb8sdtF-Li_NcJ-c464,4166
142
+ flwr/common/secure_aggregation/crypto/symmetric_encryption.py,sha256=J_pRkxbogc7e1fxRZStZFBdzzG5jeUycshJPpvyCt6g,5333
143
143
  flwr/common/secure_aggregation/ndarrays_arithmetic.py,sha256=zvVAIrIyI6OSzGhpCi8NNaTvPXmoMYQIPJT-NkBg8RU,3013
144
144
  flwr/common/secure_aggregation/quantization.py,sha256=mC4uLf05zeONo8Ke-BY0Tj8UCMOS7VD93zHCzuv3MHU,2304
145
145
  flwr/common/secure_aggregation/secaggplus_constants.py,sha256=9MF-oQh62uD7rt9VeNB-rHf2gBLd5GL3S9OejCxmILY,2183
146
146
  flwr/common/secure_aggregation/secaggplus_utils.py,sha256=OgYd68YBRaHQYLc-YdExj9CSpwL58bVTaPrdHoAj2AE,3214
147
147
  flwr/common/serde.py,sha256=K9ExsqcTPETESkt2HMaNtIQAIAfwmuwtJFlG-59I7Sw,31046
148
148
  flwr/common/telemetry.py,sha256=APKVubU_zJNrE-M_rip6S6Fsu41DxY3tAjFWNOgTmC0,9086
149
- flwr/common/typing.py,sha256=eTlGl56rdus583r11xHY_ejG-3b3rknqF735UY5zMK8,6025
149
+ flwr/common/typing.py,sha256=IMgs_7nDJWn8Eb7y16Hl4HC3imQV7_Hdla1IFs3B5u8,6382
150
150
  flwr/common/version.py,sha256=aNSxLL49RKeLz8sPcZrsTEWtrAeQ0uxu6tjmfba4O60,1325
151
151
  flwr/proto/__init__.py,sha256=hbY7JYakwZwCkYgCNlmHdc8rtvfoJbAZLalMdc--CGc,683
152
152
  flwr/proto/clientappio_pb2.py,sha256=Y3PMv-JMaBGehpslgbvGY6l2u5vNpfCTFWu-fmAmBJ4,3703
@@ -157,8 +157,8 @@ flwr/proto/error_pb2.py,sha256=LarjKL90LbwkXKlhzNrDssgl4DXcvIPve8NVCXHpsKA,1084
157
157
  flwr/proto/error_pb2.pyi,sha256=ZNH4HhJTU_KfMXlyCeg8FwU-fcUYxTqEmoJPtWtHikc,734
158
158
  flwr/proto/error_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159
159
159
  flwr/proto/error_pb2_grpc.pyi,sha256=ff2TSiLVnG6IVQcTGzb2DIH3XRSoAvAo_RMcvbMFyc0,76
160
- flwr/proto/exec_pb2.py,sha256=IVqmpzzThSjuLBCF8T9VofTpnUXtp3SYWOEp8dzyv5o,6883
161
- flwr/proto/exec_pb2.pyi,sha256=amt-3e3zJVjkRlQ8Gz6m1A7hXyeZmbQhHpAEIQyIDn0,10660
160
+ flwr/proto/exec_pb2.py,sha256=1a8nh4RyDpRLY2gC6vMSAEe6-ksj7hLonAr-pZAejqc,5664
161
+ flwr/proto/exec_pb2.pyi,sha256=2vbzq2OOYM5GI-Tug4eVM5J6xvNvSFtR8m3MsP-i-Q4,9477
162
162
  flwr/proto/exec_pb2_grpc.py,sha256=-bdLqjsqQxK9R8LIiZaKlLKH2NmjR50EaGKTPPTwFhI,10445
163
163
  flwr/proto/exec_pb2_grpc.pyi,sha256=M5k-FzeLWxal7zt28LJfzMWWRxmNknTC2BzHRRMa1sQ,2914
164
164
  flwr/proto/fab_pb2.py,sha256=-gfW_ePYHx1vDGHfimwn91qEhmmY_gslaOHwqqZnVdU,1627
@@ -211,7 +211,7 @@ flwr/proto/transport_pb2_grpc.py,sha256=vLN3EHtx2aEEMCO4f1Upu-l27BPzd3-5pV-u8wPc
211
211
  flwr/proto/transport_pb2_grpc.pyi,sha256=AGXf8RiIiW2J5IKMlm_3qT3AzcDa4F3P5IqUjve_esA,766
212
212
  flwr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
213
213
  flwr/server/__init__.py,sha256=cEg1oecBu4cKB69iJCqWEylC8b5XW47bl7rQiJsdTvM,1528
214
- flwr/server/app.py,sha256=qn5gdsDqvJ7o4ZiWpbNBHtYKSZiUJ7SiGfdkUEczTd4,31010
214
+ flwr/server/app.py,sha256=xXiRsHXypzMW0EI1ewiiGE0dk6296I9d95ae1d0MKYU,30860
215
215
  flwr/server/client_manager.py,sha256=7Ese0tgrH-i-ms363feYZJKwB8gWnXSmg_hYF2Bju4U,6227
216
216
  flwr/server/client_proxy.py,sha256=4G-oTwhb45sfWLx2uZdcXD98IZwdTS6F88xe3akCdUg,2399
217
217
  flwr/server/compat/__init__.py,sha256=VxnJtJyOjNFQXMNi9hIuzNlZM5n0Hj1p3aq_Pm2udw4,892
@@ -288,7 +288,7 @@ flwr/server/superlink/linkstate/__init__.py,sha256=v-2JyJlCB3qyhMNwMjmcNVOq4rkoo
288
288
  flwr/server/superlink/linkstate/in_memory_linkstate.py,sha256=SR6a0wb-xFVQmPEsNseT91jmBeN0Lh1m_lZ6nY6aNS0,21984
289
289
  flwr/server/superlink/linkstate/linkstate.py,sha256=ayQY5eb-2InSldQUnVcx5ABjPU4QhQRV2lEVPVM_818,13114
290
290
  flwr/server/superlink/linkstate/linkstate_factory.py,sha256=ISSMjDlwuN7swxjOeYlTNpI_kuZ8PGkMcJnf1dbhUSE,2069
291
- flwr/server/superlink/linkstate/sqlite_linkstate.py,sha256=svP5Vj6Jaq1iazd6q0wWQU0lJslfIJT0XBw8mA1SABs,42879
291
+ flwr/server/superlink/linkstate/sqlite_linkstate.py,sha256=yellTrzUics7A8Y5YKRVVLRMDMMGhudeE-s4dM1ONkY,42900
292
292
  flwr/server/superlink/linkstate/utils.py,sha256=d5uqqIOCKfd54X8CFNfUr3AWqPLpgmzsC_RagRwFugM,13321
293
293
  flwr/server/superlink/simulation/__init__.py,sha256=mg-oapC9dkzEfjXPQFior5lpWj4g9kwbLovptyYM_g0,718
294
294
  flwr/server/superlink/simulation/simulationio_grpc.py,sha256=5wflYW_TS0mjmPG6OYuHMJwXD2_cYmUNhFkdOU0jMWQ,2237
@@ -317,12 +317,12 @@ flwr/superexec/__init__.py,sha256=fcj366jh4RFby_vDwLroU4kepzqbnJgseZD_jUr_Mko,71
317
317
  flwr/superexec/app.py,sha256=Z6kYHWd62YL0Q4YKyCAbt_BcefNfbKH6V-jCC-1NkZM,1842
318
318
  flwr/superexec/deployment.py,sha256=wZ9G42gGS91knfplswh95MnQ83Fzu-rs6wcuNgDmmvY,6735
319
319
  flwr/superexec/exec_grpc.py,sha256=XD94kqzigQ9tLB3hU-jVgMAS_BmlK80Z5rQ_9M6b7aY,2897
320
- flwr/superexec/exec_servicer.py,sha256=8tFwj1fDBF6PzwLhByTlxM-KNZc83bG1UdE92-8DSFk,7699
320
+ flwr/superexec/exec_servicer.py,sha256=X10ILT-AoGMrB3IgI2mBe9i-QcIVUAl9bucuqVOPYkU,8341
321
321
  flwr/superexec/exec_user_auth_interceptor.py,sha256=K06OU-l4LnYhTDg071hGJuOaQWEJbZsYi5qxUmmtiG0,3704
322
322
  flwr/superexec/executor.py,sha256=_B55WW2TD1fBINpabSSDRenVHXYmvlfhv-k8hJKU4lQ,3115
323
323
  flwr/superexec/simulation.py,sha256=WQDon15oqpMopAZnwRZoTICYCfHqtkvFSqiTQ2hLD_g,4088
324
- flwr_nightly-1.15.0.dev20250106.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
325
- flwr_nightly-1.15.0.dev20250106.dist-info/METADATA,sha256=YEPdnt0TMXMchBYGYlwwdCYFTvx5Dwv6ssGkSD94r6E,15810
326
- flwr_nightly-1.15.0.dev20250106.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
327
- flwr_nightly-1.15.0.dev20250106.dist-info/entry_points.txt,sha256=JlNxX3qhaV18_2yj5a3kJW1ESxm31cal9iS_N_pf1Rk,538
328
- flwr_nightly-1.15.0.dev20250106.dist-info/RECORD,,
324
+ flwr_nightly-1.15.0.dev20250108.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
325
+ flwr_nightly-1.15.0.dev20250108.dist-info/METADATA,sha256=N34sl2KG1HTyopOraXZOl5iqeqjVlL_eOFOMfJGjWu4,15810
326
+ flwr_nightly-1.15.0.dev20250108.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
327
+ flwr_nightly-1.15.0.dev20250108.dist-info/entry_points.txt,sha256=JlNxX3qhaV18_2yj5a3kJW1ESxm31cal9iS_N_pf1Rk,538
328
+ flwr_nightly-1.15.0.dev20250108.dist-info/RECORD,,