flwr-nightly 1.23.0.dev20250930__py3-none-any.whl → 1.26.0.dev20260121__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 +17 -6
- flwr/app/__init__.py +4 -1
- flwr/app/error.py +2 -2
- flwr/app/exception.py +3 -3
- flwr/app/message_type.py +29 -0
- flwr/app/metadata.py +5 -2
- flwr/app/user_config.py +19 -0
- flwr/cli/app.py +62 -9
- flwr/cli/{new/templates/app/code → app_cmd}/__init__.py +9 -1
- flwr/cli/app_cmd/publish.py +285 -0
- flwr/cli/app_cmd/review.py +262 -0
- flwr/cli/auth_plugin/__init__.py +13 -6
- flwr/cli/auth_plugin/auth_plugin.py +26 -15
- flwr/cli/auth_plugin/noop_auth_plugin.py +101 -0
- flwr/cli/auth_plugin/oidc_cli_plugin.py +52 -32
- flwr/cli/build.py +166 -53
- flwr/cli/{cli_user_auth_interceptor.py → cli_account_auth_interceptor.py} +27 -10
- flwr/cli/config/__init__.py +21 -0
- flwr/cli/config/ls.py +104 -0
- flwr/cli/config_migration.py +300 -0
- flwr/cli/config_utils.py +154 -13
- flwr/cli/constant.py +67 -0
- flwr/cli/{new/templates/app/code/flwr_tune → federation}/__init__.py +8 -1
- flwr/cli/federation/ls.py +361 -0
- flwr/cli/flower_config.py +447 -0
- flwr/cli/install.py +91 -13
- flwr/cli/log.py +65 -36
- flwr/cli/login/login.py +41 -27
- flwr/cli/ls.py +232 -158
- flwr/cli/new/new.py +188 -244
- flwr/cli/pull.py +25 -34
- flwr/cli/run/run.py +106 -74
- flwr/cli/run_utils.py +148 -0
- flwr/cli/stop.py +46 -37
- flwr/cli/supernode/__init__.py +25 -0
- flwr/cli/supernode/ls.py +273 -0
- flwr/cli/supernode/register.py +190 -0
- flwr/cli/supernode/unregister.py +140 -0
- flwr/cli/typing.py +211 -0
- flwr/cli/utils.py +428 -80
- flwr/client/__init__.py +2 -1
- flwr/client/dpfedavg_numpy_client.py +4 -1
- flwr/client/grpc_adapter_client/connection.py +14 -17
- flwr/client/grpc_rere_client/connection.py +73 -43
- flwr/client/grpc_rere_client/grpc_adapter.py +35 -15
- flwr/client/grpc_rere_client/{client_interceptor.py → node_auth_client_interceptor.py} +5 -7
- flwr/client/message_handler/message_handler.py +4 -3
- flwr/client/mod/centraldp_mods.py +1 -1
- flwr/client/mod/localdp_mod.py +1 -1
- flwr/client/mod/secure_aggregation/secaggplus_mod.py +11 -9
- flwr/client/numpy_client.py +1 -1
- flwr/client/rest_client/connection.py +99 -54
- flwr/client/run_info_store.py +6 -6
- flwr/client/typing.py +1 -1
- flwr/clientapp/__init__.py +1 -2
- flwr/{client → clientapp}/client_app.py +11 -11
- 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/__init__.py +3 -2
- flwr/common/args.py +3 -4
- flwr/common/config.py +15 -17
- flwr/common/constant.py +56 -28
- flwr/common/context.py +2 -1
- flwr/common/differential_privacy.py +3 -4
- flwr/common/event_log_plugin/event_log_plugin.py +3 -4
- flwr/common/exit/exit.py +16 -3
- 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 +8 -7
- 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 +5 -5
- 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 +8 -5
- flwr/common/serde.py +22 -11
- flwr/common/serde_utils.py +2 -2
- flwr/common/telemetry.py +10 -6
- flwr/common/typing.py +65 -44
- flwr/compat/client/app.py +45 -47
- flwr/compat/client/grpc_client/connection.py +15 -14
- flwr/compat/common/constant.py +29 -0
- flwr/compat/server/app.py +6 -7
- 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 +190 -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 +158 -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 +175 -128
- 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 +12 -13
- 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 +12 -10
- 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 +20 -9
- 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 -50
- flwr/server/superlink/fleet/message_handler/message_handler.py +141 -51
- 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/__init__.py +2 -0
- flwr/server/superlink/linkstate/in_memory_linkstate.py +293 -208
- flwr/server/superlink/linkstate/linkstate.py +176 -64
- flwr/server/superlink/linkstate/linkstate_factory.py +24 -6
- flwr/server/superlink/linkstate/sql_linkstate.py +221 -0
- flwr/server/superlink/linkstate/sqlite_linkstate.py +743 -648
- flwr/server/superlink/linkstate/utils.py +11 -62
- 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 +7 -6
- flwr/server/workflow/secure_aggregation/secagg_workflow.py +2 -4
- flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +13 -11
- 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 +10 -11
- 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 +10 -10
- 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 +14 -19
- flwr/simulation/run_simulation.py +46 -44
- flwr/simulation/simulationio_connection.py +4 -4
- flwr/{common → supercore}/address.py +1 -37
- flwr/supercore/cli/flower_superexec.py +3 -4
- flwr/supercore/constant.py +69 -0
- flwr/supercore/corestate/corestate.py +24 -3
- flwr/supercore/corestate/in_memory_corestate.py +138 -0
- flwr/supercore/corestate/sql_corestate.py +153 -0
- flwr/supercore/corestate/sqlite_corestate.py +157 -0
- flwr/supercore/credential_store/__init__.py +33 -0
- flwr/supercore/credential_store/credential_store.py +34 -0
- flwr/supercore/credential_store/file_credential_store.py +76 -0
- flwr/{common → supercore}/date.py +0 -11
- 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/{cli/new/templates/app → supercore/primitives}/__init__.py +1 -1
- flwr/supercore/primitives/asymmetric.py +117 -0
- flwr/supercore/primitives/asymmetric_ed25519.py +175 -0
- flwr/supercore/sql_mixin.py +292 -0
- flwr/supercore/sqlite_mixin.py +156 -0
- flwr/{client/clientapp → supercore/state}/__init__.py +2 -2
- flwr/supercore/state/schema/README.md +125 -0
- flwr/{cli/new/templates → supercore/state/schema}/__init__.py +2 -2
- flwr/supercore/state/schema/corestate_tables.py +36 -0
- flwr/supercore/state/schema/linkstate_tables.py +152 -0
- flwr/supercore/state/schema/objectstore_tables.py +90 -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 +224 -0
- flwr/superlink/artifact_provider/artifact_provider.py +1 -2
- flwr/superlink/auth_plugin/__init__.py +5 -2
- flwr/superlink/auth_plugin/auth_plugin.py +20 -19
- 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 +20 -17
- flwr/superlink/servicer/control/control_license_interceptor.py +3 -3
- flwr/superlink/servicer/control/control_servicer.py +328 -68
- flwr/supernode/cli/flower_supernode.py +74 -26
- flwr/supernode/nodestate/in_memory_nodestate.py +121 -49
- flwr/supernode/nodestate/nodestate.py +52 -8
- flwr/supernode/nodestate/nodestate_factory.py +7 -4
- flwr/supernode/runtime/run_clientapp.py +43 -24
- flwr/supernode/servicer/clientappio/clientappio_servicer.py +48 -10
- flwr/supernode/start_client_internal.py +185 -57
- {flwr_nightly-1.23.0.dev20250930.dist-info → flwr_nightly-1.26.0.dev20260121.dist-info}/METADATA +10 -11
- flwr_nightly-1.26.0.dev20260121.dist-info/RECORD +411 -0
- flwr/cli/new/templates/app/.gitignore.tpl +0 -163
- flwr/cli/new/templates/app/LICENSE.tpl +0 -202
- flwr/cli/new/templates/app/README.baseline.md.tpl +0 -127
- flwr/cli/new/templates/app/README.flowertune.md.tpl +0 -68
- flwr/cli/new/templates/app/README.md.tpl +0 -37
- flwr/cli/new/templates/app/code/__init__.baseline.py.tpl +0 -1
- flwr/cli/new/templates/app/code/__init__.py.tpl +0 -1
- flwr/cli/new/templates/app/code/__init__.pytorch_legacy_api.py.tpl +0 -1
- flwr/cli/new/templates/app/code/client.baseline.py.tpl +0 -75
- flwr/cli/new/templates/app/code/client.huggingface.py.tpl +0 -93
- flwr/cli/new/templates/app/code/client.jax.py.tpl +0 -71
- flwr/cli/new/templates/app/code/client.mlx.py.tpl +0 -102
- flwr/cli/new/templates/app/code/client.numpy.py.tpl +0 -46
- flwr/cli/new/templates/app/code/client.pytorch.py.tpl +0 -80
- flwr/cli/new/templates/app/code/client.pytorch_legacy_api.py.tpl +0 -55
- flwr/cli/new/templates/app/code/client.sklearn.py.tpl +0 -108
- flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +0 -82
- flwr/cli/new/templates/app/code/client.xgboost.py.tpl +0 -110
- flwr/cli/new/templates/app/code/dataset.baseline.py.tpl +0 -36
- flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl +0 -92
- flwr/cli/new/templates/app/code/flwr_tune/dataset.py.tpl +0 -87
- flwr/cli/new/templates/app/code/flwr_tune/models.py.tpl +0 -56
- flwr/cli/new/templates/app/code/flwr_tune/server_app.py.tpl +0 -73
- flwr/cli/new/templates/app/code/flwr_tune/strategy.py.tpl +0 -78
- flwr/cli/new/templates/app/code/model.baseline.py.tpl +0 -66
- flwr/cli/new/templates/app/code/server.baseline.py.tpl +0 -43
- flwr/cli/new/templates/app/code/server.huggingface.py.tpl +0 -42
- flwr/cli/new/templates/app/code/server.jax.py.tpl +0 -39
- flwr/cli/new/templates/app/code/server.mlx.py.tpl +0 -41
- flwr/cli/new/templates/app/code/server.numpy.py.tpl +0 -38
- flwr/cli/new/templates/app/code/server.pytorch.py.tpl +0 -41
- flwr/cli/new/templates/app/code/server.pytorch_legacy_api.py.tpl +0 -31
- flwr/cli/new/templates/app/code/server.sklearn.py.tpl +0 -44
- flwr/cli/new/templates/app/code/server.tensorflow.py.tpl +0 -38
- flwr/cli/new/templates/app/code/server.xgboost.py.tpl +0 -56
- flwr/cli/new/templates/app/code/strategy.baseline.py.tpl +0 -1
- flwr/cli/new/templates/app/code/task.huggingface.py.tpl +0 -98
- flwr/cli/new/templates/app/code/task.jax.py.tpl +0 -57
- flwr/cli/new/templates/app/code/task.mlx.py.tpl +0 -102
- flwr/cli/new/templates/app/code/task.numpy.py.tpl +0 -7
- flwr/cli/new/templates/app/code/task.pytorch.py.tpl +0 -98
- flwr/cli/new/templates/app/code/task.pytorch_legacy_api.py.tpl +0 -111
- flwr/cli/new/templates/app/code/task.sklearn.py.tpl +0 -67
- flwr/cli/new/templates/app/code/task.tensorflow.py.tpl +0 -52
- flwr/cli/new/templates/app/code/task.xgboost.py.tpl +0 -67
- flwr/cli/new/templates/app/code/utils.baseline.py.tpl +0 -1
- flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +0 -146
- flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +0 -80
- flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +0 -65
- flwr/cli/new/templates/app/pyproject.jax.toml.tpl +0 -52
- flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +0 -56
- flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +0 -49
- flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +0 -53
- flwr/cli/new/templates/app/pyproject.pytorch_legacy_api.toml.tpl +0 -53
- flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +0 -52
- flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +0 -53
- flwr/cli/new/templates/app/pyproject.xgboost.toml.tpl +0 -61
- flwr/common/pyproject.py +0 -42
- flwr/supercore/object_store/utils.py +0 -43
- flwr_nightly-1.23.0.dev20250930.dist-info/RECORD +0 -429
- /flwr/{common → supercore}/version.py +0 -0
- {flwr_nightly-1.23.0.dev20250930.dist-info → flwr_nightly-1.26.0.dev20260121.dist-info}/WHEEL +0 -0
- {flwr_nightly-1.23.0.dev20250930.dist-info → flwr_nightly-1.26.0.dev20260121.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,84 @@
|
|
|
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
|
+
|
|
22
|
+
from flwr.common.constant import NOOP_ACCOUNT_NAME, NOOP_FLWR_AID, AuthnType
|
|
23
|
+
from flwr.common.typing import (
|
|
24
|
+
AccountAuthCredentials,
|
|
25
|
+
AccountAuthLoginDetails,
|
|
26
|
+
AccountInfo,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
from .auth_plugin import ControlAuthnPlugin, ControlAuthzPlugin
|
|
30
|
+
|
|
31
|
+
NOOP_ACCOUNT_INFO = AccountInfo(
|
|
32
|
+
flwr_aid=NOOP_FLWR_AID,
|
|
33
|
+
account_name=NOOP_ACCOUNT_NAME,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class NoOpControlAuthnPlugin(ControlAuthnPlugin):
|
|
38
|
+
"""No-operation implementation of ControlAuthnPlugin."""
|
|
39
|
+
|
|
40
|
+
def __init__(
|
|
41
|
+
self,
|
|
42
|
+
account_auth_config_path: Path,
|
|
43
|
+
verify_tls_cert: bool,
|
|
44
|
+
):
|
|
45
|
+
pass
|
|
46
|
+
|
|
47
|
+
def get_login_details(self) -> AccountAuthLoginDetails | None:
|
|
48
|
+
"""Get the login details."""
|
|
49
|
+
# This allows the `flwr login` command to load the NoOp plugin accordingly,
|
|
50
|
+
# which then raises a LoginError when attempting to login.
|
|
51
|
+
return AccountAuthLoginDetails(
|
|
52
|
+
authn_type=AuthnType.NOOP, # No operation authn type
|
|
53
|
+
device_code="",
|
|
54
|
+
verification_uri_complete="",
|
|
55
|
+
expires_in=0,
|
|
56
|
+
interval=0,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
def validate_tokens_in_metadata(
|
|
60
|
+
self, metadata: Sequence[tuple[str, str | bytes]]
|
|
61
|
+
) -> tuple[bool, AccountInfo | None]:
|
|
62
|
+
"""Return valid for no-op plugin."""
|
|
63
|
+
return True, NOOP_ACCOUNT_INFO
|
|
64
|
+
|
|
65
|
+
def get_auth_tokens(self, device_code: str) -> AccountAuthCredentials | None:
|
|
66
|
+
"""Get authentication tokens."""
|
|
67
|
+
raise RuntimeError("NoOp plugin does not support getting auth tokens.")
|
|
68
|
+
|
|
69
|
+
def refresh_tokens(
|
|
70
|
+
self, metadata: Sequence[tuple[str, str | bytes]]
|
|
71
|
+
) -> tuple[Sequence[tuple[str, str | bytes]] | None, AccountInfo | None]:
|
|
72
|
+
"""Refresh authentication tokens in the provided metadata."""
|
|
73
|
+
return metadata, NOOP_ACCOUNT_INFO
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class NoOpControlAuthzPlugin(ControlAuthzPlugin):
|
|
77
|
+
"""No-operation implementation of ControlAuthzPlugin."""
|
|
78
|
+
|
|
79
|
+
def __init__(self, account_auth_config_path: Path, verify_tls_cert: bool):
|
|
80
|
+
pass
|
|
81
|
+
|
|
82
|
+
def authorize(self, account_info: AccountInfo) -> bool:
|
|
83
|
+
"""Return True for no-op plugin."""
|
|
84
|
+
return True
|
|
@@ -0,0 +1,24 @@
|
|
|
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
|
+
"""Federation Managers."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
from .federation_manager import FederationManager
|
|
19
|
+
from .noop_federation_manager import NoOpFederationManager
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"FederationManager",
|
|
23
|
+
"NoOpFederationManager",
|
|
24
|
+
]
|
|
@@ -0,0 +1,64 @@
|
|
|
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 base class FederationManager."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
from abc import ABC, abstractmethod
|
|
19
|
+
from typing import TYPE_CHECKING
|
|
20
|
+
|
|
21
|
+
from flwr.common.typing import Federation
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from flwr.server.superlink.linkstate.linkstate import LinkState
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class FederationManager(ABC):
|
|
28
|
+
"""Abstract base class for FederationManager."""
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def linkstate(self) -> "LinkState":
|
|
32
|
+
"""Return the LinkState instance."""
|
|
33
|
+
if not (ret := getattr(self, "_linkstate", None)):
|
|
34
|
+
raise RuntimeError("linkstate not set. Assign to linkstate property first.")
|
|
35
|
+
return ret # type: ignore
|
|
36
|
+
|
|
37
|
+
@linkstate.setter
|
|
38
|
+
def linkstate(self, linkstate: "LinkState") -> None:
|
|
39
|
+
"""Set the LinkState instance."""
|
|
40
|
+
self._linkstate = linkstate
|
|
41
|
+
|
|
42
|
+
@abstractmethod
|
|
43
|
+
def exists(self, federation: str) -> bool:
|
|
44
|
+
"""Check if a federation exists."""
|
|
45
|
+
|
|
46
|
+
@abstractmethod
|
|
47
|
+
def has_member(self, flwr_aid: str, federation: str) -> bool:
|
|
48
|
+
"""Check if the given account is a member of the federation."""
|
|
49
|
+
|
|
50
|
+
@abstractmethod
|
|
51
|
+
def filter_nodes(self, node_ids: set[int], federation: str) -> set[int]:
|
|
52
|
+
"""Given a list of node IDs, return sublist with nodes in federation."""
|
|
53
|
+
|
|
54
|
+
@abstractmethod
|
|
55
|
+
def has_node(self, node_id: int, federation: str) -> bool:
|
|
56
|
+
"""Given a node ID, check if it is in the federation."""
|
|
57
|
+
|
|
58
|
+
@abstractmethod
|
|
59
|
+
def get_federations(self, flwr_aid: str) -> list[str]:
|
|
60
|
+
"""Get federations of which the account is a member."""
|
|
61
|
+
|
|
62
|
+
@abstractmethod
|
|
63
|
+
def get_details(self, federation: str) -> Federation:
|
|
64
|
+
"""Get details of the federation."""
|
|
@@ -0,0 +1,71 @@
|
|
|
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
|
+
"""NoOp implementation of FederationManager."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
from flwr.common.constant import NOOP_FLWR_AID
|
|
19
|
+
from flwr.common.typing import Federation
|
|
20
|
+
from flwr.supercore.constant import NOOP_FEDERATION
|
|
21
|
+
|
|
22
|
+
from .federation_manager import FederationManager
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class NoOpFederationManager(FederationManager):
|
|
26
|
+
"""No-Op FederationManager implementation."""
|
|
27
|
+
|
|
28
|
+
def exists(self, federation: str) -> bool:
|
|
29
|
+
"""Check if a federation exists."""
|
|
30
|
+
return federation == NOOP_FEDERATION
|
|
31
|
+
|
|
32
|
+
def has_member(self, flwr_aid: str, federation: str) -> bool:
|
|
33
|
+
"""Check if the given account is a member of the federation."""
|
|
34
|
+
if not self.exists(federation):
|
|
35
|
+
raise ValueError(f"Federation '{federation}' does not exist.")
|
|
36
|
+
return flwr_aid == NOOP_FLWR_AID
|
|
37
|
+
|
|
38
|
+
def filter_nodes(self, node_ids: set[int], federation: str) -> set[int]:
|
|
39
|
+
"""Given a list of node IDs, return sublist with nodes in federation."""
|
|
40
|
+
if not self.exists(federation):
|
|
41
|
+
raise ValueError(f"Federation '{federation}' does not exist.")
|
|
42
|
+
return node_ids
|
|
43
|
+
|
|
44
|
+
def has_node(self, node_id: int, federation: str) -> bool:
|
|
45
|
+
"""Given a node ID, check if it is in the federation."""
|
|
46
|
+
if not self.exists(federation):
|
|
47
|
+
raise ValueError(f"Federation '{federation}' does not exist.")
|
|
48
|
+
return True
|
|
49
|
+
|
|
50
|
+
def get_federations(self, flwr_aid: str) -> list[str]:
|
|
51
|
+
"""Get federations of which the account is a member."""
|
|
52
|
+
if flwr_aid != NOOP_FLWR_AID:
|
|
53
|
+
return []
|
|
54
|
+
return [NOOP_FEDERATION]
|
|
55
|
+
|
|
56
|
+
def get_details(self, federation: str) -> Federation:
|
|
57
|
+
"""Get details of the federation."""
|
|
58
|
+
if federation != NOOP_FEDERATION:
|
|
59
|
+
raise ValueError(f"Federation '{federation}' does not exist.")
|
|
60
|
+
|
|
61
|
+
run_ids = self.linkstate.get_run_ids(flwr_aid=NOOP_FLWR_AID)
|
|
62
|
+
nodes = list(self.linkstate.get_node_info(owner_aids=[NOOP_FLWR_AID]))
|
|
63
|
+
runs = [
|
|
64
|
+
run for run_id in run_ids if (run := self.linkstate.get_run(run_id=run_id))
|
|
65
|
+
]
|
|
66
|
+
return Federation(
|
|
67
|
+
name=NOOP_FEDERATION,
|
|
68
|
+
member_aids=[NOOP_FLWR_AID],
|
|
69
|
+
nodes=nodes,
|
|
70
|
+
runs=runs,
|
|
71
|
+
)
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
import contextvars
|
|
19
|
-
from
|
|
19
|
+
from collections.abc import Callable
|
|
20
|
+
from typing import Any
|
|
20
21
|
|
|
21
22
|
import grpc
|
|
22
23
|
|
|
@@ -31,34 +32,42 @@ from flwr.proto.control_pb2 import ( # pylint: disable=E0611
|
|
|
31
32
|
StreamLogsRequest,
|
|
32
33
|
StreamLogsResponse,
|
|
33
34
|
)
|
|
34
|
-
from flwr.superlink.auth_plugin import
|
|
35
|
+
from flwr.superlink.auth_plugin import ControlAuthnPlugin, ControlAuthzPlugin
|
|
35
36
|
|
|
36
|
-
Request =
|
|
37
|
-
StartRunRequest
|
|
38
|
-
|
|
39
|
-
GetLoginDetailsRequest,
|
|
40
|
-
GetAuthTokensRequest,
|
|
41
|
-
]
|
|
37
|
+
Request = (
|
|
38
|
+
StartRunRequest | StreamLogsRequest | GetLoginDetailsRequest | GetAuthTokensRequest
|
|
39
|
+
)
|
|
42
40
|
|
|
43
|
-
Response =
|
|
44
|
-
StartRunResponse
|
|
45
|
-
|
|
41
|
+
Response = (
|
|
42
|
+
StartRunResponse
|
|
43
|
+
| StreamLogsResponse
|
|
44
|
+
| GetLoginDetailsResponse
|
|
45
|
+
| GetAuthTokensResponse
|
|
46
|
+
)
|
|
46
47
|
|
|
47
48
|
|
|
48
|
-
shared_account_info: contextvars.ContextVar[AccountInfo] =
|
|
49
|
-
"account_info", default=
|
|
49
|
+
shared_account_info: contextvars.ContextVar[AccountInfo | None] = (
|
|
50
|
+
contextvars.ContextVar("account_info", default=None)
|
|
50
51
|
)
|
|
51
52
|
|
|
52
53
|
|
|
53
|
-
|
|
54
|
-
"""
|
|
54
|
+
def get_current_account_info() -> AccountInfo:
|
|
55
|
+
"""Get the current account info from context, or return a default if not set."""
|
|
56
|
+
account_info = shared_account_info.get()
|
|
57
|
+
if account_info is None:
|
|
58
|
+
return AccountInfo(flwr_aid=None, account_name=None)
|
|
59
|
+
return account_info
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class ControlAccountAuthInterceptor(grpc.ServerInterceptor): # type: ignore
|
|
63
|
+
"""Control API interceptor for account authentication."""
|
|
55
64
|
|
|
56
65
|
def __init__(
|
|
57
66
|
self,
|
|
58
|
-
|
|
67
|
+
authn_plugin: ControlAuthnPlugin,
|
|
59
68
|
authz_plugin: ControlAuthzPlugin,
|
|
60
69
|
):
|
|
61
|
-
self.
|
|
70
|
+
self.authn_plugin = authn_plugin
|
|
62
71
|
self.authz_plugin = authz_plugin
|
|
63
72
|
|
|
64
73
|
def intercept_service(
|
|
@@ -93,48 +102,48 @@ class ControlUserAuthInterceptor(grpc.ServerInterceptor): # type: ignore
|
|
|
93
102
|
|
|
94
103
|
# Intercept GetLoginDetails and GetAuthTokens requests, and return
|
|
95
104
|
# the response without authentication
|
|
96
|
-
if isinstance(request, (GetLoginDetailsRequest
|
|
105
|
+
if isinstance(request, (GetLoginDetailsRequest | GetAuthTokensRequest)):
|
|
97
106
|
return call(request, context) # type: ignore
|
|
98
107
|
|
|
99
|
-
# For other requests, check if the
|
|
100
|
-
valid_tokens, account_info = self.
|
|
108
|
+
# For other requests, check if the account is authenticated
|
|
109
|
+
valid_tokens, account_info = self.authn_plugin.validate_tokens_in_metadata(
|
|
101
110
|
metadata
|
|
102
111
|
)
|
|
103
112
|
if valid_tokens:
|
|
104
113
|
if account_info is None:
|
|
105
114
|
context.abort(
|
|
106
115
|
grpc.StatusCode.UNAUTHENTICATED,
|
|
107
|
-
"Tokens validated, but
|
|
116
|
+
"Tokens validated, but account info not found",
|
|
108
117
|
)
|
|
109
118
|
raise grpc.RpcError()
|
|
110
|
-
# Store
|
|
119
|
+
# Store account info in contextvars for authenticated accounts
|
|
111
120
|
shared_account_info.set(account_info)
|
|
112
|
-
# Check if the
|
|
113
|
-
if not self.authz_plugin.
|
|
121
|
+
# Check if the account is authorized
|
|
122
|
+
if not self.authz_plugin.authorize(account_info):
|
|
114
123
|
context.abort(
|
|
115
124
|
grpc.StatusCode.PERMISSION_DENIED,
|
|
116
|
-
"❗️
|
|
125
|
+
"❗️ Account not authorized. "
|
|
117
126
|
"Please contact the SuperLink administrator.",
|
|
118
127
|
)
|
|
119
128
|
raise grpc.RpcError()
|
|
120
129
|
return call(request, context) # type: ignore
|
|
121
130
|
|
|
122
|
-
# If the
|
|
123
|
-
tokens, account_info = self.
|
|
131
|
+
# If the account is not authenticated, refresh tokens
|
|
132
|
+
tokens, account_info = self.authn_plugin.refresh_tokens(metadata)
|
|
124
133
|
if tokens is not None:
|
|
125
134
|
if account_info is None:
|
|
126
135
|
context.abort(
|
|
127
136
|
grpc.StatusCode.UNAUTHENTICATED,
|
|
128
|
-
"Tokens refreshed, but
|
|
137
|
+
"Tokens refreshed, but account info not found",
|
|
129
138
|
)
|
|
130
139
|
raise grpc.RpcError()
|
|
131
|
-
# Store
|
|
140
|
+
# Store account info in contextvars for authenticated accounts
|
|
132
141
|
shared_account_info.set(account_info)
|
|
133
|
-
# Check if the
|
|
134
|
-
if not self.authz_plugin.
|
|
142
|
+
# Check if the account is authorized
|
|
143
|
+
if not self.authz_plugin.authorize(account_info):
|
|
135
144
|
context.abort(
|
|
136
145
|
grpc.StatusCode.PERMISSION_DENIED,
|
|
137
|
-
"❗️
|
|
146
|
+
"❗️ Account not authorized. "
|
|
138
147
|
"Please contact the SuperLink administrator.",
|
|
139
148
|
)
|
|
140
149
|
raise grpc.RpcError()
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
"""Flower Control API event log interceptor."""
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
from collections.abc import Iterator
|
|
19
|
-
from typing import Any,
|
|
18
|
+
from collections.abc import Callable, Iterator
|
|
19
|
+
from typing import Any, cast
|
|
20
20
|
|
|
21
21
|
import grpc
|
|
22
22
|
from google.protobuf.message import Message as GrpcMessage
|
|
@@ -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 get_current_account_info
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
class ControlEventLogInterceptor(grpc.ServerInterceptor): # type: ignore
|
|
@@ -60,13 +60,13 @@ class ControlEventLogInterceptor(grpc.ServerInterceptor): # type: ignore
|
|
|
60
60
|
def _generic_method_handler(
|
|
61
61
|
request: GrpcMessage,
|
|
62
62
|
context: grpc.ServicerContext,
|
|
63
|
-
) ->
|
|
63
|
+
) -> GrpcMessage | Iterator[GrpcMessage] | BaseException:
|
|
64
64
|
log_entry: LogEntry
|
|
65
65
|
# Log before call
|
|
66
66
|
log_entry = self.log_plugin.compose_log_before_event(
|
|
67
67
|
request=request,
|
|
68
68
|
context=context,
|
|
69
|
-
account_info=
|
|
69
|
+
account_info=get_current_account_info(),
|
|
70
70
|
method_name=method_name,
|
|
71
71
|
)
|
|
72
72
|
self.log_plugin.write_log(log_entry)
|
|
@@ -85,7 +85,7 @@ class ControlEventLogInterceptor(grpc.ServerInterceptor): # type: ignore
|
|
|
85
85
|
log_entry = self.log_plugin.compose_log_after_event(
|
|
86
86
|
request=request,
|
|
87
87
|
context=context,
|
|
88
|
-
account_info=
|
|
88
|
+
account_info=get_current_account_info(),
|
|
89
89
|
method_name=method_name,
|
|
90
90
|
response=unary_response or error,
|
|
91
91
|
)
|
|
@@ -115,7 +115,7 @@ class ControlEventLogInterceptor(grpc.ServerInterceptor): # type: ignore
|
|
|
115
115
|
log_entry = self.log_plugin.compose_log_after_event(
|
|
116
116
|
request=request,
|
|
117
117
|
context=context,
|
|
118
|
-
account_info=
|
|
118
|
+
account_info=get_current_account_info(),
|
|
119
119
|
method_name=method_name,
|
|
120
120
|
response=stream_response or error,
|
|
121
121
|
)
|
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
from logging import INFO
|
|
19
|
-
from typing import Optional
|
|
20
19
|
|
|
21
20
|
import grpc
|
|
22
21
|
|
|
@@ -31,18 +30,22 @@ from flwr.supercore.ffs import FfsFactory
|
|
|
31
30
|
from flwr.supercore.license_plugin import LicensePlugin
|
|
32
31
|
from flwr.supercore.object_store import ObjectStoreFactory
|
|
33
32
|
from flwr.superlink.artifact_provider import ArtifactProvider
|
|
34
|
-
from flwr.superlink.auth_plugin import
|
|
33
|
+
from flwr.superlink.auth_plugin import (
|
|
34
|
+
ControlAuthnPlugin,
|
|
35
|
+
ControlAuthzPlugin,
|
|
36
|
+
NoOpControlAuthnPlugin,
|
|
37
|
+
)
|
|
35
38
|
|
|
39
|
+
from .control_account_auth_interceptor import ControlAccountAuthInterceptor
|
|
36
40
|
from .control_event_log_interceptor import ControlEventLogInterceptor
|
|
37
41
|
from .control_license_interceptor import ControlLicenseInterceptor
|
|
38
42
|
from .control_servicer import ControlServicer
|
|
39
|
-
from .control_user_auth_interceptor import ControlUserAuthInterceptor
|
|
40
43
|
|
|
41
44
|
try:
|
|
42
45
|
from flwr.ee import get_license_plugin
|
|
43
46
|
except ImportError:
|
|
44
47
|
|
|
45
|
-
def get_license_plugin() ->
|
|
48
|
+
def get_license_plugin() -> LicensePlugin | None:
|
|
46
49
|
"""Return the license plugin."""
|
|
47
50
|
|
|
48
51
|
|
|
@@ -52,15 +55,16 @@ def run_control_api_grpc(
|
|
|
52
55
|
state_factory: LinkStateFactory,
|
|
53
56
|
ffs_factory: FfsFactory,
|
|
54
57
|
objectstore_factory: ObjectStoreFactory,
|
|
55
|
-
certificates:
|
|
58
|
+
certificates: tuple[bytes, bytes, bytes] | None,
|
|
56
59
|
is_simulation: bool,
|
|
57
|
-
|
|
58
|
-
authz_plugin:
|
|
59
|
-
event_log_plugin:
|
|
60
|
-
artifact_provider:
|
|
60
|
+
authn_plugin: ControlAuthnPlugin,
|
|
61
|
+
authz_plugin: ControlAuthzPlugin,
|
|
62
|
+
event_log_plugin: EventLogWriterPlugin | None = None,
|
|
63
|
+
artifact_provider: ArtifactProvider | None = None,
|
|
64
|
+
fleet_api_type: str | None = None,
|
|
61
65
|
) -> grpc.Server:
|
|
62
66
|
"""Run Control API (gRPC, request-response)."""
|
|
63
|
-
license_plugin:
|
|
67
|
+
license_plugin: LicensePlugin | None = get_license_plugin()
|
|
64
68
|
if license_plugin and not license_plugin.check_license():
|
|
65
69
|
flwr_exit(ExitCode.SUPERLINK_LICENSE_INVALID)
|
|
66
70
|
|
|
@@ -69,15 +73,14 @@ def run_control_api_grpc(
|
|
|
69
73
|
ffs_factory=ffs_factory,
|
|
70
74
|
objectstore_factory=objectstore_factory,
|
|
71
75
|
is_simulation=is_simulation,
|
|
72
|
-
|
|
76
|
+
authn_plugin=authn_plugin,
|
|
73
77
|
artifact_provider=artifact_provider,
|
|
78
|
+
fleet_api_type=fleet_api_type,
|
|
74
79
|
)
|
|
75
|
-
interceptors
|
|
80
|
+
interceptors = [ControlAccountAuthInterceptor(authn_plugin, authz_plugin)]
|
|
76
81
|
if license_plugin is not None:
|
|
77
82
|
interceptors.append(ControlLicenseInterceptor(license_plugin))
|
|
78
|
-
|
|
79
|
-
interceptors.append(ControlUserAuthInterceptor(auth_plugin, authz_plugin))
|
|
80
|
-
# Event log interceptor must be added after user auth interceptor
|
|
83
|
+
# Event log interceptor must be added after account auth interceptor
|
|
81
84
|
if event_log_plugin is not None:
|
|
82
85
|
interceptors.append(ControlEventLogInterceptor(event_log_plugin))
|
|
83
86
|
log(INFO, "Flower event logging enabled")
|
|
@@ -90,12 +93,12 @@ def run_control_api_grpc(
|
|
|
90
93
|
interceptors=interceptors or None,
|
|
91
94
|
)
|
|
92
95
|
|
|
93
|
-
if
|
|
96
|
+
if isinstance(authn_plugin, NoOpControlAuthnPlugin):
|
|
94
97
|
log(INFO, "Flower Deployment Runtime: Starting Control API on %s", address)
|
|
95
98
|
else:
|
|
96
99
|
log(
|
|
97
100
|
INFO,
|
|
98
|
-
"Flower Deployment Runtime: Starting Control API with
|
|
101
|
+
"Flower Deployment Runtime: Starting Control API with account "
|
|
99
102
|
"authentication on %s",
|
|
100
103
|
address,
|
|
101
104
|
)
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
"""Flower Control API license interceptor."""
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
from collections.abc import Iterator
|
|
19
|
-
from typing import Any
|
|
18
|
+
from collections.abc import Callable, Iterator
|
|
19
|
+
from typing import Any
|
|
20
20
|
|
|
21
21
|
import grpc
|
|
22
22
|
from google.protobuf.message import Message as GrpcMessage
|
|
@@ -57,7 +57,7 @@ class ControlLicenseInterceptor(grpc.ServerInterceptor): # type: ignore
|
|
|
57
57
|
def _generic_method_handler(
|
|
58
58
|
request: GrpcMessage,
|
|
59
59
|
context: grpc.ServicerContext,
|
|
60
|
-
) ->
|
|
60
|
+
) -> GrpcMessage | Iterator[GrpcMessage]:
|
|
61
61
|
"""Handle the method call with license checking."""
|
|
62
62
|
call = method_handler.unary_unary or method_handler.unary_stream
|
|
63
63
|
|