From fda4554e0d2dec2b6f4888e7823bca0143bb2114 Mon Sep 17 00:00:00 2001 From: Giriharan Date: Wed, 30 Oct 2024 15:55:02 +0530 Subject: [PATCH 1/7] feat: add client for minimal implementation of auth --- docs/api_reference.rst | 1 + docs/api_reference/auth.rst | 15 ++ docs/getting_started.rst | 26 +++ examples/auth/get_workspace_id.py | 25 +++ nisystemlink/clients/auth/__init__.py | 3 + nisystemlink/clients/auth/_auth_client.py | 35 ++++ nisystemlink/clients/auth/models/__init__.py | 3 + .../clients/auth/models/_auth_models.py | 171 ++++++++++++++++++ tests/integration/auth/test_auth_client.py | 21 +++ 9 files changed, 300 insertions(+) create mode 100644 docs/api_reference/auth.rst create mode 100644 examples/auth/get_workspace_id.py create mode 100644 nisystemlink/clients/auth/__init__.py create mode 100644 nisystemlink/clients/auth/_auth_client.py create mode 100644 nisystemlink/clients/auth/models/__init__.py create mode 100644 nisystemlink/clients/auth/models/_auth_models.py create mode 100644 tests/integration/auth/test_auth_client.py diff --git a/docs/api_reference.rst b/docs/api_reference.rst index b9181186..55cfc47b 100644 --- a/docs/api_reference.rst +++ b/docs/api_reference.rst @@ -13,6 +13,7 @@ API Reference api_reference/dataframe api_reference/spec api_reference/file + api_reference/auth Indices and tables ------------------ diff --git a/docs/api_reference/auth.rst b/docs/api_reference/auth.rst new file mode 100644 index 00000000..7d857f8a --- /dev/null +++ b/docs/api_reference/auth.rst @@ -0,0 +1,15 @@ +.. _api_tag_page: + +nisystemlink.clients.auth +====================== + +.. autoclass:: nisystemlink.clients.auth.AuthClient + :exclude-members: __init__ + + .. automethod:: __init__ + .. automethod:: authenticate + + +.. automodule:: nisystemlink.clients.auth.models + :members: + :imported-members: \ No newline at end of file diff --git a/docs/getting_started.rst b/docs/getting_started.rst index 0efe9358..1e052a98 100644 --- a/docs/getting_started.rst +++ b/docs/getting_started.rst @@ -182,5 +182,31 @@ Examples Get the metadata of a File using its Id and download it. .. literalinclude:: ../examples/file/download_file.py + :language: python + :linenos: + + +Auth API +------- + +Overview +~~~~~~~~ + +The :class:`.AuthClient` class is the primary entry point of the Auth API. + +When constructing a :class:`.AuthClient`, you can pass an +:class:`.HttpConfiguration` (like one retrieved from the +:class:`.HttpConfigurationManager`), or let :class:`.AuthClient` use the +default connection. The default connection depends on your environment. + +With a :class:`.AuthClient` object, you can: + +* Get the information about the caller. + +Examples +~~~~~~~~ + +Get the workspace id for the workspace name. +.. literalinclude:: ../examples/auth/get_workspace_id.py :language: python :linenos: \ No newline at end of file diff --git a/examples/auth/get_workspace_id.py b/examples/auth/get_workspace_id.py new file mode 100644 index 00000000..ea9fd2fe --- /dev/null +++ b/examples/auth/get_workspace_id.py @@ -0,0 +1,25 @@ +"""Functionality of getting workspace ID using the workspace name.""" + +from nisystemlink.clients.auth import AuthClient +from nisystemlink.clients.core import ApiException, HttpConfiguration + +server_url = "" # SystemLink API URL +server_api_key = "" # SystemLink API key +workspace_name = "" # Systemlink workspace name + +auth_client = AuthClient(HttpConfiguration(server_uri=server_url, api_key=server_api_key)) + +try: + caller_info = auth_client.authenticate() + workspaces_info = caller_info.workspaces + + if workspaces_info: + for workspace_info in workspaces_info: + if workspace_info.name == workspace_name: + print(workspace_info.id) + +except ApiException as exp: + print(exp) + +except Exception as exp: + print(exp) diff --git a/nisystemlink/clients/auth/__init__.py b/nisystemlink/clients/auth/__init__.py new file mode 100644 index 00000000..5d80edbd --- /dev/null +++ b/nisystemlink/clients/auth/__init__.py @@ -0,0 +1,3 @@ +from ._auth_client import AuthClient + +# flake8: noqa diff --git a/nisystemlink/clients/auth/_auth_client.py b/nisystemlink/clients/auth/_auth_client.py new file mode 100644 index 00000000..803c007b --- /dev/null +++ b/nisystemlink/clients/auth/_auth_client.py @@ -0,0 +1,35 @@ +"""Implementation of AuthClient.""" + +from typing import Optional + +from nisystemlink.clients import core +from nisystemlink.clients.core._uplink._base_client import BaseClient +from nisystemlink.clients.core._uplink._methods import get + +from . import models + + +class AuthClient(BaseClient): + """A set of methods to access the APIs of SystemLink Auth Client.""" + + def __init__(self, configuration: Optional[core.HttpConfiguration] = None): + """Initialize an instance. + + Args: + configuration: Defines the web server to connect to and information about + how to connect. If not provided, an instance of + :class:`JupyterHttpConfiguration ` # noqa: W505 + is used. + + Raises: + ApiException: if unable to communicate with the Auth Service. + """ + if configuration is None: + configuration = core.JupyterHttpConfiguration() + + super().__init__(configuration, base_path="/niauth/v1/") + + @get("auth") + def authenticate(self) -> models.AuthInfo: + """Authenticates the given x-ni-api-key and returns information about the caller.""" + ... diff --git a/nisystemlink/clients/auth/models/__init__.py b/nisystemlink/clients/auth/models/__init__.py new file mode 100644 index 00000000..978c2bde --- /dev/null +++ b/nisystemlink/clients/auth/models/__init__.py @@ -0,0 +1,3 @@ +from ._auth_models import AuthInfo + +# flake8: noqa diff --git a/nisystemlink/clients/auth/models/_auth_models.py b/nisystemlink/clients/auth/models/_auth_models.py new file mode 100644 index 00000000..0e960730 --- /dev/null +++ b/nisystemlink/clients/auth/models/_auth_models.py @@ -0,0 +1,171 @@ +"""Models utilized for Auth in SystemLink.""" + +from __future__ import annotations + +from datetime import datetime +from enum import Enum +from typing import Any, Dict, List, Optional + +from nisystemlink.clients.core._uplink._json_model import JsonModel +from pydantic import Field + + +class AuthStatement(JsonModel): + """Auth Statement information.""" + + actions: Optional[List[str]] = None + """A list of actions the user is allowed to perform.""" + resource: Optional[List[str]] = None + """A list of resources the user is allowed to access.""" + workspace: Optional[str] = Field(None, example="workspace-id") + """The workspace the user is allowed to access.""" + + +class AuthPolicy(JsonModel): + """Auth Policy information.""" + + statements: Optional[List[AuthStatement]] = None + """A list of statements defining the actions the user can perform on a resource in a workspace. + """ + + +class Statement(JsonModel): + """Statement information.""" + + actions: Optional[List[str]] = None + """A list of actions the user is allowed to perform.""" + resource: Optional[List[str]] = None + """A list of resources the user is allowed to access.""" + workspace: Optional[str] = Field(None, example="workspace-id") + """The workspace the user is allowed to access.""" + description: Optional[str] = None + """A description for this statement.""" + + +class Policy(JsonModel): + """Policy information.""" + + id: Optional[str] = Field(None, example="policy-id") + """The unique id.""" + name: Optional[str] = Field(None, example="policy-name") + """The policies's name.""" + type: Optional[str] = Field(None, example="role") + """The type of the policy.""" + built_in: Optional[bool] = Field(None, alias="builtIn", example=True) + """Whether the policy is built-in.""" + user_id: Optional[str] = Field(None, alias="userId", example="user-id") + """The user id.""" + created: Optional[datetime] = Field(None, example="2019-12-02T15:31:45.379Z") + """The created timestamp.""" + updated: Optional[datetime] = Field(None, example="2019-12-02T15:31:45.379Z") + """The last updated timestamp.""" + deleted: Optional[bool] = Field(None, example=True) + """Whether the policy is deleted or not.""" + properties: Optional[Dict[str, str]] = Field(None, example={"key1": "value1"}) + """A map of key value properties.""" + statements: Optional[List[Statement]] = None + """A list of statements defining the actions the user can perform on a resource in a workspace. + """ + template_id: Optional[str] = Field( + None, alias="templateId", example="policy-template-id" + ) + """ + The id of the policy template. Only set if the policy has been created based on a template and + does not contain inline statements. + """ + workspace: Optional[str] = Field(None, example="workspace-id") + """ + The workspace the policy template applies to. Only set if the policy has been created based on a + template and does not contain inline statements. + """ + + +class Status(Enum): + """Enumeration to represent different status of user's registration.""" + + PENDING = "pending" + ACTIVE = "active" + + +class User(JsonModel): + """User information.""" + + id: Optional[str] = Field(None, example="user-id") + """The unique id.""" + first_name: Optional[str] = Field( + None, alias="firstName", example="user-first-name" + ) + """The user's first name.""" + last_name: Optional[str] = Field(None, alias="lastName", example="user-last-name") + """The user's last name.""" + email: Optional[str] = Field(None, example="example@email.com") + """The user's email.""" + phone: Optional[str] = Field(None, example="555-555-5555") + """The user's contact phone number.""" + niua_id: Optional[str] = Field(None, alias="niuaId", example="example@email.com") + """The external id (niuaId, SID, login name).""" + login: Optional[str] = None + """ + The login name of the user. This the "username" or equivalent entered when + the user authenticates with the identity provider. + """ + accepted_to_s: Optional[bool] = Field(None, alias="acceptedToS", example=True) + """(deprecated) Whether the user accepted the terms of service.""" + properties: Optional[Dict[str, str]] = Field(None, example={"key1": "value1"}) + """A map of key value properties.""" + keywords: Optional[List[str]] = None + """A list of keywords associated with the user.""" + created: Optional[datetime] = Field(None, example="2019-12-02T15:31:45.379Z") + """The created timestamp.""" + updated: Optional[datetime] = Field(None, example="2019-12-02T15:31:45.379Z") + """The last updated timestamp.""" + org_id: Optional[str] = Field(None, alias="orgId", example="org-id") + """The id of the organization.""" + policies: Optional[List[str]] = None + """A list of policy ids to reference existing policies.""" + status: Optional[Status] = Field(None, example="active") + """The status of the users' registration.""" + entitlements: Optional[Any] = None + """(deprecated) Features to which the user is entitled within the application.""" + + +class Org(JsonModel): + """User's Organization information.""" + + id: Optional[str] = Field(None, example="org-id") + """The unique id.""" + name: Optional[str] = Field(None, example="org-name") + """The name of the organization.""" + owner_id: Optional[str] = Field(None, alias="ownerId", example="user-id") + """The userId of the organization owner.""" + + +class Workspace(JsonModel): + """Workspace information.""" + + id: Optional[str] = Field(None, example="workspace-id") + """The unique id.""" + name: Optional[str] = Field(None, example="workspace-name") + """The workspace name.""" + enabled: Optional[bool] = Field(None, example=True) + """Whether the workspace is enabled or not.""" + default: Optional[bool] = Field(None, example=True) + """ + Whether the workspace is the default. The default workspace is used when callers omit a \ +workspace id. + """ + + +class AuthInfo(JsonModel): + """Information about the authenticated caller.""" + + user: Optional[User] + """Details of authenticated caller.""" + org: Optional[Org] + """Organization of authenticated caller.""" + workspaces: Optional[List[Workspace]] + """List of workspaces the authenticated caller has access.""" + policies: Optional[List[AuthPolicy]] + """List of policies for the authenticated caller.""" + properties: Optional[Dict[str, str]] = Field(None, example={"key1": "value1"}) + """A map of key value properties.""" diff --git a/tests/integration/auth/test_auth_client.py b/tests/integration/auth/test_auth_client.py new file mode 100644 index 00000000..82273fbf --- /dev/null +++ b/tests/integration/auth/test_auth_client.py @@ -0,0 +1,21 @@ +"""Integration tests for AuthClient.""" + +import pytest +from nisystemlink.clients.auth import AuthClient + + +@pytest.fixture(scope="class") +def client(enterprise_config) -> AuthClient: + """Fixture to create a AuthClient instance.""" + return AuthClient(enterprise_config) + + +@pytest.mark.enterprise +@pytest.mark.integration +class TestAuthClient: + """A set of test methods to test SystemLink Auth API.""" + + def test__authenticate_returns(self, client: AuthClient): + """Test the case of getting caller information with SystemLink Credentials.""" + response = client.authenticate() + assert response is not None From d5deaaf2ef8c7e8456af062214e9fc138824c5bb Mon Sep 17 00:00:00 2001 From: Giriharan Date: Wed, 30 Oct 2024 16:48:39 +0530 Subject: [PATCH 2/7] feat: add utilities to get workspace ID --- examples/auth/get_workspace_id.py | 19 ++++++++++++---- nisystemlink/clients/auth/models/__init__.py | 2 +- nisystemlink/clients/auth/utilities.py | 24 ++++++++++++++++++++ 3 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 nisystemlink/clients/auth/utilities.py diff --git a/examples/auth/get_workspace_id.py b/examples/auth/get_workspace_id.py index ea9fd2fe..e5ee5edb 100644 --- a/examples/auth/get_workspace_id.py +++ b/examples/auth/get_workspace_id.py @@ -1,22 +1,31 @@ -"""Functionality of getting workspace ID using the workspace name.""" +"""Example of getting workspace ID.""" from nisystemlink.clients.auth import AuthClient +from nisystemlink.clients.auth.utilities import get_workspace_id from nisystemlink.clients.core import ApiException, HttpConfiguration + server_url = "" # SystemLink API URL server_api_key = "" # SystemLink API key workspace_name = "" # Systemlink workspace name -auth_client = AuthClient(HttpConfiguration(server_uri=server_url, api_key=server_api_key)) +auth_client = AuthClient( + HttpConfiguration(server_uri=server_url, api_key=server_api_key) +) try: caller_info = auth_client.authenticate() workspaces_info = caller_info.workspaces + workspace_id = None if workspaces_info: - for workspace_info in workspaces_info: - if workspace_info.name == workspace_name: - print(workspace_info.id) + workspace_id = get_workspace_id( + workspaces_info=workspaces_info, + workspace_name=workspace_name, + ) + + if workspace_id: + print(f"Workspace ID: {workspace_id}") except ApiException as exp: print(exp) diff --git a/nisystemlink/clients/auth/models/__init__.py b/nisystemlink/clients/auth/models/__init__.py index 978c2bde..f20f7714 100644 --- a/nisystemlink/clients/auth/models/__init__.py +++ b/nisystemlink/clients/auth/models/__init__.py @@ -1,3 +1,3 @@ -from ._auth_models import AuthInfo +from ._auth_models import AuthInfo, Workspace # flake8: noqa diff --git a/nisystemlink/clients/auth/utilities.py b/nisystemlink/clients/auth/utilities.py new file mode 100644 index 00000000..a25f11b1 --- /dev/null +++ b/nisystemlink/clients/auth/utilities.py @@ -0,0 +1,24 @@ +"""Utilities for Auth Client.""" + +from typing import List, Union + +from nisystemlink.clients.auth.models import Workspace + + +def get_workspace_id( + workspaces_info: List[Workspace], + workspace_name: str, +) -> Union[str, None]: + """Get workspace id from the list of workspace info using `workspace_name`. + + Args: + workspaces_info (List[workspace_info]): List of workspace info. + workspace_name (str): Workspace name. + + Returns: + Union[str, None]: Workspace ID of the `workspace_name`. + """ + for workspace_info in workspaces_info: + if workspace_info.name == workspace_name and workspace_info.id: + return workspace_info.id + return None From 88410de85b87b3dd0725cc8eeec936bfcf23fce6 Mon Sep 17 00:00:00 2001 From: Giriharan Date: Wed, 30 Oct 2024 19:07:27 +0530 Subject: [PATCH 3/7] fix: Update test case name --- tests/integration/auth/test_auth_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/auth/test_auth_client.py b/tests/integration/auth/test_auth_client.py index 82273fbf..e74027ee 100644 --- a/tests/integration/auth/test_auth_client.py +++ b/tests/integration/auth/test_auth_client.py @@ -15,7 +15,7 @@ def client(enterprise_config) -> AuthClient: class TestAuthClient: """A set of test methods to test SystemLink Auth API.""" - def test__authenticate_returns(self, client: AuthClient): + def test__authenticate__succeeds(self, client: AuthClient): """Test the case of getting caller information with SystemLink Credentials.""" response = client.authenticate() assert response is not None From a8a1ac7b2794b52193cabbfbc6817538fd058264 Mon Sep 17 00:00:00 2001 From: Giriharan Date: Tue, 5 Nov 2024 12:37:45 +0530 Subject: [PATCH 4/7] fix: address PR comments --- examples/auth/get_workspace_id.py | 15 +- nisystemlink/clients/auth/_auth_client.py | 8 +- nisystemlink/clients/auth/models/__init__.py | 3 +- .../clients/auth/models/_auth_info.py | 24 +++ .../clients/auth/models/_auth_models.py | 171 ------------------ .../clients/auth/models/_auth_policy.py | 31 ++++ nisystemlink/clients/auth/models/_user.py | 65 +++++++ .../clients/auth/models/_workspace.py | 21 +++ nisystemlink/clients/auth/utilities.py | 24 +-- tests/integration/auth/test_auth_client.py | 6 +- 10 files changed, 168 insertions(+), 200 deletions(-) create mode 100644 nisystemlink/clients/auth/models/_auth_info.py delete mode 100644 nisystemlink/clients/auth/models/_auth_models.py create mode 100644 nisystemlink/clients/auth/models/_auth_policy.py create mode 100644 nisystemlink/clients/auth/models/_user.py create mode 100644 nisystemlink/clients/auth/models/_workspace.py diff --git a/examples/auth/get_workspace_id.py b/examples/auth/get_workspace_id.py index e5ee5edb..8c4efb32 100644 --- a/examples/auth/get_workspace_id.py +++ b/examples/auth/get_workspace_id.py @@ -1,7 +1,7 @@ """Example of getting workspace ID.""" from nisystemlink.clients.auth import AuthClient -from nisystemlink.clients.auth.utilities import get_workspace_id +from nisystemlink.clients.auth.utilities import get_workspace_by_name from nisystemlink.clients.core import ApiException, HttpConfiguration @@ -14,15 +14,16 @@ ) try: - caller_info = auth_client.authenticate() - workspaces_info = caller_info.workspaces + caller_info = auth_client.get_auth_info() + workspaces = caller_info.workspaces if caller_info.workspaces else None workspace_id = None - if workspaces_info: - workspace_id = get_workspace_id( - workspaces_info=workspaces_info, - workspace_name=workspace_name, + if workspaces: + workspace_info = get_workspace_by_name( + workspaces=workspaces, + name=workspace_name, ) + workspace_id = workspace_info.id if workspace_info else None if workspace_id: print(f"Workspace ID: {workspace_id}") diff --git a/nisystemlink/clients/auth/_auth_client.py b/nisystemlink/clients/auth/_auth_client.py index 803c007b..adeb6148 100644 --- a/nisystemlink/clients/auth/_auth_client.py +++ b/nisystemlink/clients/auth/_auth_client.py @@ -10,8 +10,6 @@ class AuthClient(BaseClient): - """A set of methods to access the APIs of SystemLink Auth Client.""" - def __init__(self, configuration: Optional[core.HttpConfiguration] = None): """Initialize an instance. @@ -25,11 +23,11 @@ def __init__(self, configuration: Optional[core.HttpConfiguration] = None): ApiException: if unable to communicate with the Auth Service. """ if configuration is None: - configuration = core.JupyterHttpConfiguration() + configuration = core.HttpConfigurationManager.get_configuration() super().__init__(configuration, base_path="/niauth/v1/") @get("auth") - def authenticate(self) -> models.AuthInfo: - """Authenticates the given x-ni-api-key and returns information about the caller.""" + def get_auth_info(self) -> models.AuthInfo: + """Gets information about the authenticated API Key.""" ... diff --git a/nisystemlink/clients/auth/models/__init__.py b/nisystemlink/clients/auth/models/__init__.py index f20f7714..ea571818 100644 --- a/nisystemlink/clients/auth/models/__init__.py +++ b/nisystemlink/clients/auth/models/__init__.py @@ -1,3 +1,4 @@ -from ._auth_models import AuthInfo, Workspace +from ._auth_info import AuthInfo +from ._workspace import Workspace # flake8: noqa diff --git a/nisystemlink/clients/auth/models/_auth_info.py b/nisystemlink/clients/auth/models/_auth_info.py new file mode 100644 index 00000000..8bf3d83d --- /dev/null +++ b/nisystemlink/clients/auth/models/_auth_info.py @@ -0,0 +1,24 @@ +from __future__ import annotations + +from typing import Dict, List, Optional + +from nisystemlink.clients.core._uplink._json_model import JsonModel + +from ._auth_policy import AuthPolicy +from ._user import Org, User +from ._workspace import Workspace + + +class AuthInfo(JsonModel): + """Information about the authenticated caller.""" + + user: Optional[User] + """Details of authenticated caller.""" + org: Optional[Org] + """Organization of authenticated caller.""" + workspaces: Optional[List[Workspace]] + """List of workspaces the authenticated caller has access.""" + policies: Optional[List[AuthPolicy]] + """List of policies for the authenticated caller.""" + properties: Optional[Dict[str, str]] = None + """A map of key value properties.""" diff --git a/nisystemlink/clients/auth/models/_auth_models.py b/nisystemlink/clients/auth/models/_auth_models.py deleted file mode 100644 index 0e960730..00000000 --- a/nisystemlink/clients/auth/models/_auth_models.py +++ /dev/null @@ -1,171 +0,0 @@ -"""Models utilized for Auth in SystemLink.""" - -from __future__ import annotations - -from datetime import datetime -from enum import Enum -from typing import Any, Dict, List, Optional - -from nisystemlink.clients.core._uplink._json_model import JsonModel -from pydantic import Field - - -class AuthStatement(JsonModel): - """Auth Statement information.""" - - actions: Optional[List[str]] = None - """A list of actions the user is allowed to perform.""" - resource: Optional[List[str]] = None - """A list of resources the user is allowed to access.""" - workspace: Optional[str] = Field(None, example="workspace-id") - """The workspace the user is allowed to access.""" - - -class AuthPolicy(JsonModel): - """Auth Policy information.""" - - statements: Optional[List[AuthStatement]] = None - """A list of statements defining the actions the user can perform on a resource in a workspace. - """ - - -class Statement(JsonModel): - """Statement information.""" - - actions: Optional[List[str]] = None - """A list of actions the user is allowed to perform.""" - resource: Optional[List[str]] = None - """A list of resources the user is allowed to access.""" - workspace: Optional[str] = Field(None, example="workspace-id") - """The workspace the user is allowed to access.""" - description: Optional[str] = None - """A description for this statement.""" - - -class Policy(JsonModel): - """Policy information.""" - - id: Optional[str] = Field(None, example="policy-id") - """The unique id.""" - name: Optional[str] = Field(None, example="policy-name") - """The policies's name.""" - type: Optional[str] = Field(None, example="role") - """The type of the policy.""" - built_in: Optional[bool] = Field(None, alias="builtIn", example=True) - """Whether the policy is built-in.""" - user_id: Optional[str] = Field(None, alias="userId", example="user-id") - """The user id.""" - created: Optional[datetime] = Field(None, example="2019-12-02T15:31:45.379Z") - """The created timestamp.""" - updated: Optional[datetime] = Field(None, example="2019-12-02T15:31:45.379Z") - """The last updated timestamp.""" - deleted: Optional[bool] = Field(None, example=True) - """Whether the policy is deleted or not.""" - properties: Optional[Dict[str, str]] = Field(None, example={"key1": "value1"}) - """A map of key value properties.""" - statements: Optional[List[Statement]] = None - """A list of statements defining the actions the user can perform on a resource in a workspace. - """ - template_id: Optional[str] = Field( - None, alias="templateId", example="policy-template-id" - ) - """ - The id of the policy template. Only set if the policy has been created based on a template and - does not contain inline statements. - """ - workspace: Optional[str] = Field(None, example="workspace-id") - """ - The workspace the policy template applies to. Only set if the policy has been created based on a - template and does not contain inline statements. - """ - - -class Status(Enum): - """Enumeration to represent different status of user's registration.""" - - PENDING = "pending" - ACTIVE = "active" - - -class User(JsonModel): - """User information.""" - - id: Optional[str] = Field(None, example="user-id") - """The unique id.""" - first_name: Optional[str] = Field( - None, alias="firstName", example="user-first-name" - ) - """The user's first name.""" - last_name: Optional[str] = Field(None, alias="lastName", example="user-last-name") - """The user's last name.""" - email: Optional[str] = Field(None, example="example@email.com") - """The user's email.""" - phone: Optional[str] = Field(None, example="555-555-5555") - """The user's contact phone number.""" - niua_id: Optional[str] = Field(None, alias="niuaId", example="example@email.com") - """The external id (niuaId, SID, login name).""" - login: Optional[str] = None - """ - The login name of the user. This the "username" or equivalent entered when - the user authenticates with the identity provider. - """ - accepted_to_s: Optional[bool] = Field(None, alias="acceptedToS", example=True) - """(deprecated) Whether the user accepted the terms of service.""" - properties: Optional[Dict[str, str]] = Field(None, example={"key1": "value1"}) - """A map of key value properties.""" - keywords: Optional[List[str]] = None - """A list of keywords associated with the user.""" - created: Optional[datetime] = Field(None, example="2019-12-02T15:31:45.379Z") - """The created timestamp.""" - updated: Optional[datetime] = Field(None, example="2019-12-02T15:31:45.379Z") - """The last updated timestamp.""" - org_id: Optional[str] = Field(None, alias="orgId", example="org-id") - """The id of the organization.""" - policies: Optional[List[str]] = None - """A list of policy ids to reference existing policies.""" - status: Optional[Status] = Field(None, example="active") - """The status of the users' registration.""" - entitlements: Optional[Any] = None - """(deprecated) Features to which the user is entitled within the application.""" - - -class Org(JsonModel): - """User's Organization information.""" - - id: Optional[str] = Field(None, example="org-id") - """The unique id.""" - name: Optional[str] = Field(None, example="org-name") - """The name of the organization.""" - owner_id: Optional[str] = Field(None, alias="ownerId", example="user-id") - """The userId of the organization owner.""" - - -class Workspace(JsonModel): - """Workspace information.""" - - id: Optional[str] = Field(None, example="workspace-id") - """The unique id.""" - name: Optional[str] = Field(None, example="workspace-name") - """The workspace name.""" - enabled: Optional[bool] = Field(None, example=True) - """Whether the workspace is enabled or not.""" - default: Optional[bool] = Field(None, example=True) - """ - Whether the workspace is the default. The default workspace is used when callers omit a \ -workspace id. - """ - - -class AuthInfo(JsonModel): - """Information about the authenticated caller.""" - - user: Optional[User] - """Details of authenticated caller.""" - org: Optional[Org] - """Organization of authenticated caller.""" - workspaces: Optional[List[Workspace]] - """List of workspaces the authenticated caller has access.""" - policies: Optional[List[AuthPolicy]] - """List of policies for the authenticated caller.""" - properties: Optional[Dict[str, str]] = Field(None, example={"key1": "value1"}) - """A map of key value properties.""" diff --git a/nisystemlink/clients/auth/models/_auth_policy.py b/nisystemlink/clients/auth/models/_auth_policy.py new file mode 100644 index 00000000..096de0b1 --- /dev/null +++ b/nisystemlink/clients/auth/models/_auth_policy.py @@ -0,0 +1,31 @@ +from __future__ import annotations + +from typing import List, Optional + +from nisystemlink.clients.core._uplink._json_model import JsonModel + + +class AuthStatement(JsonModel): + """Auth Statement information.""" + + actions: Optional[List[str]] = None + """A list of actions the user is allowed to perform.""" + resource: Optional[List[str]] = None + """A list of resources the user is allowed to access.""" + workspace: Optional[str] = None + """The workspace the user is allowed to access.""" + + +class Statement(AuthStatement): + """Statement information.""" + + description: Optional[str] = None + """A description for this statement.""" + + +class AuthPolicy(JsonModel): + """Auth Policy information.""" + + statements: Optional[List[AuthStatement]] = None + """A list of statements defining the actions the user can perform on a resource in a workspace. + """ diff --git a/nisystemlink/clients/auth/models/_user.py b/nisystemlink/clients/auth/models/_user.py new file mode 100644 index 00000000..ac14c5d4 --- /dev/null +++ b/nisystemlink/clients/auth/models/_user.py @@ -0,0 +1,65 @@ +from __future__ import annotations + +from datetime import datetime +from enum import Enum +from typing import Any, Dict, List, Optional + +from nisystemlink.clients.core._uplink._json_model import JsonModel + + +class Status(Enum): + """Enumeration to represent different status of user's registration.""" + + PENDING = "pending" + ACTIVE = "active" + + +class Org(JsonModel): + """User's Organization information.""" + + id: Optional[str] = None + """The unique id.""" + name: Optional[str] = None + """The name of the organization.""" + owner_id: Optional[str] = None + """The userId of the organization owner.""" + + +class User(JsonModel): + """User information.""" + + id: Optional[str] = None + """The unique id.""" + first_name: Optional[str] = None + """The user's first name.""" + last_name: Optional[str] = None + """The user's last name.""" + email: Optional[str] = None + """The user's email.""" + phone: Optional[str] = None + """The user's contact phone number.""" + niua_id: Optional[str] = None + """The external id (niuaId, SID, login name).""" + login: Optional[str] = None + """ + The login name of the user. This the "username" or equivalent entered when + the user authenticates with the identity provider. + """ + accepted_to_s: Optional[bool] = None + """(deprecated) Whether the user accepted the terms of service.""" + properties: Optional[Dict[str, str]] = None + """A map of key value properties.""" + keywords: Optional[List[str]] = None + """A list of keywords associated with the user.""" + created: Optional[datetime] = None + """The created timestamp.""" + updated: Optional[datetime] = None + """The last updated timestamp.""" + org_id: Optional[str] = None + """The id of the organization.""" + policies: Optional[List[str]] = None + """A list of policy ids to reference existing policies.""" + status: Optional[Status] = None + """The status of the users' registration.""" + entitlements: Optional[Any] = None + """(deprecated) Features to which the user is entitled within the application.""" diff --git a/nisystemlink/clients/auth/models/_workspace.py b/nisystemlink/clients/auth/models/_workspace.py new file mode 100644 index 00000000..2927322d --- /dev/null +++ b/nisystemlink/clients/auth/models/_workspace.py @@ -0,0 +1,21 @@ +from __future__ import annotations + +from typing import Optional + +from nisystemlink.clients.core._uplink._json_model import JsonModel + + +class Workspace(JsonModel): + """Workspace information.""" + + id: Optional[str] = None + """The unique id.""" + name: Optional[str] = None + """The workspace name.""" + enabled: Optional[bool] = None + """Whether the workspace is enabled or not.""" + default: Optional[bool] = None + """ + Whether the workspace is the default. The default workspace is used when callers omit a \ +workspace id. + """ diff --git a/nisystemlink/clients/auth/utilities.py b/nisystemlink/clients/auth/utilities.py index a25f11b1..ea94b4dc 100644 --- a/nisystemlink/clients/auth/utilities.py +++ b/nisystemlink/clients/auth/utilities.py @@ -1,24 +1,24 @@ """Utilities for Auth Client.""" -from typing import List, Union +from typing import List, Optional from nisystemlink.clients.auth.models import Workspace -def get_workspace_id( - workspaces_info: List[Workspace], - workspace_name: str, -) -> Union[str, None]: - """Get workspace id from the list of workspace info using `workspace_name`. +def get_workspace_by_name( + workspaces: List[Workspace], + name: str, +) -> Optional[Workspace]: + """Get workspace information from the list of workspace using `name`. Args: - workspaces_info (List[workspace_info]): List of workspace info. - workspace_name (str): Workspace name. + workspaces (List[Workspace]): List of workspace. + name (str): Workspace name. Returns: - Union[str, None]: Workspace ID of the `workspace_name`. + Optional[Workspace]: Workspace info of the `name`. """ - for workspace_info in workspaces_info: - if workspace_info.name == workspace_name and workspace_info.id: - return workspace_info.id + for workspace in workspaces: + if workspace.name == name and workspace.id: + return workspace return None diff --git a/tests/integration/auth/test_auth_client.py b/tests/integration/auth/test_auth_client.py index e74027ee..c3b02183 100644 --- a/tests/integration/auth/test_auth_client.py +++ b/tests/integration/auth/test_auth_client.py @@ -13,9 +13,7 @@ def client(enterprise_config) -> AuthClient: @pytest.mark.enterprise @pytest.mark.integration class TestAuthClient: - """A set of test methods to test SystemLink Auth API.""" - - def test__authenticate__succeeds(self, client: AuthClient): + def test__get_auth_info__succeeds(self, client: AuthClient): """Test the case of getting caller information with SystemLink Credentials.""" - response = client.authenticate() + response = client.get_auth_info() assert response is not None From dfb0fabe45b0f0587b9b6d63bf251f40252399da Mon Sep 17 00:00:00 2001 From: Giriharan Date: Tue, 5 Nov 2024 13:50:04 +0530 Subject: [PATCH 5/7] fix: address self review comments --- examples/auth/get_workspace_id.py | 2 +- nisystemlink/clients/auth/utilities.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/auth/get_workspace_id.py b/examples/auth/get_workspace_id.py index 8c4efb32..c2536302 100644 --- a/examples/auth/get_workspace_id.py +++ b/examples/auth/get_workspace_id.py @@ -23,7 +23,7 @@ workspaces=workspaces, name=workspace_name, ) - workspace_id = workspace_info.id if workspace_info else None + workspace_id = workspace_info.id if workspace_id: print(f"Workspace ID: {workspace_id}") diff --git a/nisystemlink/clients/auth/utilities.py b/nisystemlink/clients/auth/utilities.py index ea94b4dc..36bc10aa 100644 --- a/nisystemlink/clients/auth/utilities.py +++ b/nisystemlink/clients/auth/utilities.py @@ -16,7 +16,7 @@ def get_workspace_by_name( name (str): Workspace name. Returns: - Optional[Workspace]: Workspace info of the `name`. + Optional[Workspace]: Workspace information. """ for workspace in workspaces: if workspace.name == name and workspace.id: From ad7e3f099d607e2f240d542831f346f69e903316 Mon Sep 17 00:00:00 2001 From: Giriharan Date: Tue, 5 Nov 2024 14:09:00 +0530 Subject: [PATCH 6/7] fix: issue due to invalid type in example --- examples/auth/get_workspace_id.py | 6 +- .../clients/auth/models/_auth_policy.py | 15 ++++- nisystemlink/clients/auth/models/_user.py | 55 ++++++++++++++++--- 3 files changed, 64 insertions(+), 12 deletions(-) diff --git a/examples/auth/get_workspace_id.py b/examples/auth/get_workspace_id.py index c2536302..ed6a44a1 100644 --- a/examples/auth/get_workspace_id.py +++ b/examples/auth/get_workspace_id.py @@ -9,21 +9,25 @@ server_api_key = "" # SystemLink API key workspace_name = "" # Systemlink workspace name +# Provide the valid API key and API URL for client initialization. auth_client = AuthClient( HttpConfiguration(server_uri=server_url, api_key=server_api_key) ) +# Getting workspace ID. try: + # Get the caller details for workspaces information. caller_info = auth_client.get_auth_info() workspaces = caller_info.workspaces if caller_info.workspaces else None workspace_id = None + # Get the required workspace information for getting ID. if workspaces: workspace_info = get_workspace_by_name( workspaces=workspaces, name=workspace_name, ) - workspace_id = workspace_info.id + workspace_id = workspace_info.id if workspace_info else None if workspace_id: print(f"Workspace ID: {workspace_id}") diff --git a/nisystemlink/clients/auth/models/_auth_policy.py b/nisystemlink/clients/auth/models/_auth_policy.py index 096de0b1..51495362 100644 --- a/nisystemlink/clients/auth/models/_auth_policy.py +++ b/nisystemlink/clients/auth/models/_auth_policy.py @@ -9,11 +9,20 @@ class AuthStatement(JsonModel): """Auth Statement information.""" actions: Optional[List[str]] = None - """A list of actions the user is allowed to perform.""" + """A list of actions the user is allowed to perform. + + example: notebookexecution:Query + """ resource: Optional[List[str]] = None - """A list of resources the user is allowed to access.""" + """A list of resources the user is allowed to access. + + example: Notebook + """ workspace: Optional[str] = None - """The workspace the user is allowed to access.""" + """The workspace the user is allowed to access. + + example: 5afb2ce3741fe11d88838cc9 + """ class Statement(AuthStatement): diff --git a/nisystemlink/clients/auth/models/_user.py b/nisystemlink/clients/auth/models/_user.py index ac14c5d4..83171436 100644 --- a/nisystemlink/clients/auth/models/_user.py +++ b/nisystemlink/clients/auth/models/_user.py @@ -29,37 +29,76 @@ class User(JsonModel): """User information.""" id: Optional[str] = None - """The unique id.""" + """The unique id. + + example: "47d-47c7-8dd1-70f63de3583f" + """ + first_name: Optional[str] = None """The user's first name.""" + last_name: Optional[str] = None """The user's last name.""" + email: Optional[str] = None - """The user's email.""" + """The user's email. + + example: example@email.com + """ + phone: Optional[str] = None - """The user's contact phone number.""" + """The user's contact phone number. + + example: 555-555-5555 + """ + niua_id: Optional[str] = None - """The external id (niuaId, SID, login name).""" + """The external id (niuaId, SID, login name). + + example: example@email.com + """ + login: Optional[str] = None """ The login name of the user. This the "username" or equivalent entered when the user authenticates with the identity provider. """ + accepted_to_s: Optional[bool] = None """(deprecated) Whether the user accepted the terms of service.""" + properties: Optional[Dict[str, str]] = None - """A map of key value properties.""" + """A map of key value properties. + + example: { "key1": "value1" } + """ + keywords: Optional[List[str]] = None """A list of keywords associated with the user.""" + created: Optional[datetime] = None - """The created timestamp.""" + """The created timestamp. + + example: 2019-12-02T15:31:45.379Z + """ + updated: Optional[datetime] = None - """The last updated timestamp.""" + """The last updated timestamp. + + example: 2019-12-02T15:31:45.379Z + """ + org_id: Optional[str] = None - """The id of the organization.""" + """The id of the organization. + + example: "47d-47c7-8dd1-70f63de3435f" + """ + policies: Optional[List[str]] = None """A list of policy ids to reference existing policies.""" + status: Optional[Status] = None """The status of the users' registration.""" + entitlements: Optional[Any] = None """(deprecated) Features to which the user is entitled within the application.""" From bfd9308a0cf3e683733da7425499abafb4fcaa98 Mon Sep 17 00:00:00 2001 From: Giriharan Date: Thu, 7 Nov 2024 12:27:52 +0530 Subject: [PATCH 7/7] fix: self review comments --- examples/auth/get_workspace_id.py | 5 +++-- nisystemlink/clients/auth/_auth_client.py | 9 ++++++++- nisystemlink/clients/auth/models/_auth_info.py | 8 ++++---- nisystemlink/clients/auth/utilities/__init__.py | 5 +++++ .../{utilities.py => utilities/_get_workspace_info.py} | 2 +- 5 files changed, 21 insertions(+), 8 deletions(-) create mode 100644 nisystemlink/clients/auth/utilities/__init__.py rename nisystemlink/clients/auth/{utilities.py => utilities/_get_workspace_info.py} (94%) diff --git a/examples/auth/get_workspace_id.py b/examples/auth/get_workspace_id.py index ed6a44a1..3e6ef70d 100644 --- a/examples/auth/get_workspace_id.py +++ b/examples/auth/get_workspace_id.py @@ -1,10 +1,11 @@ """Example of getting workspace ID.""" from nisystemlink.clients.auth import AuthClient -from nisystemlink.clients.auth.utilities import get_workspace_by_name +from nisystemlink.clients.auth.utilities._get_workspace_info import ( + get_workspace_by_name, +) from nisystemlink.clients.core import ApiException, HttpConfiguration - server_url = "" # SystemLink API URL server_api_key = "" # SystemLink API key workspace_name = "" # Systemlink workspace name diff --git a/nisystemlink/clients/auth/_auth_client.py b/nisystemlink/clients/auth/_auth_client.py index adeb6148..2ecaccf2 100644 --- a/nisystemlink/clients/auth/_auth_client.py +++ b/nisystemlink/clients/auth/_auth_client.py @@ -29,5 +29,12 @@ def __init__(self, configuration: Optional[core.HttpConfiguration] = None): @get("auth") def get_auth_info(self) -> models.AuthInfo: - """Gets information about the authenticated API Key.""" + """Gets information about the authenticated API Key. + + Returns: + models.AuthInfo: Information about the caller. + + Raises: + ApiException: if unable to communicate with the Auth Service. + """ ... diff --git a/nisystemlink/clients/auth/models/_auth_info.py b/nisystemlink/clients/auth/models/_auth_info.py index 8bf3d83d..c63525e8 100644 --- a/nisystemlink/clients/auth/models/_auth_info.py +++ b/nisystemlink/clients/auth/models/_auth_info.py @@ -12,13 +12,13 @@ class AuthInfo(JsonModel): """Information about the authenticated caller.""" - user: Optional[User] + user: Optional[User] = None """Details of authenticated caller.""" - org: Optional[Org] + org: Optional[Org] = None """Organization of authenticated caller.""" - workspaces: Optional[List[Workspace]] + workspaces: Optional[List[Workspace]] = None """List of workspaces the authenticated caller has access.""" - policies: Optional[List[AuthPolicy]] + policies: Optional[List[AuthPolicy]] = None """List of policies for the authenticated caller.""" properties: Optional[Dict[str, str]] = None """A map of key value properties.""" diff --git a/nisystemlink/clients/auth/utilities/__init__.py b/nisystemlink/clients/auth/utilities/__init__.py new file mode 100644 index 00000000..7fa9b593 --- /dev/null +++ b/nisystemlink/clients/auth/utilities/__init__.py @@ -0,0 +1,5 @@ +from nisystemlink.clients.auth.utilities._get_workspace_info import ( + get_workspace_by_name, +) + +# flake8: noqa diff --git a/nisystemlink/clients/auth/utilities.py b/nisystemlink/clients/auth/utilities/_get_workspace_info.py similarity index 94% rename from nisystemlink/clients/auth/utilities.py rename to nisystemlink/clients/auth/utilities/_get_workspace_info.py index 36bc10aa..827a4824 100644 --- a/nisystemlink/clients/auth/utilities.py +++ b/nisystemlink/clients/auth/utilities/_get_workspace_info.py @@ -1,4 +1,4 @@ -"""Utilities for Auth Client.""" +"""Get workspace information.""" from typing import List, Optional