From 9d7edc25434a0d82bbdfb0ceee778bece4204fe8 Mon Sep 17 00:00:00 2001 From: jo Date: Thu, 13 Nov 2025 20:27:17 +0100 Subject: [PATCH 1/5] feat: support Storage Box Subaccount Actions From 4ddca12d37fc38a3c3325ba3b788c762182a63de Mon Sep 17 00:00:00 2001 From: jo Date: Thu, 13 Nov 2025 20:34:03 +0100 Subject: [PATCH 2/5] feat: change_home_directory --- hcloud/storage_boxes/client.py | 24 ++++++++++++++++++++++++ tests/unit/storage_boxes/test_client.py | 23 +++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/hcloud/storage_boxes/client.py b/hcloud/storage_boxes/client.py index 267b2c0f..11edaf2f 100644 --- a/hcloud/storage_boxes/client.py +++ b/hcloud/storage_boxes/client.py @@ -1046,3 +1046,27 @@ def delete_subaccount( return DeleteStorageBoxSubaccountResponse( action=BoundAction(self._parent.actions, response["action"]), ) + + def change_subaccount_home_directory( + self, + subaccount: StorageBoxSubaccount | BoundStorageBoxSubaccount, + home_directory: str, + ) -> BoundAction: + """ + Change the home directory of a Storage Box Subaccount. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-subaccount-actions-change-home-directory + + :param subaccount: Storage Box Subaccount to update. + :param home_directory: Home directory for the Subaccount. + """ + data: dict[str, Any] = { + "home_directory": home_directory, + } + + response = self._client.request( + method="POST", + url=f"{self._base_url}/{subaccount.storage_box.id}/subaccounts/{subaccount.id}/actions/change_home_directory", + json=data, + ) + return BoundAction(self._parent.actions, response["action"]) diff --git a/tests/unit/storage_boxes/test_client.py b/tests/unit/storage_boxes/test_client.py index 1503120b..2d019ae1 100644 --- a/tests/unit/storage_boxes/test_client.py +++ b/tests/unit/storage_boxes/test_client.py @@ -1045,3 +1045,26 @@ def test_delete_subaccount( ) assert_bound_action1(result.action, resource_client._parent.actions) + + def test_change_subaccount_home_directory( + self, + request_mock: mock.MagicMock, + resource_client: StorageBoxesClient, + action_response, + ): + request_mock.return_value = action_response + + action = resource_client.change_subaccount_home_directory( + StorageBoxSubaccount(id=45, storage_box=StorageBox(42)), + home_directory="path", + ) + + request_mock.assert_called_with( + method="POST", + url="/storage_boxes/42/subaccounts/45/actions/change_home_directory", + json={ + "home_directory": "path", + }, + ) + + assert_bound_action1(action, resource_client._parent.actions) From 8ef683ce68b23dc12d6b74ac3e68e848d035ac1a Mon Sep 17 00:00:00 2001 From: jo Date: Thu, 13 Nov 2025 20:36:21 +0100 Subject: [PATCH 3/5] feat: reset_subaccount_password --- hcloud/storage_boxes/client.py | 24 ++++++++++++++++++++++++ tests/unit/storage_boxes/test_client.py | 23 +++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/hcloud/storage_boxes/client.py b/hcloud/storage_boxes/client.py index 11edaf2f..4b0b1806 100644 --- a/hcloud/storage_boxes/client.py +++ b/hcloud/storage_boxes/client.py @@ -1070,3 +1070,27 @@ def change_subaccount_home_directory( json=data, ) return BoundAction(self._parent.actions, response["action"]) + + def reset_subaccount_password( + self, + subaccount: StorageBoxSubaccount | BoundStorageBoxSubaccount, + password: str, + ) -> BoundAction: + """ + Reset the password of a Storage Box Subaccount. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-subaccount-actions-reset-password + + :param subaccount: Storage Box Subaccount to update. + :param password: Password for the Subaccount. + """ + data: dict[str, Any] = { + "password": password, + } + + response = self._client.request( + method="POST", + url=f"{self._base_url}/{subaccount.storage_box.id}/subaccounts/{subaccount.id}/actions/reset_subaccount_password", + json=data, + ) + return BoundAction(self._parent.actions, response["action"]) diff --git a/tests/unit/storage_boxes/test_client.py b/tests/unit/storage_boxes/test_client.py index 2d019ae1..c95f84a1 100644 --- a/tests/unit/storage_boxes/test_client.py +++ b/tests/unit/storage_boxes/test_client.py @@ -1068,3 +1068,26 @@ def test_change_subaccount_home_directory( ) assert_bound_action1(action, resource_client._parent.actions) + + def test_reset_subaccount_password( + self, + request_mock: mock.MagicMock, + resource_client: StorageBoxesClient, + action_response, + ): + request_mock.return_value = action_response + + action = resource_client.reset_subaccount_password( + StorageBoxSubaccount(id=45, storage_box=StorageBox(42)), + password="password", + ) + + request_mock.assert_called_with( + method="POST", + url="/storage_boxes/42/subaccounts/45/actions/reset_subaccount_password", + json={ + "password": "password", + }, + ) + + assert_bound_action1(action, resource_client._parent.actions) From c01ae57e8745a6550d90223d957f00b7dbaf9910 Mon Sep 17 00:00:00 2001 From: jo Date: Thu, 13 Nov 2025 20:40:05 +0100 Subject: [PATCH 4/5] feat: update_access_settings --- hcloud/storage_boxes/client.py | 22 +++++++++++++++++++ tests/unit/storage_boxes/test_client.py | 29 +++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/hcloud/storage_boxes/client.py b/hcloud/storage_boxes/client.py index 4b0b1806..e8dced5a 100644 --- a/hcloud/storage_boxes/client.py +++ b/hcloud/storage_boxes/client.py @@ -1094,3 +1094,25 @@ def reset_subaccount_password( json=data, ) return BoundAction(self._parent.actions, response["action"]) + + def update_subaccount_access_settings( + self, + subaccount: StorageBoxSubaccount | BoundStorageBoxSubaccount, + access_settings: StorageBoxSubaccountAccessSettings, + ) -> BoundAction: + """ + Update the access settings of a Storage Box Subaccount. + + See https://docs.hetzner.cloud/reference/hetzner#storage-box-subaccount-actions-update-access-settings + + :param subaccount: Storage Box Subaccount to update. + :param access_settings: Access settings for the Subaccount. + """ + data: dict[str, Any] = access_settings.to_payload() + + response = self._client.request( + method="POST", + url=f"{self._base_url}/{subaccount.storage_box.id}/subaccounts/{subaccount.id}/actions/update_access_settings", + json=data, + ) + return BoundAction(self._parent.actions, response["action"]) diff --git a/tests/unit/storage_boxes/test_client.py b/tests/unit/storage_boxes/test_client.py index c95f84a1..4538db3c 100644 --- a/tests/unit/storage_boxes/test_client.py +++ b/tests/unit/storage_boxes/test_client.py @@ -1091,3 +1091,32 @@ def test_reset_subaccount_password( ) assert_bound_action1(action, resource_client._parent.actions) + + def test_update_subaccount_access_settings( + self, + request_mock: mock.MagicMock, + resource_client: StorageBoxesClient, + action_response, + ): + request_mock.return_value = action_response + + action = resource_client.update_subaccount_access_settings( + StorageBoxSubaccount(id=45, storage_box=StorageBox(42)), + access_settings=StorageBoxSubaccountAccessSettings( + reachable_externally=True, + ssh_enabled=True, + samba_enabled=False, + ), + ) + + request_mock.assert_called_with( + method="POST", + url="/storage_boxes/42/subaccounts/45/actions/update_access_settings", + json={ + "reachable_externally": True, + "ssh_enabled": True, + "samba_enabled": False, + }, + ) + + assert_bound_action1(action, resource_client._parent.actions) From f644ed8615e6effe4a41782ce5266913d31f4e69 Mon Sep 17 00:00:00 2001 From: jo Date: Thu, 13 Nov 2025 20:41:36 +0100 Subject: [PATCH 5/5] check if storage box is none --- hcloud/storage_boxes/client.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/hcloud/storage_boxes/client.py b/hcloud/storage_boxes/client.py index e8dced5a..7d3e9755 100644 --- a/hcloud/storage_boxes/client.py +++ b/hcloud/storage_boxes/client.py @@ -1060,6 +1060,9 @@ def change_subaccount_home_directory( :param subaccount: Storage Box Subaccount to update. :param home_directory: Home directory for the Subaccount. """ + if subaccount.storage_box is None: + raise ValueError("subaccount storage_box property is none") + data: dict[str, Any] = { "home_directory": home_directory, } @@ -1084,6 +1087,9 @@ def reset_subaccount_password( :param subaccount: Storage Box Subaccount to update. :param password: Password for the Subaccount. """ + if subaccount.storage_box is None: + raise ValueError("subaccount storage_box property is none") + data: dict[str, Any] = { "password": password, } @@ -1108,6 +1114,9 @@ def update_subaccount_access_settings( :param subaccount: Storage Box Subaccount to update. :param access_settings: Access settings for the Subaccount. """ + if subaccount.storage_box is None: + raise ValueError("subaccount storage_box property is none") + data: dict[str, Any] = access_settings.to_payload() response = self._client.request(