diff --git a/linode_api4/groups/account.py b/linode_api4/groups/account.py index 8805c6416..884503fe8 100644 --- a/linode_api4/groups/account.py +++ b/linode_api4/groups/account.py @@ -8,6 +8,7 @@ AccountBetaProgram, AccountSettings, BetaProgram, + ChildAccount, Event, Invoice, Login, @@ -18,6 +19,7 @@ ServiceTransfer, User, ) +from linode_api4.objects.profile import PersonalAccessToken class AccountGroup(Group): @@ -496,3 +498,14 @@ def availabilities(self, *filters): :rtype: PaginatedList of AccountAvailability """ return self.client._get_and_filter(AccountAvailability, *filters) + + def child_accounts(self, *filters): + """ + Returns a list of all child accounts under the this parent account. + + API doc: TBD + + :returns: a list of all child accounts. + :rtype: PaginatedList of ChildAccount + """ + return self.client._get_and_filter(ChildAccount, *filters) diff --git a/linode_api4/objects/account.py b/linode_api4/objects/account.py index 19ecd79a0..aa0a8f57a 100644 --- a/linode_api4/objects/account.py +++ b/linode_api4/objects/account.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from datetime import datetime import requests @@ -16,6 +18,7 @@ ) from linode_api4.objects.longview import LongviewClient, LongviewSubscription from linode_api4.objects.nodebalancer import NodeBalancer +from linode_api4.objects.profile import PersonalAccessToken from linode_api4.objects.support import SupportTicket @@ -53,6 +56,37 @@ class Account(Base): } +class ChildAccount(Account): + """ + A child account under a parent account. + + API Documentation: TBD + """ + + api_endpoint = "/account/child-accounts/{euuid}" + id_attribute = "euuid" + + def create_token(self, **kwargs): + """ + Create a ephemeral token for accessing the child account. + + API Documentation: TBD + """ + resp = self._client.post( + "{}/token".format(self.api_endpoint), + model=self, + data=kwargs, + ) + + if "errors" in resp: + raise UnexpectedResponseError( + "Unexpected response when creating a token for the child account!", + json=resp, + ) + + return PersonalAccessToken(self._client, resp["id"], resp) + + class ServiceTransfer(Base): """ A transfer request for transferring a service between Linode accounts. @@ -476,6 +510,7 @@ class User(Base): properties = { "email": Property(), "username": Property(identifier=True, mutable=True), + "user_type": Property(), "restricted": Property(mutable=True), "ssh_keys": Property(), "tfa_enabled": Property(), diff --git a/test/fixtures/account_child-accounts.json b/test/fixtures/account_child-accounts.json new file mode 100644 index 000000000..e7e9aca43 --- /dev/null +++ b/test/fixtures/account_child-accounts.json @@ -0,0 +1,36 @@ +{ + "data": [ + { + "active_since": "2018-01-01T00:01:01", + "address_1": "123 Main Street", + "address_2": "Suite A", + "balance": 200, + "balance_uninvoiced": 145, + "billing_source": "external", + "capabilities": [ + "Linodes", + "NodeBalancers", + "Block Storage", + "Object Storage" + ], + "city": "Philadelphia", + "company": "Linode LLC", + "country": "US", + "credit_card": { + "expiry": "11/2022", + "last_four": 1111 + }, + "email": "john.smith@linode.com", + "euuid": "E1AF5EEC-526F-487D-B317EBEB34C87D71", + "first_name": "John", + "last_name": "Smith", + "phone": "215-555-1212", + "state": "PA", + "tax_id": "ATU99999999", + "zip": "19102-1234" + } + ], + "page": 1, + "pages": 1, + "results": 1 +} diff --git a/test/fixtures/account_child-accounts_123456.json b/test/fixtures/account_child-accounts_123456.json new file mode 100644 index 000000000..8ce264693 --- /dev/null +++ b/test/fixtures/account_child-accounts_123456.json @@ -0,0 +1,29 @@ +{ + "active_since": "2018-01-01T00:01:01", + "address_1": "123 Main Street", + "address_2": "Suite A", + "balance": 200, + "balance_uninvoiced": 145, + "billing_source": "external", + "capabilities": [ + "Linodes", + "NodeBalancers", + "Block Storage", + "Object Storage" + ], + "city": "Philadelphia", + "company": "Linode LLC", + "country": "US", + "credit_card": { + "expiry": "11/2022", + "last_four": 1111 + }, + "email": "john.smith@linode.com", + "euuid": "E1AF5EEC-526F-487D-B317EBEB34C87D71", + "first_name": "John", + "last_name": "Smith", + "phone": "215-555-1212", + "state": "PA", + "tax_id": "ATU99999999", + "zip": "19102-1234" +} \ No newline at end of file diff --git a/test/fixtures/account_child-accounts_123456_token.json b/test/fixtures/account_child-accounts_123456_token.json new file mode 100644 index 000000000..44afea72b --- /dev/null +++ b/test/fixtures/account_child-accounts_123456_token.json @@ -0,0 +1,8 @@ +{ + "created": "2024-01-01T00:01:01", + "expiry": "2024-01-01T13:46:32", + "id": 123, + "label": "cool_customer_proxy", + "scopes": "*", + "token": "abcdefghijklmnop" +} \ No newline at end of file diff --git a/test/integration/models/account/test_account.py b/test/integration/models/account/test_account.py index c2d2ffb8a..693d5bb82 100644 --- a/test/integration/models/account/test_account.py +++ b/test/integration/models/account/test_account.py @@ -3,7 +3,14 @@ import pytest -from linode_api4.objects import Account, AccountSettings, Event, Login, User +from linode_api4.objects import ( + Account, + AccountSettings, + ChildAccount, + Event, + Login, + User, +) @pytest.mark.smoke @@ -85,3 +92,12 @@ def test_get_user(test_linode_client): assert username == user.username assert "email" in user._raw_json assert "email" in user._raw_json + + +def test_list_child_accounts(test_linode_client): + client = test_linode_client + child_accounts = client.account.child_accounts() + if len(child_accounts) > 0: + child_account = ChildAccount(client, child_accounts[0].euuid) + child_account._api_get() + child_account.create_token() diff --git a/test/unit/objects/account_test.py b/test/unit/objects/account_test.py index b3f7be1e3..053cc3d0e 100644 --- a/test/unit/objects/account_test.py +++ b/test/unit/objects/account_test.py @@ -24,6 +24,7 @@ Volume, get_obj_grants, ) +from linode_api4.objects.account import ChildAccount class InvoiceTest(ClientBaseCase): @@ -290,3 +291,21 @@ def test_account_availability_api_get(self): self.assertEqual(availability.available, ["Linodes", "Kubernetes"]) self.assertEqual(m.call_url, account_availability_url) + + +class ChildAccountTest(ClientBaseCase): + """ + Test methods of the ChildAccount + """ + + def test_child_account_api_list(self): + result = self.client.account.child_accounts() + self.assertEqual(len(result), 1) + self.assertEqual(result[0].euuid, "E1AF5EEC-526F-487D-B317EBEB34C87D71") + + def test_child_account_create_token(self): + child_account = self.client.load(ChildAccount, 123456) + with self.mock_post("/account/child-accounts/123456/token") as m: + token = child_account.create_token() + self.assertEqual(token.token, "abcdefghijklmnop") + self.assertEqual(m.call_data, {})