diff --git a/docs/content/en/latest/administration/users/_index.md b/docs/content/en/latest/administration/users/_index.md index 280df4408..3e0312816 100644 --- a/docs/content/en/latest/administration/users/_index.md +++ b/docs/content/en/latest/administration/users/_index.md @@ -16,6 +16,10 @@ See [Manage Users and UserGroups](https://www.gooddata.com/docs/cloud/manage-dep * [get_user](./get_user/) * [delete_user](./delete_user/) * [list_users](./list_users/) +* [create_user_api_token](./create_user_api_token/) +* [get_user_api_token](./get_user_api_token/) +* [delete_user_api_token](./delete_user_api_token/) +* [list_user_api_tokens](./list_user_api_tokens/) ### Declarative Methods diff --git a/docs/content/en/latest/administration/users/create_user_api_token.md b/docs/content/en/latest/administration/users/create_user_api_token.md new file mode 100644 index 000000000..3e7c4c334 --- /dev/null +++ b/docs/content/en/latest/administration/users/create_user_api_token.md @@ -0,0 +1,34 @@ +--- +title: "create_user_api_token" +linkTitle: "create_user_api_token" +weight: 10 +no_list: true +superheading: "catalog_user." +--- + + + +``create_user_api_token(user_id: str, api_token_id: str)`` + +Create a new user api token. + +{{% parameters-block title="Parameters"%}} +{{< parameter p_name="user_id" p_type="string" >}} +User identification string. e.g. "admin" +{{< /parameter >}} +{{< parameter p_name="api_token_id" p_type="string" >}} +API token identification string. e.g. "admin_token" +{{< /parameter >}} +{{% /parameters-block %}} + +{{% parameters-block title="Returns" %}} +{{< parameter p_type="CatalogApiToken" >}} +Instance of CatalogApiToken holding the information about API token (its id, and bearer token). +{{< /parameter >}} +{{% /parameters-block %}} + +## Example + +```python +sdk.catalog_user.create_user_api_token(user_id="admin", api_token_id="admin_token") +``` diff --git a/docs/content/en/latest/administration/users/delete_user_api_token.md b/docs/content/en/latest/administration/users/delete_user_api_token.md new file mode 100644 index 000000000..76bb9122c --- /dev/null +++ b/docs/content/en/latest/administration/users/delete_user_api_token.md @@ -0,0 +1,31 @@ +--- +title: "delete_user_api_token" +linkTitle: "delete_user_api_token" +weight: 10 +no_list: true +superheading: "catalog_user." +--- + + + +``delete_user_api_token(user_id: str, api_token_id: str)`` + +Delete user api token. + +{{% parameters-block title="Parameters"%}} +{{< parameter p_name="user_id" p_type="string" >}} +User identification string. e.g. "admin" +{{< /parameter >}} +{{< parameter p_name="api_token_id" p_type="string" >}} +API token identification string. e.g. "admin_token" +{{< /parameter >}} +{{% /parameters-block %}} + +{{% parameters-block title="Returns" None="yes" %}} +{{% /parameters-block %}} + +## Example + +```python +sdk.catalog_user.delete_user_api_token(user_id="admin", api_token_id="admin_token") +``` diff --git a/docs/content/en/latest/administration/users/get_user_api_token.md b/docs/content/en/latest/administration/users/get_user_api_token.md new file mode 100644 index 000000000..fc5193a05 --- /dev/null +++ b/docs/content/en/latest/administration/users/get_user_api_token.md @@ -0,0 +1,34 @@ +--- +title: "get_user_api_token" +linkTitle: "get_user_api_token" +weight: 10 +no_list: true +superheading: "catalog_user." +--- + + + +``get_user_api_token(user_id: str, api_token_id: str)`` + +Get user api token. + +{{% parameters-block title="Parameters"%}} +{{< parameter p_name="user_id" p_type="string" >}} +User identification string. e.g. "admin" +{{< /parameter >}} +{{< parameter p_name="api_token_id" p_type="string" >}} +API token identification string. e.g. "admin_token" +{{< /parameter >}} +{{% /parameters-block %}} + +{{% parameters-block title="Returns" %}} +{{< parameter p_type="CatalogApiToken" >}} +Instance of CatalogApiToken holding the information about API token. +Note that the bearer token is not returned. It will always be None. +{{< /parameter >}} +{{% /parameters-block %}} +## Example + +```python +sdk.catalog_user.get_user_api_token(user_id="admin", api_token_id="admin_token") +``` diff --git a/docs/content/en/latest/administration/users/list_user_api_tokens.md b/docs/content/en/latest/administration/users/list_user_api_tokens.md new file mode 100644 index 000000000..6c4dad769 --- /dev/null +++ b/docs/content/en/latest/administration/users/list_user_api_tokens.md @@ -0,0 +1,32 @@ +--- +title: "list_user_api_tokens" +linkTitle: "list_user_api_tokens" +weight: 10 +no_list: true +superheading: "catalog_user." +--- + + + +``list_user_api_tokens(user_id: str)`` + +List all user API tokens. + +{{% parameters-block title="Parameters"%}} +{{< parameter p_name="user_id" p_type="string" >}} +User identification string. e.g. "admin" +{{< /parameter >}} +{{% /parameters-block %}} + +{{% parameters-block title="Returns" %}} +{{< parameter p_type="list[CatalogApiToken]" >}} +List of CatalogApiToken instances holding the information about users API tokens. +Note that the bearer token is not returned. It will always be None. +{{< /parameter >}} +{{% /parameters-block %}} + +## Example + +```python +sdk.catalog_user.list_user_api_tokens(user_id="admin") +``` diff --git a/gooddata-sdk/gooddata_sdk/catalog/user/entity_model/api_token.py b/gooddata-sdk/gooddata_sdk/catalog/user/entity_model/api_token.py new file mode 100644 index 000000000..9fcac8f5f --- /dev/null +++ b/gooddata-sdk/gooddata_sdk/catalog/user/entity_model/api_token.py @@ -0,0 +1,12 @@ +# (C) 2024 GoodData Corporation +from typing import Optional + +from attrs import define + +from gooddata_sdk.catalog.base import Base + + +@define(auto_attribs=True, kw_only=True) +class CatalogApiToken(Base): + id: str + bearer_token: Optional[str] = None diff --git a/gooddata-sdk/gooddata_sdk/catalog/user/service.py b/gooddata-sdk/gooddata_sdk/catalog/user/service.py index 92498a5e7..a6881cf52 100644 --- a/gooddata-sdk/gooddata_sdk/catalog/user/service.py +++ b/gooddata-sdk/gooddata_sdk/catalog/user/service.py @@ -5,11 +5,14 @@ from pathlib import Path from gooddata_api_client.exceptions import NotFoundException +from gooddata_api_client.model.json_api_api_token_in import JsonApiApiTokenIn +from gooddata_api_client.model.json_api_api_token_in_document import JsonApiApiTokenInDocument from gooddata_sdk.catalog.catalog_service_base import CatalogServiceBase from gooddata_sdk.catalog.user.declarative_model.user import CatalogDeclarativeUsers from gooddata_sdk.catalog.user.declarative_model.user_and_user_groups import CatalogDeclarativeUsersUserGroups from gooddata_sdk.catalog.user.declarative_model.user_group import CatalogDeclarativeUserGroups +from gooddata_sdk.catalog.user.entity_model.api_token import CatalogApiToken from gooddata_sdk.catalog.user.entity_model.user import CatalogUser, CatalogUserDocument from gooddata_sdk.catalog.user.entity_model.user_group import CatalogUserGroup, CatalogUserGroupDocument from gooddata_sdk.catalog.user.management_model.management import ( @@ -391,3 +394,26 @@ def assign_permissions_bulk(self, permissions_assignment: CatalogPermissionsAssi def revoke_permissions_bulk(self, permissions_assignment: CatalogPermissionsAssignment) -> None: self._user_management_api.revoke_permissions(permissions_assignment.to_api()) + + def list_user_api_tokens(self, user_id: str) -> list[CatalogApiToken]: + get_api_tokens = functools.partial( + self._entities_api.get_all_entities_api_tokens, + user_id, + _check_return_type=False, + ) + api_tokens = load_all_entities(get_api_tokens) + return [CatalogApiToken(id=v["id"]) for v in api_tokens.data] + + def create_user_api_token(self, user_id: str, api_token_id: str) -> CatalogApiToken: + document = JsonApiApiTokenInDocument(data=JsonApiApiTokenIn(id=api_token_id, type="apiToken")) + api_token = self._entities_api.create_entity_api_tokens(user_id, document, _check_return_type=False) + v = api_token.data + return CatalogApiToken(id=v["id"], bearer_token=v.get("attributes", {}).get("bearerToken")) + + def get_user_api_token(self, user_id: str, api_token_id: str) -> CatalogApiToken: + api_token = self._entities_api.get_entity_api_tokens(user_id, api_token_id, _check_return_type=False) + v = api_token.data + return CatalogApiToken(id=v["id"]) + + def delete_user_api_token(self, user_id: str, api_token_id: str) -> None: + self._entities_api.delete_entity_api_tokens(user_id, api_token_id) diff --git a/gooddata-sdk/tests/catalog/fixtures/users/test_api_tokens.yaml b/gooddata-sdk/tests/catalog/fixtures/users/test_api_tokens.yaml new file mode 100644 index 000000000..4f23b253b --- /dev/null +++ b/gooddata-sdk/tests/catalog/fixtures/users/test_api_tokens.yaml @@ -0,0 +1,459 @@ +# (C) 2024 GoodData Corporation +version: 1 +interactions: + - request: + method: GET + uri: http://localhost:3000/api/v1/entities/users/demo/apiTokens?page=0&size=500 + body: null + headers: + Accept: + - application/vnd.gooddata.api+json + Accept-Encoding: + - br, gzip, deflate + X-GDC-VALIDATE-RELATIONS: + - 'true' + X-Requested-With: + - XMLHttpRequest + response: + status: + code: 200 + message: OK + headers: + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Expose-Headers: + - Content-Disposition, Content-Length, Content-Range, Set-Cookie + Cache-Control: + - no-cache, no-store, max-age=0, must-revalidate + Connection: + - keep-alive + Content-Length: + - '189' + Content-Security-Policy: + - 'default-src ''self'' *.wistia.com *.wistia.net; script-src ''self'' ''unsafe-inline'' + ''unsafe-eval'' *.wistia.com *.wistia.net *.hsforms.net *.hsforms.com + src.litix.io matomo.anywhere.gooddata.com *.jquery.com unpkg.com cdnjs.cloudflare.com; + img-src * data: blob:; style-src ''self'' ''unsafe-inline'' fonts.googleapis.com + cdn.jsdelivr.net fast.fonts.net; font-src ''self'' data: fonts.gstatic.com + *.alicdn.com *.wistia.com cdn.jsdelivr.net info.gooddata.com; frame-src + ''self'' *.hsforms.net *.hsforms.com; object-src ''none''; worker-src + ''self'' blob:; child-src blob:; connect-src ''self'' *.tiles.mapbox.com + *.mapbox.com *.litix.io *.wistia.com *.hsforms.net *.hsforms.com embedwistia-a.akamaihd.net + matomo.anywhere.gooddata.com; media-src ''self'' blob: data: *.wistia.com + *.wistia.net embedwistia-a.akamaihd.net' + Content-Type: + - application/vnd.gooddata.api+json + DATE: &id001 + - PLACEHOLDER + Expires: + - '0' + GoodData-Deployment: + - aio + Permission-Policy: + - geolocation 'none'; midi 'none'; sync-xhr 'none'; microphone 'none'; camera + 'none'; magnetometer 'none'; gyroscope 'none'; fullscreen 'none'; payment + 'none'; + Pragma: + - no-cache + Referrer-Policy: + - no-referrer + Server: + - nginx + Vary: + - Origin + - Access-Control-Request-Method + - Access-Control-Request-Headers + X-Content-Type-Options: + - nosniff + X-GDC-TRACE-ID: *id001 + X-XSS-Protection: + - '0' + set-cookie: + - SPRING_REDIRECT_URI=; Max-Age=0; Expires=Mon, 14 Oct 2024 06:20:11 GMT; + Path=/; HTTPOnly; SameSite=Lax + body: + string: + data: [] + links: + self: http://localhost:3000/api/v1/entities/users/demo/apiTokens?page=0&size=500 + next: http://localhost:3000/api/v1/entities/users/demo/apiTokens?page=1&size=500 + - request: + method: POST + uri: http://localhost:3000/api/v1/entities/users/demo/apiTokens + body: + data: + id: test_token + type: apiToken + headers: + Accept: + - application/vnd.gooddata.api+json + Accept-Encoding: + - br, gzip, deflate + Content-Type: + - application/vnd.gooddata.api+json + X-GDC-VALIDATE-RELATIONS: + - 'true' + X-Requested-With: + - XMLHttpRequest + response: + status: + code: 201 + message: Created + headers: + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Expose-Headers: + - Content-Disposition, Content-Length, Content-Range, Set-Cookie + Cache-Control: + - no-cache, no-store, max-age=0, must-revalidate + Connection: + - keep-alive + Content-Length: + - '231' + Content-Security-Policy: + - 'default-src ''self'' *.wistia.com *.wistia.net; script-src ''self'' ''unsafe-inline'' + ''unsafe-eval'' *.wistia.com *.wistia.net *.hsforms.net *.hsforms.com + src.litix.io matomo.anywhere.gooddata.com *.jquery.com unpkg.com cdnjs.cloudflare.com; + img-src * data: blob:; style-src ''self'' ''unsafe-inline'' fonts.googleapis.com + cdn.jsdelivr.net fast.fonts.net; font-src ''self'' data: fonts.gstatic.com + *.alicdn.com *.wistia.com cdn.jsdelivr.net info.gooddata.com; frame-src + ''self'' *.hsforms.net *.hsforms.com; object-src ''none''; worker-src + ''self'' blob:; child-src blob:; connect-src ''self'' *.tiles.mapbox.com + *.mapbox.com *.litix.io *.wistia.com *.hsforms.net *.hsforms.com embedwistia-a.akamaihd.net + matomo.anywhere.gooddata.com; media-src ''self'' blob: data: *.wistia.com + *.wistia.net embedwistia-a.akamaihd.net' + Content-Type: + - application/vnd.gooddata.api+json + DATE: *id001 + Expires: + - '0' + GoodData-Deployment: + - aio + Permission-Policy: + - geolocation 'none'; midi 'none'; sync-xhr 'none'; microphone 'none'; camera + 'none'; magnetometer 'none'; gyroscope 'none'; fullscreen 'none'; payment + 'none'; + Pragma: + - no-cache + Referrer-Policy: + - no-referrer + Server: + - nginx + Vary: + - Origin + - Access-Control-Request-Method + - Access-Control-Request-Headers + X-Content-Type-Options: + - nosniff + X-GDC-TRACE-ID: *id001 + X-XSS-Protection: + - '0' + set-cookie: + - SPRING_REDIRECT_URI=; Max-Age=0; Expires=Mon, 14 Oct 2024 06:20:11 GMT; + Path=/; HTTPOnly; SameSite=Lax + body: + string: + data: + id: test_token + type: apiToken + attributes: + bearerToken: ZGVtbzp0ZXN0X3Rva2VuOmFxcGgrNzVWZW53c3E2RHJqQnFYMnZsaUc5NUFMQjFC + links: + self: http://localhost:3000/api/v1/entities/users/demo/apiTokens/test_token + - request: + method: GET + uri: http://localhost:3000/api/v1/entities/users/demo/apiTokens/test_token + body: null + headers: + Accept: + - application/vnd.gooddata.api+json + Accept-Encoding: + - br, gzip, deflate + X-GDC-VALIDATE-RELATIONS: + - 'true' + X-Requested-With: + - XMLHttpRequest + response: + status: + code: 200 + message: OK + headers: + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Expose-Headers: + - Content-Disposition, Content-Length, Content-Range, Set-Cookie + Cache-Control: + - no-cache, no-store, max-age=0, must-revalidate + Connection: + - keep-alive + Content-Length: + - '151' + Content-Security-Policy: + - 'default-src ''self'' *.wistia.com *.wistia.net; script-src ''self'' ''unsafe-inline'' + ''unsafe-eval'' *.wistia.com *.wistia.net *.hsforms.net *.hsforms.com + src.litix.io matomo.anywhere.gooddata.com *.jquery.com unpkg.com cdnjs.cloudflare.com; + img-src * data: blob:; style-src ''self'' ''unsafe-inline'' fonts.googleapis.com + cdn.jsdelivr.net fast.fonts.net; font-src ''self'' data: fonts.gstatic.com + *.alicdn.com *.wistia.com cdn.jsdelivr.net info.gooddata.com; frame-src + ''self'' *.hsforms.net *.hsforms.com; object-src ''none''; worker-src + ''self'' blob:; child-src blob:; connect-src ''self'' *.tiles.mapbox.com + *.mapbox.com *.litix.io *.wistia.com *.hsforms.net *.hsforms.com embedwistia-a.akamaihd.net + matomo.anywhere.gooddata.com; media-src ''self'' blob: data: *.wistia.com + *.wistia.net embedwistia-a.akamaihd.net' + Content-Type: + - application/vnd.gooddata.api+json + DATE: *id001 + Expires: + - '0' + GoodData-Deployment: + - aio + Permission-Policy: + - geolocation 'none'; midi 'none'; sync-xhr 'none'; microphone 'none'; camera + 'none'; magnetometer 'none'; gyroscope 'none'; fullscreen 'none'; payment + 'none'; + Pragma: + - no-cache + Referrer-Policy: + - no-referrer + Server: + - nginx + Vary: + - Origin + - Access-Control-Request-Method + - Access-Control-Request-Headers + X-Content-Type-Options: + - nosniff + X-GDC-TRACE-ID: *id001 + X-XSS-Protection: + - '0' + set-cookie: + - SPRING_REDIRECT_URI=; Max-Age=0; Expires=Mon, 14 Oct 2024 06:20:11 GMT; + Path=/; HTTPOnly; SameSite=Lax + body: + string: + data: + id: test_token + type: apiToken + attributes: {} + links: + self: http://localhost:3000/api/v1/entities/users/demo/apiTokens/test_token + - request: + method: GET + uri: http://localhost:3000/api/v1/entities/users/demo/apiTokens?page=0&size=500 + body: null + headers: + Accept: + - application/vnd.gooddata.api+json + Accept-Encoding: + - br, gzip, deflate + X-GDC-VALIDATE-RELATIONS: + - 'true' + X-Requested-With: + - XMLHttpRequest + response: + status: + code: 200 + message: OK + headers: + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Expose-Headers: + - Content-Disposition, Content-Length, Content-Range, Set-Cookie + Cache-Control: + - no-cache, no-store, max-age=0, must-revalidate + Connection: + - keep-alive + Content-Length: + - '331' + Content-Security-Policy: + - 'default-src ''self'' *.wistia.com *.wistia.net; script-src ''self'' ''unsafe-inline'' + ''unsafe-eval'' *.wistia.com *.wistia.net *.hsforms.net *.hsforms.com + src.litix.io matomo.anywhere.gooddata.com *.jquery.com unpkg.com cdnjs.cloudflare.com; + img-src * data: blob:; style-src ''self'' ''unsafe-inline'' fonts.googleapis.com + cdn.jsdelivr.net fast.fonts.net; font-src ''self'' data: fonts.gstatic.com + *.alicdn.com *.wistia.com cdn.jsdelivr.net info.gooddata.com; frame-src + ''self'' *.hsforms.net *.hsforms.com; object-src ''none''; worker-src + ''self'' blob:; child-src blob:; connect-src ''self'' *.tiles.mapbox.com + *.mapbox.com *.litix.io *.wistia.com *.hsforms.net *.hsforms.com embedwistia-a.akamaihd.net + matomo.anywhere.gooddata.com; media-src ''self'' blob: data: *.wistia.com + *.wistia.net embedwistia-a.akamaihd.net' + Content-Type: + - application/vnd.gooddata.api+json + DATE: *id001 + Expires: + - '0' + GoodData-Deployment: + - aio + Permission-Policy: + - geolocation 'none'; midi 'none'; sync-xhr 'none'; microphone 'none'; camera + 'none'; magnetometer 'none'; gyroscope 'none'; fullscreen 'none'; payment + 'none'; + Pragma: + - no-cache + Referrer-Policy: + - no-referrer + Server: + - nginx + Vary: + - Origin + - Access-Control-Request-Method + - Access-Control-Request-Headers + X-Content-Type-Options: + - nosniff + X-GDC-TRACE-ID: *id001 + X-XSS-Protection: + - '0' + set-cookie: + - SPRING_REDIRECT_URI=; Max-Age=0; Expires=Mon, 14 Oct 2024 06:20:11 GMT; + Path=/; HTTPOnly; SameSite=Lax + body: + string: + data: + - id: test_token + type: apiToken + attributes: {} + links: + self: http://localhost:3000/api/v1/entities/users/demo/apiTokens/test_token + links: + self: http://localhost:3000/api/v1/entities/users/demo/apiTokens?page=0&size=500 + next: http://localhost:3000/api/v1/entities/users/demo/apiTokens?page=1&size=500 + - request: + method: DELETE + uri: http://localhost:3000/api/v1/entities/users/demo/apiTokens/test_token + body: null + headers: + Accept-Encoding: + - br, gzip, deflate + X-GDC-VALIDATE-RELATIONS: + - 'true' + X-Requested-With: + - XMLHttpRequest + response: + status: + code: 204 + message: No Content + headers: + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Expose-Headers: + - Content-Disposition, Content-Length, Content-Range, Set-Cookie + Cache-Control: + - no-cache, no-store, max-age=0, must-revalidate + Connection: + - keep-alive + Content-Security-Policy: + - 'default-src ''self'' *.wistia.com *.wistia.net; script-src ''self'' ''unsafe-inline'' + ''unsafe-eval'' *.wistia.com *.wistia.net *.hsforms.net *.hsforms.com + src.litix.io matomo.anywhere.gooddata.com *.jquery.com unpkg.com cdnjs.cloudflare.com; + img-src * data: blob:; style-src ''self'' ''unsafe-inline'' fonts.googleapis.com + cdn.jsdelivr.net fast.fonts.net; font-src ''self'' data: fonts.gstatic.com + *.alicdn.com *.wistia.com cdn.jsdelivr.net info.gooddata.com; frame-src + ''self'' *.hsforms.net *.hsforms.com; object-src ''none''; worker-src + ''self'' blob:; child-src blob:; connect-src ''self'' *.tiles.mapbox.com + *.mapbox.com *.litix.io *.wistia.com *.hsforms.net *.hsforms.com embedwistia-a.akamaihd.net + matomo.anywhere.gooddata.com; media-src ''self'' blob: data: *.wistia.com + *.wistia.net embedwistia-a.akamaihd.net' + DATE: *id001 + Expires: + - '0' + GoodData-Deployment: + - aio + Permission-Policy: + - geolocation 'none'; midi 'none'; sync-xhr 'none'; microphone 'none'; camera + 'none'; magnetometer 'none'; gyroscope 'none'; fullscreen 'none'; payment + 'none'; + Pragma: + - no-cache + Referrer-Policy: + - no-referrer + Server: + - nginx + Vary: + - Origin + - Access-Control-Request-Method + - Access-Control-Request-Headers + X-Content-Type-Options: + - nosniff + X-GDC-TRACE-ID: *id001 + X-XSS-Protection: + - '0' + set-cookie: + - SPRING_REDIRECT_URI=; Max-Age=0; Expires=Mon, 14 Oct 2024 06:20:11 GMT; + Path=/; HTTPOnly; SameSite=Lax + body: + string: '' + - request: + method: GET + uri: http://localhost:3000/api/v1/entities/users/demo/apiTokens?page=0&size=500 + body: null + headers: + Accept: + - application/vnd.gooddata.api+json + Accept-Encoding: + - br, gzip, deflate + X-GDC-VALIDATE-RELATIONS: + - 'true' + X-Requested-With: + - XMLHttpRequest + response: + status: + code: 200 + message: OK + headers: + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Expose-Headers: + - Content-Disposition, Content-Length, Content-Range, Set-Cookie + Cache-Control: + - no-cache, no-store, max-age=0, must-revalidate + Connection: + - keep-alive + Content-Length: + - '189' + Content-Security-Policy: + - 'default-src ''self'' *.wistia.com *.wistia.net; script-src ''self'' ''unsafe-inline'' + ''unsafe-eval'' *.wistia.com *.wistia.net *.hsforms.net *.hsforms.com + src.litix.io matomo.anywhere.gooddata.com *.jquery.com unpkg.com cdnjs.cloudflare.com; + img-src * data: blob:; style-src ''self'' ''unsafe-inline'' fonts.googleapis.com + cdn.jsdelivr.net fast.fonts.net; font-src ''self'' data: fonts.gstatic.com + *.alicdn.com *.wistia.com cdn.jsdelivr.net info.gooddata.com; frame-src + ''self'' *.hsforms.net *.hsforms.com; object-src ''none''; worker-src + ''self'' blob:; child-src blob:; connect-src ''self'' *.tiles.mapbox.com + *.mapbox.com *.litix.io *.wistia.com *.hsforms.net *.hsforms.com embedwistia-a.akamaihd.net + matomo.anywhere.gooddata.com; media-src ''self'' blob: data: *.wistia.com + *.wistia.net embedwistia-a.akamaihd.net' + Content-Type: + - application/vnd.gooddata.api+json + DATE: *id001 + Expires: + - '0' + GoodData-Deployment: + - aio + Permission-Policy: + - geolocation 'none'; midi 'none'; sync-xhr 'none'; microphone 'none'; camera + 'none'; magnetometer 'none'; gyroscope 'none'; fullscreen 'none'; payment + 'none'; + Pragma: + - no-cache + Referrer-Policy: + - no-referrer + Server: + - nginx + Vary: + - Origin + - Access-Control-Request-Method + - Access-Control-Request-Headers + X-Content-Type-Options: + - nosniff + X-GDC-TRACE-ID: *id001 + X-XSS-Protection: + - '0' + set-cookie: + - SPRING_REDIRECT_URI=; Max-Age=0; Expires=Mon, 14 Oct 2024 06:20:11 GMT; + Path=/; HTTPOnly; SameSite=Lax + body: + string: + data: [] + links: + self: http://localhost:3000/api/v1/entities/users/demo/apiTokens?page=0&size=500 + next: http://localhost:3000/api/v1/entities/users/demo/apiTokens?page=1&size=500 diff --git a/gooddata-sdk/tests/catalog/test_catalog_user_service.py b/gooddata-sdk/tests/catalog/test_catalog_user_service.py index 285d2df73..26ecf303d 100644 --- a/gooddata-sdk/tests/catalog/test_catalog_user_service.py +++ b/gooddata-sdk/tests/catalog/test_catalog_user_service.py @@ -638,6 +638,32 @@ def test_revoke_permissions_bulk(test_config): sdk.catalog_user.manage_user_permissions(user_id, origin_permissions) +@gd_vcr.use_cassette(str(_fixtures_dir / "test_api_tokens.yaml")) +def test_api_tokens(test_config): + sdk = GoodDataSdk.create(host_=test_config["host"], token_=test_config["token"]) + tokens = sdk.catalog_user.list_user_api_tokens(test_config["demo_user"]) + assert len(tokens) == 0 + + token_id = "test_token" + try: + token = sdk.catalog_user.create_user_api_token(test_config["demo_user"], "test_token") + assert token.id == token_id + assert token.bearer_token is not None + + token = sdk.catalog_user.get_user_api_token(test_config["demo_user"], token_id) + assert token.id == token_id + assert token.bearer_token is None + + tokens = sdk.catalog_user.list_user_api_tokens(test_config["demo_user"]) + assert len(tokens) == 1 + assert tokens[0].id == token_id + assert tokens[0].bearer_token is None + finally: + sdk.catalog_user.delete_user_api_token(test_config["demo_user"], token_id) + tokens = sdk.catalog_user.list_user_api_tokens(test_config["demo_user"]) + assert len(tokens) == 0 + + # Help functions