diff --git a/eng/.docsettings.yml b/eng/.docsettings.yml index a732c246e727..d0dba16dd0bb 100644 --- a/eng/.docsettings.yml +++ b/eng/.docsettings.yml @@ -124,6 +124,7 @@ known_content_issues: - ['sdk/core/azure-mgmt-nspkg/README.rst', 'nspkg and common'] - ['sdk/core/azure-nspkg/README.rst', 'nspkg and common'] - ['sdk/keyvault/azure-keyvault-nspkg/README.md', 'nspkg and common'] + - ['sdk/mixedreality/azure-mixedreality-nspkg/README.md', 'nspkg and common'] - ['sdk/search/azure-search-nspkg/README.md', 'nspkg and common'] - ['sdk/storage/azure-storage-blob/samples/README.md', 'nspkg and common'] - ['sdk/storage/azure-storage-file-datalake/samples/README.md', 'nspkg and common'] diff --git a/sdk/mixedreality/azure-mixedreality-authentication/CHANGELOG.md b/sdk/mixedreality/azure-mixedreality-authentication/CHANGELOG.md new file mode 100644 index 000000000000..11533387e830 --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/CHANGELOG.md @@ -0,0 +1,5 @@ +# Release History + +## 1.0.0b1 (Unreleased) + +- Initial release. diff --git a/sdk/mixedreality/azure-mixedreality-authentication/LICENSE.txt b/sdk/mixedreality/azure-mixedreality-authentication/LICENSE.txt new file mode 100644 index 000000000000..0313a903d76c --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 Microsoft + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/sdk/mixedreality/azure-mixedreality-authentication/MANIFEST.in b/sdk/mixedreality/azure-mixedreality-authentication/MANIFEST.in new file mode 100644 index 000000000000..b69eaab9edd0 --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/MANIFEST.in @@ -0,0 +1,6 @@ +include *.md +include azure/__init__.py +include azure/mixedreality/__init__.py +include LICENSE.txt +recursive-include tests *.py +recursive-include samples *.py *.md \ No newline at end of file diff --git a/sdk/mixedreality/azure-mixedreality-authentication/README.md b/sdk/mixedreality/azure-mixedreality-authentication/README.md new file mode 100644 index 000000000000..ca0845d344a6 --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/README.md @@ -0,0 +1,199 @@ +[![Build Status](https://dev.azure.com/azure-sdk/public/_apis/build/status/azure-sdk-for-python.client?branchName=master)](https://dev.azure.com/azure-sdk/public/_build/latest?definitionId=46?branchName=master) + +# Azure Mixed Reality Authentication Package client library for Python + +Mixed Reality services, like Azure Spatial Anchors, Azure Remote Rendering, and others, use the Mixed Reality security +token service (STS) for authentication. This package supports exchanging Mixed Reality account credentials for an access +token from the STS that can be used to access Mixed Reality services. + +![Mixed Reality service authentication diagram](https://docs.microsoft.com/azure/spatial-anchors/concepts/media/spatial-anchors-authentication-overview.png) + +# Getting started + +## Currently supported environments + +This package has been tested with Python 2.7, 3.5, 3.6, 3.7, 3.8, and 3.9. + +## Prerequisites + +- An [Azure subscription][azure_sub]. +- You must have an account with an [Azure Mixed Reality service](https://azure.microsoft.com/topic/mixed-reality/): + - [Azure Remote Rendering](https://docs.microsoft.com/azure/remote-rendering/) + - [Azure Spatial Anchors](https://docs.microsoft.com/azure/spatial-anchors/) +- Familiarity with the authentication and credential concepts from the [Azure Identity library][azure_identity]. +- Python 2.7, or 3.5 or later is required to use this package. + +## Install the package + +Install the Azure Mixed Reality Authentication SDK. + +```bash +pip install --pre azure-mixedreality-authentication +``` + +## Create and authenticate a `MixedRealityStsClient` + +To create a client object to request an access token for a Mixed Reality service, you will need the `account identifier` +and `account domain` of your Mixed Reality service resource and a `credential`. + +Mixed Reality services support a few different forms of authentication: + +- Account Key authentication + - Account keys enable you to get started quickly with using Mixed Reality services. But before you deploy your application + to production, we recommend that you update your app to use Azure AD authentication. +- Azure Active Directory (AD) token authentication + - If you're building an enterprise application and your company is using Azure AD as its identity system, you can use + user-based Azure AD authentication in your app. You then grant access to your Mixed Reality accounts by using your + existing Azure AD security groups. You can also grant access directly to users in your organization. + - Otherwise, we recommend that you obtain Azure AD tokens from a web service that supports your app. We recommend this + method for production applications because it allows you to avoid embedding the credentials for access to a Mixed + Reality service in your client application. + +See [here][register_aad_app] for detailed instructions and information. + +### Using account key authentication + +Use the [Azure Portal][azure_portal] to browse to your Mixed Reality service resource and retrieve an `account key`. + +Once you have an account key, you can use the `AzureKeyCredential` class to authenticate the client as follows: + +```python +from azure.core.credentials import AzureKeyCredential +from azure.mixedreality.authentication import MixedRealityStsClient + +account_id = "" +account_domain = "" +account_key = "" +key_credential = AzureKeyCredential(account_key) + +client = MixedRealityStsClient(account_id, account_domain, key_credential) +``` + +> Note: Account key authentication is **not recommended** for production applications. + +### Using an Azure Active Directory Credential + +Account key authentication is used in most of the examples, but you can also authenticate with Azure Active Directory +using the [Azure Identity library][azure_identity]. This is the recommended method for production applications. To use +the [DefaultAzureCredential][defaultazurecredential] provider shown below, or other credential providers provided with +the Azure SDK, please install the `@azure/identity` package: + +You will also need to [register a new AAD application][register_aad_app] and grant access to your Mixed Reality resource +by assigning the appropriate role for your Mixed Reality service to your service principal. + +```python +from azure.identity import DefaultAzureCredential +from azure.mixedreality.authentication import MixedRealityStsClient + +account_id = "" +account_domain = "" +default_credential = DefaultAzureCredential() + +client = MixedRealityStsClient(account_id, account_domain, default_credential) +``` + +# Key concepts + +## MixedRealityStsClient + +The `MixedRealityStsClient` is the client library used to access the Mixed Reality STS to get an access token. An access +token can be retrieved by calling `get_token()` on an `MixedRealityStsClient` instance. + +Tokens obtained from the Mixed Reality STS have a lifetime of **24 hours**. + +### Token result value + +The return value for a successful call to `get_token` is an `azure.core.credentials.AccessToken`. + +See the authentication examples [above](#authenticate-the-client) or [Azure Identity][azure_identity] for more complex +authentication scenarios. + +## Retrieve an access token synchronously + +```python +from azure.core.credentials import AzureKeyCredential +from azure.mixedreality.authentication import MixedRealityStsClient + +account_id = "" +account_domain = "" +account_key = "" +key_credential = AzureKeyCredential(account_key) + +client = MixedRealityStsClient(account_id, account_domain, key_credential) + +token = client.get_token() +``` + +## Retrieve an access token asynchronously + +```python +from azure.core.credentials import AzureKeyCredential +from azure.mixedreality.authentication.aio import MixedRealityStsClient + +account_id = "" +account_domain = "" +account_key = "" +key_credential = AzureKeyCredential(account_key) + +client = MixedRealityStsClient(account_id, account_domain, key_credential) + +token = await client.get_token() +``` + +# Examples + +These are code samples that show common scenario operations with the Azure Mixed Reality Authentication client library. +The async versions of the samples (the python sample files appended with `_async`) show asynchronous operations, +and require Python 3.5 or later. +Before running the sample code, refer to Prerequisites + +to create a resource, then set some Environment Variables + +```bash +set MIXEDREALITY_ACCOUNT_DOMAIN="" +set MIXEDREALITY_ACCOUNT_ID="" +set MIXEDREALITY_ACCOUNT_KEY="" + +pip install azure-mixedreality-authentication + +python samples\client_sample.py +python samples\client_sample_async.py +``` + +# Troubleshooting + +The [troubleshooting](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/identity/azure-identity#troubleshooting) +section for Azure Identity can be helpful when troubleshooting authentication issues. + +# Next steps + +## Mixed Reality client libraries + +- Coming soon + +## Contributing + +This project welcomes contributions and suggestions. Most contributions require you to agree to a +Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us +the rights to use your contribution. For details, visit https://cla.microsoft.com. + +When you submit a pull request, a CLA-bot will automatically determine whether you need to provide +a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions +provided by the bot. You will only need to do this once across all repos using our CLA. + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). +For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or +contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. + +If you'd like to contribute to this library, please read the +[contributing guide](https://github.com/Azure/azure-sdk-for-python/blob/master/CONTRIBUTING.md) to learn more about how to +build and test the code. + +![Impressions](https://azure-sdk-impressions.azurewebsites.net/api/impressions/azure-sdk-for-python%2Fsdk%2Ftemplate%2Fazure-template%2FREADME.png) + +[azure_cli]: https://docs.microsoft.com/cli/azure +[azure_sub]: https://azure.microsoft.com/free/ +[azure_portal]: https://portal.azure.com +[azure_identity]: https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/identity/azure-identity +[register_aad_app]: https://docs.microsoft.com/azure/spatial-anchors/concepts/authentication +[defaultazurecredential]: https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/identity/azure-identity#defaultazurecredential diff --git a/sdk/mixedreality/azure-mixedreality-authentication/azure/__init__.py b/sdk/mixedreality/azure-mixedreality-authentication/azure/__init__.py new file mode 100644 index 000000000000..431ed648086a --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/azure/__init__.py @@ -0,0 +1,7 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +__path__ = __import__('pkgutil').extend_path(__path__, __name__) # type: ignore diff --git a/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/__init__.py b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/__init__.py new file mode 100644 index 000000000000..431ed648086a --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/__init__.py @@ -0,0 +1,7 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +__path__ = __import__('pkgutil').extend_path(__path__, __name__) # type: ignore diff --git a/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/__init__.py b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/__init__.py new file mode 100644 index 000000000000..2996cdaaeb4c --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/__init__.py @@ -0,0 +1,11 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +from ._version import VERSION +from ._client import MixedRealityStsClient + +__version__ = VERSION +__all__ = ['MixedRealityStsClient'] diff --git a/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_client.py b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_client.py new file mode 100644 index 000000000000..919a99cb55a1 --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_client.py @@ -0,0 +1,113 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +from typing import TYPE_CHECKING + +try: + from urllib.parse import urlparse +except ImportError: + from urlparse import urlparse # type: ignore + +from azure.core.credentials import AzureKeyCredential +from azure.core.tracing.decorator import distributed_trace +from azure.core.pipeline.policies import BearerTokenCredentialPolicy + +from ._generated import MixedRealityStsRestClient +from ._generated.models import TokenRequestOptions +from ._version import SDK_MONIKER +from ._shared.authentication_endpoint import construct_endpoint_url +from ._shared.mixedreality_account_key_credential import MixedRealityAccountKeyCredential +from ._utils import convert_to_access_token, generate_cv_base + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import Any, Union + from azure.core.credentials import TokenCredential + from azure.core.credentials import AccessToken + + +class MixedRealityStsClient(object): + """ A client to interact with the Mixed Reality STS service. + + :param str account_id: + The Mixed Reality service account identifier. + :param str account_domain: + The Mixed Reality service account domain. + :param Union[TokenCredential, AzureKeyCredential] credential: + The credential used to access the Mixed Reality service. + :keyword str custom_endpoint_url: + Override the Mixed Reality STS service endpoint. + """ + + def __init__(self, account_id, account_domain, credential, **kwargs): + # type: (str, str, Union[TokenCredential, AzureKeyCredential], Any) -> None + if not account_id: + raise ValueError("account_id must be a non-empty string.") + + if not account_domain: + raise ValueError("account_domain must be a non-empty string.") + + if not credential: + raise ValueError("credential can not be None.") + + self._account_id = account_id + self._account_domain = account_domain + + if isinstance(credential, AzureKeyCredential): + credential = MixedRealityAccountKeyCredential(account_id, credential) + + self._credential = credential + + endpoint_url = kwargs.pop('custom_endpoint_url', construct_endpoint_url(account_domain)) + + try: + if not endpoint_url.lower().startswith('http'): + endpoint_url = "https://" + endpoint_url + except AttributeError: + raise ValueError("Host URL must be a string.") + + parsed_url = urlparse(endpoint_url.rstrip('/')) + if not parsed_url.netloc: + raise ValueError("Invalid URL: {}".format(endpoint_url)) + + self._endpoint_url = endpoint_url + + authentication_policy = BearerTokenCredentialPolicy(credential, endpoint_url + '/.default') + + self._client = MixedRealityStsRestClient( + base_url=endpoint_url, + authentication_policy=authentication_policy, + sdk_moniker=SDK_MONIKER, + **kwargs) + + @distributed_trace + def get_token(self, **kwargs): + # type: (Any) -> AccessToken + """ + Retrieve a token from the STS service for the specified account identifier asynchronously. + :return: Instance of azure.core.credentials.AccessToken - token and expiry date of it + :rtype: ~azure.core.credentials.AccessToken + """ + token_request_options = TokenRequestOptions() + token_request_options.client_request_id = generate_cv_base() + + response = self._client.get_token( + self._account_id, + token_request_options=token_request_options, + **kwargs) + return convert_to_access_token(response) + + def close(self): + # type: () -> None + self._client.close() + + def __enter__(self): + # type: () -> MixedRealityStsClient + self._client.__enter__() # pylint:disable=no-member + return self + + def __exit__(self, *args): + # type: (*Any) -> None + self._client.__exit__(*args) # pylint:disable=no-member diff --git a/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/__init__.py b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/__init__.py new file mode 100644 index 000000000000..86838c6b621e --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/__init__.py @@ -0,0 +1,16 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from ._mixed_reality_sts_rest_client import MixedRealityStsRestClient +__all__ = ['MixedRealityStsRestClient'] + +try: + from ._patch import patch_sdk # type: ignore + patch_sdk() +except ImportError: + pass diff --git a/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/_configuration.py b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/_configuration.py new file mode 100644 index 000000000000..9a762cc1fa5c --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/_configuration.py @@ -0,0 +1,51 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import TYPE_CHECKING + +from azure.core.configuration import Configuration +from azure.core.pipeline import policies + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import Any, Optional + +VERSION = "unknown" + +class MixedRealityStsRestClientConfiguration(Configuration): + """Configuration for MixedRealityStsRestClient. + + Note that all parameters used to create this instance are saved as instance + attributes. + + """ + + def __init__( + self, + **kwargs # type: Any + ): + # type: (...) -> None + super(MixedRealityStsRestClientConfiguration, self).__init__(**kwargs) + + kwargs.setdefault('sdk_moniker', 'mixedrealitystsrestclient/{}'.format(VERSION)) + self._configure(**kwargs) + + def _configure( + self, + **kwargs # type: Any + ): + # type: (...) -> None + self.user_agent_policy = kwargs.get('user_agent_policy') or policies.UserAgentPolicy(**kwargs) + self.headers_policy = kwargs.get('headers_policy') or policies.HeadersPolicy(**kwargs) + self.proxy_policy = kwargs.get('proxy_policy') or policies.ProxyPolicy(**kwargs) + self.logging_policy = kwargs.get('logging_policy') or policies.NetworkTraceLoggingPolicy(**kwargs) + self.http_logging_policy = kwargs.get('http_logging_policy') or policies.HttpLoggingPolicy(**kwargs) + self.retry_policy = kwargs.get('retry_policy') or policies.RetryPolicy(**kwargs) + self.custom_hook_policy = kwargs.get('custom_hook_policy') or policies.CustomHookPolicy(**kwargs) + self.redirect_policy = kwargs.get('redirect_policy') or policies.RedirectPolicy(**kwargs) + self.authentication_policy = kwargs.get('authentication_policy') diff --git a/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/_mixed_reality_sts_rest_client.py b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/_mixed_reality_sts_rest_client.py new file mode 100644 index 000000000000..53973ea0933f --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/_mixed_reality_sts_rest_client.py @@ -0,0 +1,57 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import TYPE_CHECKING + +from azure.core import PipelineClient +from msrest import Deserializer, Serializer + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import Any, Optional + +from ._configuration import MixedRealityStsRestClientConfiguration +from .operations import MixedRealityStsRestClientOperationsMixin +from . import models + + +class MixedRealityStsRestClient(MixedRealityStsRestClientOperationsMixin): + """Definition for the Mixed Reality Cloud STS service APIs. + + :param str base_url: Service URL + """ + + def __init__( + self, + base_url=None, # type: Optional[str] + **kwargs # type: Any + ): + # type: (...) -> None + if not base_url: + base_url = 'https://sts.mixedreality.azure.com' + self._config = MixedRealityStsRestClientConfiguration(**kwargs) + self._client = PipelineClient(base_url=base_url, config=self._config, **kwargs) + + client_models = {k: v for k, v in models.__dict__.items() if isinstance(v, type)} + self._serialize = Serializer(client_models) + self._serialize.client_side_validation = False + self._deserialize = Deserializer(client_models) + + + def close(self): + # type: () -> None + self._client.close() + + def __enter__(self): + # type: () -> MixedRealityStsRestClient + self._client.__enter__() + return self + + def __exit__(self, *exc_details): + # type: (Any) -> None + self._client.__exit__(*exc_details) diff --git a/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/aio/__init__.py b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/aio/__init__.py new file mode 100644 index 000000000000..17251fcb2dbf --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/aio/__init__.py @@ -0,0 +1,10 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from ._mixed_reality_sts_rest_client import MixedRealityStsRestClient +__all__ = ['MixedRealityStsRestClient'] diff --git a/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/aio/_configuration.py b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/aio/_configuration.py new file mode 100644 index 000000000000..6045303ca6c1 --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/aio/_configuration.py @@ -0,0 +1,45 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import Any, Optional + +from azure.core.configuration import Configuration +from azure.core.pipeline import policies + +VERSION = "unknown" + +class MixedRealityStsRestClientConfiguration(Configuration): + """Configuration for MixedRealityStsRestClient. + + Note that all parameters used to create this instance are saved as instance + attributes. + + """ + + def __init__( + self, + **kwargs: Any + ) -> None: + super(MixedRealityStsRestClientConfiguration, self).__init__(**kwargs) + + kwargs.setdefault('sdk_moniker', 'mixedrealitystsrestclient/{}'.format(VERSION)) + self._configure(**kwargs) + + def _configure( + self, + **kwargs: Any + ) -> None: + self.user_agent_policy = kwargs.get('user_agent_policy') or policies.UserAgentPolicy(**kwargs) + self.headers_policy = kwargs.get('headers_policy') or policies.HeadersPolicy(**kwargs) + self.proxy_policy = kwargs.get('proxy_policy') or policies.ProxyPolicy(**kwargs) + self.logging_policy = kwargs.get('logging_policy') or policies.NetworkTraceLoggingPolicy(**kwargs) + self.http_logging_policy = kwargs.get('http_logging_policy') or policies.HttpLoggingPolicy(**kwargs) + self.retry_policy = kwargs.get('retry_policy') or policies.AsyncRetryPolicy(**kwargs) + self.custom_hook_policy = kwargs.get('custom_hook_policy') or policies.CustomHookPolicy(**kwargs) + self.redirect_policy = kwargs.get('redirect_policy') or policies.AsyncRedirectPolicy(**kwargs) + self.authentication_policy = kwargs.get('authentication_policy') diff --git a/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/aio/_mixed_reality_sts_rest_client.py b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/aio/_mixed_reality_sts_rest_client.py new file mode 100644 index 000000000000..593e47542967 --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/aio/_mixed_reality_sts_rest_client.py @@ -0,0 +1,49 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import Any, Optional + +from azure.core import AsyncPipelineClient +from msrest import Deserializer, Serializer + +from ._configuration import MixedRealityStsRestClientConfiguration +from .operations import MixedRealityStsRestClientOperationsMixin +from .. import models + + +class MixedRealityStsRestClient(MixedRealityStsRestClientOperationsMixin): + """Definition for the Mixed Reality Cloud STS service APIs. + + :param str base_url: Service URL + """ + + def __init__( + self, + base_url: Optional[str] = None, + **kwargs: Any + ) -> None: + if not base_url: + base_url = 'https://sts.mixedreality.azure.com' + self._config = MixedRealityStsRestClientConfiguration(**kwargs) + self._client = AsyncPipelineClient(base_url=base_url, config=self._config, **kwargs) + + client_models = {k: v for k, v in models.__dict__.items() if isinstance(v, type)} + self._serialize = Serializer(client_models) + self._serialize.client_side_validation = False + self._deserialize = Deserializer(client_models) + + + async def close(self) -> None: + await self._client.close() + + async def __aenter__(self) -> "MixedRealityStsRestClient": + await self._client.__aenter__() + return self + + async def __aexit__(self, *exc_details) -> None: + await self._client.__aexit__(*exc_details) diff --git a/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/aio/operations/__init__.py b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/aio/operations/__init__.py new file mode 100644 index 000000000000..56b55f918c06 --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/aio/operations/__init__.py @@ -0,0 +1,13 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from ._mixed_reality_sts_rest_client_operations import MixedRealityStsRestClientOperationsMixin + +__all__ = [ + 'MixedRealityStsRestClientOperationsMixin', +] diff --git a/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/aio/operations/_mixed_reality_sts_rest_client_operations.py b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/aio/operations/_mixed_reality_sts_rest_client_operations.py new file mode 100644 index 000000000000..90031d443396 --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/aio/operations/_mixed_reality_sts_rest_client_operations.py @@ -0,0 +1,89 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +from typing import Any, Callable, Dict, Generic, Optional, TypeVar +import warnings + +from azure.core.exceptions import ClientAuthenticationError, HttpResponseError, ResourceExistsError, ResourceNotFoundError, map_error +from azure.core.pipeline import PipelineResponse +from azure.core.pipeline.transport import AsyncHttpResponse, HttpRequest + +from ... import models as _models + +T = TypeVar('T') +ClsType = Optional[Callable[[PipelineResponse[HttpRequest, AsyncHttpResponse], T, Dict[str, Any]], Any]] + +class MixedRealityStsRestClientOperationsMixin: + + async def get_token( + self, + account_id: str, + api_version: Optional[str] = "2019-02-28-preview", + token_request_options: Optional["_models.TokenRequestOptions"] = None, + **kwargs + ) -> "_models.StsTokenResponseMessage": + """Gets an access token to be used with Mixed Reality services. + + Gets an access token to be used with Mixed Reality services. + + :param account_id: The Mixed Reality account identifier. + :type account_id: str + :param api_version: Api Version. + :type api_version: str + :param token_request_options: Parameter group. + :type token_request_options: ~azure.mixedreality.authentication._generated.models.TokenRequestOptions + :keyword callable cls: A custom type or function that will be passed the direct response + :return: StsTokenResponseMessage, or the result of cls(response) + :rtype: ~azure.mixedreality.authentication._generated.models.StsTokenResponseMessage + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.StsTokenResponseMessage"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + + _client_request_id = None + if token_request_options is not None: + _client_request_id = token_request_options.client_request_id + accept = "application/json" + + # Construct URL + url = self.get_token.metadata['url'] # type: ignore + path_format_arguments = { + 'accountId': self._serialize.url("account_id", account_id, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + if api_version is not None: + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + if _client_request_id is not None: + header_parameters['X-MRC-CV'] = self._serialize.header("client_request_id", _client_request_id, 'str') + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + request = self._client.get(url, query_parameters, header_parameters) + pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + response_headers = {} + response_headers['MS-CV']=self._deserialize('str', response.headers.get('MS-CV')) + deserialized = self._deserialize('StsTokenResponseMessage', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, response_headers) + + return deserialized + get_token.metadata = {'url': '/Accounts/{accountId}/token'} # type: ignore diff --git a/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/models/__init__.py b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/models/__init__.py new file mode 100644 index 000000000000..6e706bc24cdc --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/models/__init__.py @@ -0,0 +1,19 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +try: + from ._models_py3 import StsTokenResponseMessage + from ._models_py3 import TokenRequestOptions +except (SyntaxError, ImportError): + from ._models import StsTokenResponseMessage # type: ignore + from ._models import TokenRequestOptions # type: ignore + +__all__ = [ + 'StsTokenResponseMessage', + 'TokenRequestOptions', +] diff --git a/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/models/_models.py b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/models/_models.py new file mode 100644 index 000000000000..2b8d812be323 --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/models/_models.py @@ -0,0 +1,54 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +import msrest.serialization + + +class StsTokenResponseMessage(msrest.serialization.Model): + """Represents a token response message from the STS service. + + All required parameters must be populated in order to send to Azure. + + :param access_token: Required. An access token for the account. + :type access_token: str + """ + + _validation = { + 'access_token': {'required': True}, + } + + _attribute_map = { + 'access_token': {'key': 'AccessToken', 'type': 'str'}, + } + + def __init__( + self, + **kwargs + ): + super(StsTokenResponseMessage, self).__init__(**kwargs) + self.access_token = kwargs['access_token'] + + +class TokenRequestOptions(msrest.serialization.Model): + """Parameter group. + + :param client_request_id: The client request correlation vector, which should be set to a new + value for each request. Useful when debugging with Microsoft. + :type client_request_id: str + """ + + _attribute_map = { + 'client_request_id': {'key': 'clientRequestId', 'type': 'str'}, + } + + def __init__( + self, + **kwargs + ): + super(TokenRequestOptions, self).__init__(**kwargs) + self.client_request_id = kwargs.get('client_request_id', None) diff --git a/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/models/_models_py3.py b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/models/_models_py3.py new file mode 100644 index 000000000000..5ecff28c8992 --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/models/_models_py3.py @@ -0,0 +1,60 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import Optional + +import msrest.serialization + + +class StsTokenResponseMessage(msrest.serialization.Model): + """Represents a token response message from the STS service. + + All required parameters must be populated in order to send to Azure. + + :param access_token: Required. An access token for the account. + :type access_token: str + """ + + _validation = { + 'access_token': {'required': True}, + } + + _attribute_map = { + 'access_token': {'key': 'AccessToken', 'type': 'str'}, + } + + def __init__( + self, + *, + access_token: str, + **kwargs + ): + super(StsTokenResponseMessage, self).__init__(**kwargs) + self.access_token = access_token + + +class TokenRequestOptions(msrest.serialization.Model): + """Parameter group. + + :param client_request_id: The client request correlation vector, which should be set to a new + value for each request. Useful when debugging with Microsoft. + :type client_request_id: str + """ + + _attribute_map = { + 'client_request_id': {'key': 'clientRequestId', 'type': 'str'}, + } + + def __init__( + self, + *, + client_request_id: Optional[str] = None, + **kwargs + ): + super(TokenRequestOptions, self).__init__(**kwargs) + self.client_request_id = client_request_id diff --git a/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/operations/__init__.py b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/operations/__init__.py new file mode 100644 index 000000000000..56b55f918c06 --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/operations/__init__.py @@ -0,0 +1,13 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from ._mixed_reality_sts_rest_client_operations import MixedRealityStsRestClientOperationsMixin + +__all__ = [ + 'MixedRealityStsRestClientOperationsMixin', +] diff --git a/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/operations/_mixed_reality_sts_rest_client_operations.py b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/operations/_mixed_reality_sts_rest_client_operations.py new file mode 100644 index 000000000000..8b8628e0c285 --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/operations/_mixed_reality_sts_rest_client_operations.py @@ -0,0 +1,94 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +from typing import TYPE_CHECKING +import warnings + +from azure.core.exceptions import ClientAuthenticationError, HttpResponseError, ResourceExistsError, ResourceNotFoundError, map_error +from azure.core.pipeline import PipelineResponse +from azure.core.pipeline.transport import HttpRequest, HttpResponse + +from .. import models as _models + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import Any, Callable, Dict, Generic, Optional, TypeVar + + T = TypeVar('T') + ClsType = Optional[Callable[[PipelineResponse[HttpRequest, HttpResponse], T, Dict[str, Any]], Any]] + +class MixedRealityStsRestClientOperationsMixin(object): + + def get_token( + self, + account_id, # type: str + api_version="2019-02-28-preview", # type: Optional[str] + token_request_options=None, # type: Optional["_models.TokenRequestOptions"] + **kwargs # type: Any + ): + # type: (...) -> "_models.StsTokenResponseMessage" + """Gets an access token to be used with Mixed Reality services. + + Gets an access token to be used with Mixed Reality services. + + :param account_id: The Mixed Reality account identifier. + :type account_id: str + :param api_version: Api Version. + :type api_version: str + :param token_request_options: Parameter group. + :type token_request_options: ~azure.mixedreality.authentication._generated.models.TokenRequestOptions + :keyword callable cls: A custom type or function that will be passed the direct response + :return: StsTokenResponseMessage, or the result of cls(response) + :rtype: ~azure.mixedreality.authentication._generated.models.StsTokenResponseMessage + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.StsTokenResponseMessage"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + + _client_request_id = None + if token_request_options is not None: + _client_request_id = token_request_options.client_request_id + accept = "application/json" + + # Construct URL + url = self.get_token.metadata['url'] # type: ignore + path_format_arguments = { + 'accountId': self._serialize.url("account_id", account_id, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + if api_version is not None: + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + if _client_request_id is not None: + header_parameters['X-MRC-CV'] = self._serialize.header("client_request_id", _client_request_id, 'str') + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + request = self._client.get(url, query_parameters, header_parameters) + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + response_headers = {} + response_headers['MS-CV']=self._deserialize('str', response.headers.get('MS-CV')) + deserialized = self._deserialize('StsTokenResponseMessage', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, response_headers) + + return deserialized + get_token.metadata = {'url': '/Accounts/{accountId}/token'} # type: ignore diff --git a/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/py.typed b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/py.typed new file mode 100644 index 000000000000..e5aff4f83af8 --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_generated/py.typed @@ -0,0 +1 @@ +# Marker file for PEP 561. \ No newline at end of file diff --git a/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_shared/__init__.py b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_shared/__init__.py new file mode 100644 index 000000000000..5b396cd202e8 --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_shared/__init__.py @@ -0,0 +1,5 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- diff --git a/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_shared/aio/__init__.py b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_shared/aio/__init__.py new file mode 100644 index 000000000000..5b396cd202e8 --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_shared/aio/__init__.py @@ -0,0 +1,5 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- diff --git a/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_shared/aio/mixed_reality_token_credential.py b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_shared/aio/mixed_reality_token_credential.py new file mode 100644 index 000000000000..7cc4e98d8bc6 --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_shared/aio/mixed_reality_token_credential.py @@ -0,0 +1,67 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +from typing import TYPE_CHECKING + +from azure.mixedreality.authentication.aio import MixedRealityStsClient +from .static_access_token_credential import StaticAccessTokenCredential + +if TYPE_CHECKING: + from typing import Any + from azure.core.credentials import AccessToken + from azure.core.credentials_async import AsyncTokenCredential + +def get_mixedreality_credential( + account_id: str, + account_domain: str, + endpoint_url: str, + credential: "AsyncTokenCredential", + **kwargs): + if isinstance(credential, StaticAccessTokenCredential): + return credential + + return MixedRealityTokenCredential( + account_id=account_id, + account_domain=account_domain, + endpoint_url=endpoint_url, + credential=credential, + **kwargs) + + +class MixedRealityTokenCredential(object): + """ Represents a token credential that can be used to access a Mixed Reality service. + This implements the TokenCredential protocol. + + :param str account_id: The Mixed Reality service account identifier. + :param str endpoint_url: The Mixed Reality STS service endpoint. + :param TokenCredential credential: The credential used to access the Mixed Reality service. + """ + + def __init__( + self, + account_id: str, + account_domain: str, + endpoint_url: str, + credential: "AsyncTokenCredential", + **kwargs): + self.stsClient = MixedRealityStsClient( + account_id=account_id, + account_domain=account_domain, + endpoint_url=endpoint_url, + credential=credential, + **kwargs) + + async def get_token(self, *scopes: str, **kwargs: "Any") -> "AccessToken": #pylint: disable=unused-argument + return await self.stsClient.get_token(**kwargs) + + async def close(self) -> None: + self.stsClient.close() + + async def __aenter__(self): + await self.stsClient.__aenter__() + return self + + async def __aexit__(self, exc_type, exc_value, traceback) -> None: + await self.stsClient.__aexit__() diff --git a/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_shared/aio/mixedreality_account_key_credential.py b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_shared/aio/mixedreality_account_key_credential.py new file mode 100644 index 000000000000..2f9efb559a53 --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_shared/aio/mixedreality_account_key_credential.py @@ -0,0 +1,54 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +from typing import TYPE_CHECKING + +from datetime import date, datetime + +from azure.core.credentials import AccessToken + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import Any + from azure.core.credentials import AzureKeyCredential + +ACCOUNT_KEY_VALID_YEARS = 10 + +class MixedRealityAccountKeyCredential(object): + """ Represents an object used for Mixed Reality account key authentication. + + :param str account_id: The Mixed Reality service account identifier. + :param AzureKeyCredential account_key: The Mixed Reality service account primary or secondary key credential. + """ + + def __init__(self, account_id, account_key): + # type: (str, AzureKeyCredential) -> None + self.account_id = account_id + self.account_key = account_key + + async def get_token(self, *scopes: str, **kwargs: "Any") -> "AccessToken": #pylint: disable=unused-argument + token = self.account_id + ":" + self.account_key.key + + # No way to know when an account key might expire, so we'll set the + # access token wrapping it to expire 10 years in the future. + expiration_date = _add_years(datetime.now(), ACCOUNT_KEY_VALID_YEARS) + expiration_timestamp = int(expiration_date.timestamp()) + + return AccessToken(token, expiration_timestamp) + + async def close(self) -> None: + pass + + async def __aenter__(self): + pass + + async def __aexit__(self, exc_type, exc_value, traceback) -> None: + pass + +def _add_years(date_to_update, years): + try: + return date_to_update.replace(year=date_to_update.year + years) + except ValueError: + return date_to_update + (date(date_to_update.year + years, 1, 1) - date(date_to_update.year, 1, 1)) diff --git a/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_shared/aio/static_access_token_credential.py b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_shared/aio/static_access_token_credential.py new file mode 100644 index 000000000000..fccbd6f1a2b0 --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_shared/aio/static_access_token_credential.py @@ -0,0 +1,38 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import Any + from azure.core.credentials_async import AsyncTokenCredential + from azure.core.credentials import AccessToken + +class StaticAccessTokenCredential(object): + """ Represents a static access token credential. + This implements the AsyncTokenCredential protocol. + + :param AccessToken access_token: An access token. + """ + + def __init__(self, access_token: "AccessToken"): + self._access_token = access_token + + async def get_token( + self, + #pylint: disable=unused-argument + *scopes: str, + **kwargs: "Any") -> "AccessToken": + return self._access_token + + async def close(self) -> None: + pass + + async def __aenter__(self): + pass + + async def __aexit__(self, exc_type, exc_value, traceback) -> None: + pass diff --git a/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_shared/authentication_endpoint.py b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_shared/authentication_endpoint.py new file mode 100644 index 000000000000..72bdcc70fd96 --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_shared/authentication_endpoint.py @@ -0,0 +1,9 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +def construct_endpoint_url(account_domain): + # type: (str) -> str + return 'https://sts.' + account_domain diff --git a/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_shared/mixed_reality_token_credential.py b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_shared/mixed_reality_token_credential.py new file mode 100644 index 000000000000..e0bb1dda3627 --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_shared/mixed_reality_token_credential.py @@ -0,0 +1,50 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +from typing import TYPE_CHECKING + +from azure.mixedreality.authentication import MixedRealityStsClient +from .static_access_token_credential import StaticAccessTokenCredential + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import Any, Union + from azure.core.credentials import AccessToken, TokenCredential + + +def get_mixedreality_credential(account_id, account_domain, endpoint_url, credential, **kwargs): + # type: (str, str, str, TokenCredential, Any) -> TokenCredential + if isinstance(credential, StaticAccessTokenCredential): + return credential + + return MixedRealityTokenCredential( + account_id=account_id, + account_domain=account_domain, + endpoint_url=endpoint_url, + credential=credential, + **kwargs) + + +class MixedRealityTokenCredential(object): + """ Represents a token credential that can be used to access a Mixed Reality service. + This implements the TokenCredential protocol. + + :param str account_id: The Mixed Reality service account identifier. + :param str endpoint_url: The Mixed Reality STS service endpoint. + :param TokenCredential credential: The credential used to access the Mixed Reality service. + """ + + def __init__(self, account_id, account_domain, endpoint_url, credential, **kwargs): + # type: (str, str, str, TokenCredential, Any) -> None + self.stsClient = MixedRealityStsClient( + account_id=account_id, + account_domain=account_domain, + endpoint_url=endpoint_url, + credential=credential, + **kwargs) + + def get_token(self, *scopes, **kwargs): #pylint: disable=unused-argument + # type: (*str, **Any) -> AccessToken + return self.stsClient.get_token(**kwargs) diff --git a/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_shared/mixedreality_account_key_credential.py b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_shared/mixedreality_account_key_credential.py new file mode 100644 index 000000000000..a7ec1ca5db63 --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_shared/mixedreality_account_key_credential.py @@ -0,0 +1,46 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +from typing import TYPE_CHECKING +from datetime import date, datetime +import time + +from azure.core.credentials import AzureKeyCredential, AccessToken + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import Any + +ACCOUNT_KEY_VALID_YEARS = 10 + +class MixedRealityAccountKeyCredential(object): + """ Represents an object used for Mixed Reality account key authentication. + + :param str account_id: The Mixed Reality service account identifier. + :param AzureKeyCredential account_key: The Mixed Reality service account primary or secondary key credential. + """ + + def __init__(self, account_id, account_key): + # type: (str, AzureKeyCredential) -> None + self.account_id = account_id + self.account_key = account_key + + def get_token(self, *scopes, **kwargs): #pylint: disable=unused-argument + # type: (*str, **Any) -> AccessToken + + token = self.account_id + ":" + self.account_key.key + + # No way to know when an account key might expire, so we'll set the + # access token wrapping it to expire 10 years in the future. + expiration_date = _add_years(datetime.now(), ACCOUNT_KEY_VALID_YEARS) + expiration_timestamp = int(time.mktime(expiration_date.timetuple())) + + return AccessToken(token, expiration_timestamp) + +def _add_years(date_to_update, years): + try: + return date_to_update.replace(year=date_to_update.year + years) + except ValueError: + return date_to_update + (date(date_to_update.year + years, 1, 1) - date(date_to_update.year, 1, 1)) diff --git a/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_shared/static_access_token_credential.py b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_shared/static_access_token_credential.py new file mode 100644 index 000000000000..712f4e20738f --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_shared/static_access_token_credential.py @@ -0,0 +1,27 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import Any + from azure.core.credentials import TokenCredential + from azure.core.credentials import AccessToken + +class StaticAccessTokenCredential(object): + """ Represents a static access token credential. + This implements the TokenCredential protocol. + + :param AccessToken access_token: An access token. + """ + + def __init__(self, access_token): + # type: (AccessToken) -> None + self._access_token = access_token + + def get_token(self, *scopes, **kwargs): #pylint: disable=unused-argument + # type: (*str, **Any) -> AccessToken + return self._access_token diff --git a/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_utils.py b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_utils.py new file mode 100644 index 000000000000..9a016a0692a7 --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_utils.py @@ -0,0 +1,73 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +import base64 +import json +import random + +from azure.core.credentials import AccessToken + +from ._generated.models import StsTokenResponseMessage + + +def convert_to_access_token(token_response_message): + # type: (StsTokenResponseMessage) -> AccessToken + """ + Converts the specified token response message to an AccessToken. + """ + if not token_response_message: + raise ValueError("token_response_message must be a non-empty string.") + + expiration_timestamp = retrieve_jwt_expiration_timestamp(token_response_message.access_token) + + return AccessToken(token_response_message.access_token, expiration_timestamp) + +def retrieve_jwt_expiration_timestamp(jwt_value): + # type: (str) -> int + """ + Retrieves the expiration value from the JWT. + + :param str jwt_value: The JWT value. + :returns: int + """ + if not jwt_value: + raise ValueError("jwt_value must be a non-empty string.") + + parts = jwt_value.split(".") + + if len(parts) < 3: + raise ValueError("Invalid JWT structure. Expected a JWS Compact Serialization formatted value.") + + try: + padded_base64_payload = base64.b64decode(parts[1]).decode('utf-8') + payload = json.loads(padded_base64_payload) + except ValueError: + raise ValueError("Unable to decode the JWT.") + + try: + exp = payload['exp'] + except KeyError: + raise ValueError("Invalid JWT payload structure. No expiration.") + + return int(exp) + +BASE_64_CHAR_SET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" +CV_BASE_LENGTH = 22 + +def generate_cv_base(): + # type: () -> str + """ + Seed function to randomly generate a 16 character base64 encoded string for + the Correlation Vector's base value. + """ + result = '' + + #pylint: disable=unused-variable + for i in range(CV_BASE_LENGTH): + random_index = random.randint(0, len(BASE_64_CHAR_SET) - 1) + result += BASE_64_CHAR_SET[random_index] + + return result diff --git a/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_version.py b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_version.py new file mode 100644 index 000000000000..14cf0981d001 --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/_version.py @@ -0,0 +1,10 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +# matches SEMVER +VERSION = "1.0.0b1" + +SDK_MONIKER = "mixedreality-authentication/{}".format(VERSION) # type: str diff --git a/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/aio/__init__.py b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/aio/__init__.py new file mode 100644 index 000000000000..736dc1c08fe4 --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/aio/__init__.py @@ -0,0 +1,11 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +from ._client_async import MixedRealityStsClient + +__all__ = [ + 'MixedRealityStsClient' +] diff --git a/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/aio/_client_async.py b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/aio/_client_async.py new file mode 100644 index 000000000000..fedbacccf397 --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/azure/mixedreality/authentication/aio/_client_async.py @@ -0,0 +1,112 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +# pylint: disable=unused-import,ungrouped-imports +from typing import Any, TYPE_CHECKING, Union + +try: + from urllib.parse import urlparse +except ImportError: + from urlparse import urlparse # type: ignore + +from azure.core.credentials import AzureKeyCredential +from azure.core.tracing.decorator_async import distributed_trace_async +from azure.core.pipeline.policies import AsyncBearerTokenCredentialPolicy + +from .._generated.aio import MixedRealityStsRestClient +from .._generated.models import TokenRequestOptions +from .._version import SDK_MONIKER +from .._shared.authentication_endpoint import construct_endpoint_url +from .._shared.aio.mixedreality_account_key_credential import MixedRealityAccountKeyCredential +from .._utils import convert_to_access_token, generate_cv_base + +if TYPE_CHECKING: + from azure.core.credentials import AccessToken + from azure.core.credentials_async import AsyncTokenCredential + + +class MixedRealityStsClient(object): + """ A client to interact with the Mixed Reality STS service. + + :param str account_id: + The Mixed Reality service account identifier. + :param str account_domain: + The Mixed Reality service account domain. + :param Union[TokenCredential, AzureKeyCredential] credential: + The credential used to access the Mixed Reality service. + :keyword str custom_endpoint_url: + Override the Mixed Reality STS service endpoint. + """ + + def __init__(self, + account_id: str, + account_domain: str, + credential: Union[AzureKeyCredential, "AsyncTokenCredential"], #pylint: disable=unsubscriptable-object + **kwargs) -> None: + if not account_id: + raise ValueError("account_id must be a non-empty string.") + + if not account_domain: + raise ValueError("account_domain must be a non-empty string.") + + if not credential: + raise ValueError("credential can not be None.") + + self._account_id = account_id + self._account_domain = account_domain + + if isinstance(credential, AzureKeyCredential): + credential = MixedRealityAccountKeyCredential(account_id, credential) + + self._credential = credential + + endpoint_url = kwargs.pop('custom_endpoint_url', construct_endpoint_url(account_domain)) + + try: + if not endpoint_url.lower().startswith('http'): + endpoint_url = "https://" + endpoint_url + except AttributeError as ex: + raise ValueError("Host URL must be a string.") from ex + + parsed_url = urlparse(endpoint_url.rstrip('/')) + if not parsed_url.netloc: + raise ValueError("Invalid URL: {}".format(endpoint_url)) + + self._endpoint_url = endpoint_url + + authentication_policy = AsyncBearerTokenCredentialPolicy(credential, [endpoint_url + '/.default']) + + self._client = MixedRealityStsRestClient( + base_url=endpoint_url, + authentication_policy=authentication_policy, + sdk_moniker=SDK_MONIKER, + **kwargs) + + @distributed_trace_async + async def get_token(self, **kwargs) -> "AccessToken": + """ + Retrieve a token from the STS service for the specified account identifier asynchronously. + :return: Instance of azure.core.credentials.AccessToken - token and expiry date of it + :rtype: :class:`azure.core.credentials.AccessToken` + """ + token_request_options = TokenRequestOptions() + token_request_options.client_request_id = generate_cv_base() + + response = await self._client.get_token( + self._account_id, + token_request_options=token_request_options, + **kwargs) + return convert_to_access_token(response) + + async def close(self) -> None: + await self._client.close() + + async def __aenter__(self) -> "MixedRealityStsClient": + await self._client.__aenter__() + return self + + async def __aexit__(self, *args) -> None: + await self._client.__aexit__(*args) diff --git a/sdk/mixedreality/azure-mixedreality-authentication/dev_requirements.txt b/sdk/mixedreality/azure-mixedreality-authentication/dev_requirements.txt new file mode 100644 index 000000000000..d9b5454b3434 --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/dev_requirements.txt @@ -0,0 +1,4 @@ +-e ../../../tools/azure-sdk-tools +../azure-mixedreality-nspkg +../../core/azure-core +aiohttp>=3.0; python_version >= '3.5' diff --git a/sdk/mixedreality/azure-mixedreality-authentication/samples/client_sample async.py b/sdk/mixedreality/azure-mixedreality-authentication/samples/client_sample async.py new file mode 100644 index 000000000000..5dc595bc2f3e --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/samples/client_sample async.py @@ -0,0 +1,70 @@ + +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +""" +FILE: client_sample_async.py +DESCRIPTION: + These samples demonstrate creating a client and requesting a token. + +USAGE: + python client_sample_async.py + Set the environment variables with your own values before running the sample: + 1) MIXEDREALITY_ACCOUNT_DOMAIN - the Mixed Reality account domain. + 2) MIXEDREALITY_ACCOUNT_ID - the Mixed Reality account identifier. + 3) MIXEDREALITY_ACCOUNT_KEY - the Mixed Reality account primary or secondary key. +""" + + +import os +import asyncio + + +class ClientSamplesAsync(object): + from azure.core.credentials import AzureKeyCredential + + account_domain = os.environ.get("MIXEDREALITY_ACCOUNT_DOMAIN", None) + if not account_domain: + raise ValueError("Set MIXEDREALITY_ACCOUNT_DOMAIN env before run this sample.") + + account_id = os.environ.get("MIXEDREALITY_ACCOUNT_ID", None) + if not account_id: + raise ValueError("Set MIXEDREALITY_ACCOUNT_ID env before run this sample.") + + account_key = os.environ.get("MIXEDREALITY_ACCOUNT_KEY", None) + if not account_key: + raise ValueError("Set MIXEDREALITY_ACCOUNT_KEY env before run this sample.") + + key_credential = AzureKeyCredential(account_key) + + def create_client(self): + # [START create_client] + from azure.mixedreality.authentication.aio import MixedRealityStsClient + client = MixedRealityStsClient(self.account_id, self.account_domain, self.key_credential) + # [END create_client] + + print("client created") + + async def get_token(self): + from azure.mixedreality.authentication.aio import MixedRealityStsClient + client = MixedRealityStsClient(self.account_id, self.account_domain, self.key_credential) + + async with client: + # [START get_token] + access_token = await client.get_token() + # [END get_token] + + print("token retrieved: " + access_token.token) + + +async def main(): + sample = ClientSamplesAsync() + sample.create_client() + await sample.get_token() + +if __name__ == '__main__': + loop = asyncio.get_event_loop() + loop.run_until_complete(main()) diff --git a/sdk/mixedreality/azure-mixedreality-authentication/samples/client_sample.py b/sdk/mixedreality/azure-mixedreality-authentication/samples/client_sample.py new file mode 100644 index 000000000000..0de0e21a39de --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/samples/client_sample.py @@ -0,0 +1,63 @@ + +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +""" +FILE: client_sample.py +DESCRIPTION: + These samples demonstrate creating a client and requesting a token. + +USAGE: + python client_sample.py + Set the environment variables with your own values before running the sample: + 1) MIXEDREALITY_ACCOUNT_DOMAIN - the Mixed Reality account domain. + 2) MIXEDREALITY_ACCOUNT_ID - the Mixed Reality account identifier. + 3) MIXEDREALITY_ACCOUNT_KEY - the Mixed Reality account primary or secondary key. +""" + + +import os + + +class ClientSamples(object): + from azure.core.credentials import AzureKeyCredential + + account_domain = os.environ.get("MIXEDREALITY_ACCOUNT_DOMAIN", None) + if not account_domain: + raise ValueError("Set MIXEDREALITY_ACCOUNT_DOMAIN env before run this sample.") + + account_id = os.environ.get("MIXEDREALITY_ACCOUNT_ID", None) + if not account_id: + raise ValueError("Set MIXEDREALITY_ACCOUNT_ID env before run this sample.") + + account_key = os.environ.get("MIXEDREALITY_ACCOUNT_KEY", None) + if not account_key: + raise ValueError("Set MIXEDREALITY_ACCOUNT_KEY env before run this sample.") + + key_credential = AzureKeyCredential(account_key) + + def create_client(self): + # [START create_client] + from azure.mixedreality.authentication import MixedRealityStsClient + client = MixedRealityStsClient(self.account_id, self.account_domain, self.key_credential) + # [END create_client] + + print("client created") + + def get_token(self): + # [START get_token] + from azure.mixedreality.authentication import MixedRealityStsClient + client = MixedRealityStsClient(self.account_id, self.account_domain, self.key_credential) + access_token = client.get_token() + # [END get_token] + + print("token retrieved: " + access_token.token) + + +if __name__ == '__main__': + sample = ClientSamples() + sample.create_client() + sample.get_token() diff --git a/sdk/mixedreality/azure-mixedreality-authentication/sdk_packaging.toml b/sdk/mixedreality/azure-mixedreality-authentication/sdk_packaging.toml new file mode 100644 index 000000000000..2684d587e162 --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/sdk_packaging.toml @@ -0,0 +1,7 @@ +[packaging] +auto_update = false +package_name = "azure-mixedreality-authentication" +package_pprint_name = "Mixed Reality Authentication" +package_doc_id = "" +is_stable = false +is_arm = false diff --git a/sdk/mixedreality/azure-mixedreality-authentication/setup.cfg b/sdk/mixedreality/azure-mixedreality-authentication/setup.cfg new file mode 100644 index 000000000000..3c6e79cf31da --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/setup.cfg @@ -0,0 +1,2 @@ +[bdist_wheel] +universal=1 diff --git a/sdk/mixedreality/azure-mixedreality-authentication/setup.py b/sdk/mixedreality/azure-mixedreality-authentication/setup.py new file mode 100644 index 000000000000..0384e23bf7c8 --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/setup.py @@ -0,0 +1,80 @@ +from setuptools import setup, find_packages +import os +from io import open +import re + +# example setup.py Feel free to copy the entire "azure-template" folder into a package folder named +# with "azure-". Ensure that the below arguments to setup() are updated to reflect +# your package. + +# this setup.py is set up in a specific way to keep the azure* and azure-mgmt-* namespaces WORKING all the way +# up from python 2.7. Reference here: https://github.com/Azure/azure-sdk-for-python/wiki/Azure-packaging + +PACKAGE_NAME = "azure-mixedreality-authentication" +PACKAGE_PPRINT_NAME = "Mixed Reality Authentication" + +# a-b-c => a/b/c +package_folder_path = PACKAGE_NAME.replace('-', '/') +# a-b-c => a.b.c +namespace_name = PACKAGE_NAME.replace('-', '.') + +# Version extraction inspired from 'requests' +with open(os.path.join(package_folder_path, '_version.py'), 'r') as fd: + version = re.search(r'^VERSION\s*=\s*[\'"]([^\'"]*)[\'"]', + fd.read(), re.MULTILINE).group(1) +if not version: + raise RuntimeError('Cannot find version information') + +with open('README.md', encoding='utf-8') as f: + readme = f.read() + +with open('CHANGELOG.md', encoding='utf-8') as f: + changelog = f.read() + +setup( + name=PACKAGE_NAME, + version=version, + description='Microsoft Azure {} Client Library for Python'.format(PACKAGE_PPRINT_NAME), + + # ensure that these are updated to reflect the package owners' information + long_description=readme + "\n\n" + changelog, + long_description_content_type='text/markdown', + url='https://github.com/Azure/azure-sdk-for-python', + author='Microsoft Corporation', + author_email='azuresdkengsysadmins@microsoft.com', + + license='MIT License', + # ensure that the development status reflects the status of your package + classifiers=[ + "Development Status :: 4 - Beta", + + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'License :: OSI Approved :: MIT License', + ], + packages=find_packages(exclude=[ + 'tests', + # Exclude packages that will be covered by PEP420 or nspkg + 'azure', + 'azure.mixedreality' + ]), + install_requires=[ + 'azure-core<2.0.0,>=1.2.2', + 'msrest>=0.5.0' + ], + extras_require={ + ":python_version<'3.0'": ['azure-mixedreality-nspkg'], + ":python_version<'3.5'": ["typing"] + }, + project_urls={ + 'Bug Reports': 'https://github.com/Azure/azure-sdk-for-python/issues', + 'Source': 'https://github.com/Azure/azure-sdk-python', + } +) diff --git a/sdk/mixedreality/azure-mixedreality-authentication/swagger/SWAGGER.md b/sdk/mixedreality/azure-mixedreality-authentication/swagger/SWAGGER.md new file mode 100644 index 000000000000..40af809e4840 --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/swagger/SWAGGER.md @@ -0,0 +1,30 @@ +# Azure Mixed Reality Authentication Service client library for Python + +## Setup + +```ps +npm install -g autorest +``` + +## Generation + +```ps +cd +autorest SWAGGER.md +``` + +### Code generation settings + +```yaml +title: MixedRealityStsRestClient +input-file: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/aa19725fe79aea2a9dc580f3c66f77f89cc34563/specification/mixedreality/data-plane/Microsoft.MixedReality/preview/2019-02-28-preview/mr-sts.json +output-folder: ../azure/mixedreality/authentication/_generated +namespace: azure.mixedreality.authentication._generated +no-namespace-folders: true +license-header: MICROSOFT_MIT_NO_VERSION +enable-xml: false +clear-output-folder: true +python: true +v3: true +add-credentials: false +``` diff --git a/sdk/mixedreality/azure-mixedreality-authentication/swagger/update.ps1 b/sdk/mixedreality/azure-mixedreality-authentication/swagger/update.ps1 new file mode 100644 index 000000000000..a7490a9848d1 --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/swagger/update.ps1 @@ -0,0 +1,6 @@ +Push-Location $PSScriptRoot +try { + & autorest SWAGGER.md +} finally { + Pop-Location +} diff --git a/sdk/mixedreality/azure-mixedreality-authentication/tests/_constants.py b/sdk/mixedreality/azure-mixedreality-authentication/tests/_constants.py new file mode 100644 index 000000000000..9de02c451788 --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/tests/_constants.py @@ -0,0 +1,10 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +# Fake account details matching recordings. +MIXEDREALITY_ACCOUNT_DOMAIN="mixedreality.azure.com" +MIXEDREALITY_ACCOUNT_ID="68321d5a-7978-4ceb-b880-0f49751daae9" +MIXEDREALITY_ACCOUNT_KEY="NjgzMjFkNWEtNzk3OC00Y2ViLWI4ODAtMGY0OTc1MWRhYWU5" \ No newline at end of file diff --git a/sdk/mixedreality/azure-mixedreality-authentication/tests/conftest.py b/sdk/mixedreality/azure-mixedreality-authentication/tests/conftest.py new file mode 100644 index 000000000000..9f69fcac8bdc --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/tests/conftest.py @@ -0,0 +1,11 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +import sys + +# Ignore collection of async tests for Python 2 +collect_ignore_glob = [] +if sys.version_info < (3, 5): + collect_ignore_glob.append("*_async.py") diff --git a/sdk/mixedreality/azure-mixedreality-authentication/tests/recordings/test_client.test_get_token.yaml b/sdk/mixedreality/azure-mixedreality-authentication/tests/recordings/test_client.test_get_token.yaml new file mode 100644 index 000000000000..6be3baa36955 --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/tests/recordings/test_client.test_get_token.yaml @@ -0,0 +1,38 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-mixedreality-authentication/1.0.0b1 Python/3.9.1 (Windows-10-10.0.19041-SP0) + X-MRC-CV: + - z2TH5gImRhQCZc4ubhqtRV + method: GET + uri: https://sts.mixedreality.azure.com/Accounts/68321d5a-7978-4ceb-b880-0f49751daae9/token?api-version=2019-02-28-preview + response: + body: + string: '{"AccessToken":"eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJlbWFpbCI6IkJvYkBjb250b3NvLmNvbSIsImdpdmVuX25hbWUiOiJCb2IiLCJpc3MiOiJodHRwOi8vRGVmYXVsdC5Jc3N1ZXIuY29tIiwiYXVkIjoiaHR0cDovL0RlZmF1bHQuQXVkaWVuY2UuY29tIiwiaWF0IjoiMTYwNzk3ODY4MyIsIm5iZiI6IjE2MDc5Nzg2ODMiLCJleHAiOiIxNjA3OTc4OTgzIn0=."}' + headers: + cache-control: + - no-store,no-cache + content-length: + - '1264' + content-type: + - application/json; charset=utf-8 + date: + - Thu, 28 Jan 2021 01:56:10 GMT + ms-cv: + - gKXl1CkeG0mRb6HEtMUmHQ.0 + pragma: + - no-cache + x-content-type-options: + - nosniff + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/mixedreality/azure-mixedreality-authentication/tests/recordings/test_client_async.test_get_token.yaml b/sdk/mixedreality/azure-mixedreality-authentication/tests/recordings/test_client_async.test_get_token.yaml new file mode 100644 index 000000000000..32b3c3b0e66c --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/tests/recordings/test_client_async.test_get_token.yaml @@ -0,0 +1,28 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json + User-Agent: + - azsdk-python-mixedreality-authentication/1.0.0b1 Python/3.9.1 (Windows-10-10.0.19041-SP0) + X-MRC-CV: + - DOGUG9rex0lylvLZmhuJgR + method: GET + uri: https://sts.mixedreality.azure.com/Accounts/68321d5a-7978-4ceb-b880-0f49751daae9/token?api-version=2019-02-28-preview + response: + body: + string: '{"AccessToken":"eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJlbWFpbCI6IkJvYkBjb250b3NvLmNvbSIsImdpdmVuX25hbWUiOiJCb2IiLCJpc3MiOiJodHRwOi8vRGVmYXVsdC5Jc3N1ZXIuY29tIiwiYXVkIjoiaHR0cDovL0RlZmF1bHQuQXVkaWVuY2UuY29tIiwiaWF0IjoiMTYwNzk3ODY4MyIsIm5iZiI6IjE2MDc5Nzg2ODMiLCJleHAiOiIxNjA3OTc4OTgzIn0=."}' + headers: + cache-control: no-store,no-cache + content-length: '1264' + content-type: application/json; charset=utf-8 + date: Thu, 28 Jan 2021 18:31:51 GMT + ms-cv: K89E7J8QQ0qfkjIiukWztw.0 + pragma: no-cache + x-content-type-options: nosniff + status: + code: 200 + message: OK + url: https://sts.mixedreality.azure.com/Accounts/68321d5a-7978-4ceb-b880-0f49751daae9/token?api-version=2019-02-28-preview +version: 1 diff --git a/sdk/mixedreality/azure-mixedreality-authentication/tests/test_client.py b/sdk/mixedreality/azure-mixedreality-authentication/tests/test_client.py new file mode 100644 index 000000000000..33e4fee685df --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/tests/test_client.py @@ -0,0 +1,106 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +import os +import pytest + +from devtools_testutils import AzureTestCase + +from azure.core.credentials import AzureKeyCredential +from azure.mixedreality.authentication import MixedRealityStsClient +from azure.mixedreality.authentication._shared.mixedreality_account_key_credential import MixedRealityAccountKeyCredential + +# Import fake account details matching recordings. +from _constants import ( + MIXEDREALITY_ACCOUNT_DOMAIN, + MIXEDREALITY_ACCOUNT_ID, + MIXEDREALITY_ACCOUNT_KEY +) + + +class ClientTests(AzureTestCase): + def __init__(self, *args, **kwargs): + super(ClientTests, self).__init__(*args, **kwargs) + self.account_domain = self.get_var('MIXEDREALITY_ACCOUNT_DOMAIN', MIXEDREALITY_ACCOUNT_DOMAIN) + self.account_id = self.get_var('MIXEDREALITY_ACCOUNT_ID', MIXEDREALITY_ACCOUNT_ID) + self.account_key = self.get_var('MIXEDREALITY_ACCOUNT_KEY', MIXEDREALITY_ACCOUNT_KEY) + self.key_credential = AzureKeyCredential(self.account_key) + + def setUp(self): + super(ClientTests, self).setUp() + + def tearDown(self): + super(ClientTests, self).tearDown() + + def get_var(self, variable_name, default_or_playback_value): + if self.is_live: + return os.environ.get(variable_name, default_or_playback_value) + + return default_or_playback_value + + def test_create_client(self): + client = MixedRealityStsClient( + account_id=self.account_id, + account_domain=self.account_domain, + credential=self.key_credential) + + assert client is not None + + def test_create_client_custom_with_endpoint(self): + custom_endpoint_url = "https://my.custom.endpoint" + client = MixedRealityStsClient( + account_id=self.account_id, + account_domain=self.account_domain, + credential=self.key_credential, + custom_endpoint_url=custom_endpoint_url) + + assert client._endpoint_url == custom_endpoint_url + + def test_create_client_with_credential(self): + token_credential = MixedRealityAccountKeyCredential(self.account_id, self.key_credential) + client = MixedRealityStsClient( + account_id=self.account_id, + account_domain=self.account_domain, + credential=token_credential) + + assert client._credential == token_credential + + def test_create_client_with_invalid_arguments(self): + with pytest.raises(ValueError): + MixedRealityStsClient( + account_id=None, + account_domain=self.account_domain, + credential=self.key_credential) + + with pytest.raises(ValueError): + MixedRealityStsClient( + account_id=self.account_id, + account_domain=None, + credential=self.key_credential) + + with pytest.raises(ValueError): + MixedRealityStsClient( + account_id=self.account_id, + account_domain=self.account_domain, + credential=None) + + with pytest.raises(ValueError): + MixedRealityStsClient( + account_id=self.account_id, + account_domain=self.account_domain, + credential=self.key_credential, + custom_endpoint_url="#") + + def test_get_token(self): + client = MixedRealityStsClient( + account_id=self.account_id, + account_domain=self.account_domain, + credential=self.key_credential) + + token = client.get_token() + + assert token is not None + assert token.token is not None + assert token.expires_on is not None diff --git a/sdk/mixedreality/azure-mixedreality-authentication/tests/test_client_async.py b/sdk/mixedreality/azure-mixedreality-authentication/tests/test_client_async.py new file mode 100644 index 000000000000..aeead54991fc --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/tests/test_client_async.py @@ -0,0 +1,108 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +import os +import pytest + +from devtools_testutils import AzureTestCase + +from azure.core.credentials import AzureKeyCredential +from azure.mixedreality.authentication.aio import MixedRealityStsClient +from azure.mixedreality.authentication._shared.aio.mixedreality_account_key_credential import MixedRealityAccountKeyCredential + +# Import fake account details matching recordings. +from _constants import ( + MIXEDREALITY_ACCOUNT_DOMAIN, + MIXEDREALITY_ACCOUNT_ID, + MIXEDREALITY_ACCOUNT_KEY +) + + +class ClientTests(AzureTestCase): + def __init__(self, *args, **kwargs): + super(ClientTests, self).__init__(*args, **kwargs) + self.account_domain = self.get_var('MIXEDREALITY_ACCOUNT_DOMAIN', MIXEDREALITY_ACCOUNT_DOMAIN) + self.account_id = self.get_var('MIXEDREALITY_ACCOUNT_ID', MIXEDREALITY_ACCOUNT_ID) + self.account_key = self.get_var('MIXEDREALITY_ACCOUNT_KEY', MIXEDREALITY_ACCOUNT_KEY) + self.key_credential = AzureKeyCredential(self.account_key) + + def setUp(self): + super(ClientTests, self).setUp() + + def tearDown(self): + super(ClientTests, self).tearDown() + + def get_var(self, variable_name, default_or_playback_value): + # type: (str, str) -> str + if self.is_live: + return os.environ.get(variable_name, default_or_playback_value) + + return default_or_playback_value + + def test_create_client(self): + client = MixedRealityStsClient( + account_id=self.account_id, + account_domain=self.account_domain, + credential=self.key_credential) + + assert client is not None + + def test_create_client_custom_with_endpoint(self): + custom_endpoint_url = "https://my.custom.endpoint" + client = MixedRealityStsClient( + account_id=self.account_id, + account_domain=self.account_domain, + credential=self.key_credential, + custom_endpoint_url=custom_endpoint_url) + + assert client._endpoint_url == custom_endpoint_url + + def test_create_client_with_credential(self): + token_credential = MixedRealityAccountKeyCredential(self.account_id, self.key_credential) + client = MixedRealityStsClient( + account_id=self.account_id, + account_domain=self.account_domain, + credential=token_credential) + + assert client._credential == token_credential + + def test_create_client_with_invalid_arguments(self): + with pytest.raises(ValueError): + MixedRealityStsClient( + account_id=None, + account_domain=self.account_domain, + credential=self.key_credential) + + with pytest.raises(ValueError): + MixedRealityStsClient( + account_id=self.account_id, + account_domain=None, + credential=self.key_credential) + + with pytest.raises(ValueError): + MixedRealityStsClient( + account_id=self.account_id, + account_domain=self.account_domain, + credential=None) + + with pytest.raises(ValueError): + MixedRealityStsClient( + account_id=self.account_id, + account_domain=self.account_domain, + credential=self.key_credential, + custom_endpoint_url="#") + + @AzureTestCase.await_prepared_test + async def test_get_token(self): + client = MixedRealityStsClient( + account_id=self.account_id, + account_domain=self.account_domain, + credential=self.key_credential) + + token = await client.get_token() + + assert token is not None + assert token.token is not None + assert token.expires_on is not None diff --git a/sdk/mixedreality/azure-mixedreality-authentication/tests/test_mixed_reality_token_credential.py b/sdk/mixedreality/azure-mixedreality-authentication/tests/test_mixed_reality_token_credential.py new file mode 100644 index 000000000000..bb9b3b376c6a --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/tests/test_mixed_reality_token_credential.py @@ -0,0 +1,37 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +from azure.core.credentials import AccessToken, AzureKeyCredential + +from azure.mixedreality.authentication._shared.mixed_reality_token_credential import get_mixedreality_credential, MixedRealityTokenCredential +from azure.mixedreality.authentication._shared.static_access_token_credential import StaticAccessTokenCredential +from azure.mixedreality.authentication._shared.mixedreality_account_key_credential import MixedRealityAccountKeyCredential + +class TestMixedRealityTokenCredential: + def test_get_mixedreality_credential_static_credential(self): + access_token = AccessToken("My access token", 0) + credential = StaticAccessTokenCredential(access_token) + + actualCredential = get_mixedreality_credential( + account_id="account_id", + account_domain="account_domain", + endpoint_url="http://my.endpoint.url", + credential=credential) + + assert credential == actualCredential + + def test_get_mixedreality_credential_other_credential(self): + keyCredential = AzureKeyCredential("my_account_key") + credential = MixedRealityAccountKeyCredential("account_id", keyCredential) + + actualCredential = get_mixedreality_credential( + account_id="account_id", + account_domain="account_domain", + endpoint_url="http://my.endpoint.url", + credential=credential) + + assert credential != actualCredential + assert isinstance(actualCredential, MixedRealityTokenCredential) diff --git a/sdk/mixedreality/azure-mixedreality-authentication/tests/test_mixed_reality_token_credential_async.py b/sdk/mixedreality/azure-mixedreality-authentication/tests/test_mixed_reality_token_credential_async.py new file mode 100644 index 000000000000..a7b2e02dc793 --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/tests/test_mixed_reality_token_credential_async.py @@ -0,0 +1,37 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +from azure.core.credentials import AccessToken, AzureKeyCredential + +from azure.mixedreality.authentication._shared.aio.mixed_reality_token_credential import get_mixedreality_credential, MixedRealityTokenCredential +from azure.mixedreality.authentication._shared.aio.static_access_token_credential import StaticAccessTokenCredential +from azure.mixedreality.authentication._shared.aio.mixedreality_account_key_credential import MixedRealityAccountKeyCredential + +class TestMixedRealityTokenCredential: + def test_get_mixedreality_credential_static_credential(self): + access_token = AccessToken("My access token", 0) + credential = StaticAccessTokenCredential(access_token) + + actualCredential = get_mixedreality_credential( + account_id="account_id", + account_domain="account_domain", + endpoint_url="http://my.endpoint.url", + credential=credential) + + assert credential == actualCredential + + def test_get_mixedreality_credential_other_credential(self): + keyCredential = AzureKeyCredential("my_account_key") + credential = MixedRealityAccountKeyCredential("account_id", keyCredential) + + actualCredential = get_mixedreality_credential( + account_id="account_id", + account_domain="account_domain", + endpoint_url="http://my.endpoint.url", + credential=credential) + + assert credential != actualCredential + assert isinstance(actualCredential, MixedRealityTokenCredential) diff --git a/sdk/mixedreality/azure-mixedreality-authentication/tests/test_static_access_token_credential.py b/sdk/mixedreality/azure-mixedreality-authentication/tests/test_static_access_token_credential.py new file mode 100644 index 000000000000..0511780951dd --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/tests/test_static_access_token_credential.py @@ -0,0 +1,21 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +from azure.core.credentials import AccessToken + +from azure.mixedreality.authentication._shared.static_access_token_credential import StaticAccessTokenCredential + +class TestStaticAccessTokenCredential: + def test_get_token(self): + token = "My access token" + expiration = 0 + + access_token = AccessToken(token=token, expires_on=expiration) + staticAccessToken = StaticAccessTokenCredential(access_token) + + actual = staticAccessToken.get_token() + + assert access_token == actual diff --git a/sdk/mixedreality/azure-mixedreality-authentication/tests/test_static_access_token_credential_async.py b/sdk/mixedreality/azure-mixedreality-authentication/tests/test_static_access_token_credential_async.py new file mode 100644 index 000000000000..f9b7171460fd --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/tests/test_static_access_token_credential_async.py @@ -0,0 +1,23 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +from azure.core.credentials import AccessToken +from devtools_testutils import AzureTestCase + +from azure.mixedreality.authentication._shared.aio.static_access_token_credential import StaticAccessTokenCredential + +class TestAsyncStaticAccessTokenCredential: + @AzureTestCase.await_prepared_test + async def test_get_token(self): + token = "My access token" + expiration = 0 + + access_token = AccessToken(token=token, expires_on=expiration) + staticAccessToken = StaticAccessTokenCredential(access_token) + + actual = await staticAccessToken.get_token() + + assert access_token == actual diff --git a/sdk/mixedreality/azure-mixedreality-authentication/tests/test_utils.py b/sdk/mixedreality/azure-mixedreality-authentication/tests/test_utils.py new file mode 100644 index 000000000000..17b31bcb87fa --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-authentication/tests/test_utils.py @@ -0,0 +1,60 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +import pytest + +from azure.mixedreality.authentication._utils import generate_cv_base, retrieve_jwt_expiration_timestamp + + +class TestUtils: + def test_generate_cv_base(self): + cv = generate_cv_base() + + assert cv is not None + assert len(cv) == 22 + + def test_generate_cv_base_are_random(self): + cv1 = generate_cv_base() + cv2 = generate_cv_base() + + assert cv1 is not None + assert cv2 is not None + assert cv1 != cv2 + + def test_retrieve_jwt_expiration_timestamp(self): + # Note: The trailing "." on the end indicates an empty signature indicating that this JWT is not signed. + jwt_value = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJlbWFpbCI6IkJvYkBjb250b3NvLmNvbSIsImdpdmVuX25hbWUiOiJCb2IiLCJpc3MiOiJodHRwOi8vRGVmYXVsdC5Jc3N1ZXIuY29tIiwiYXVkIjoiaHR0cDovL0RlZmF1bHQuQXVkaWVuY2UuY29tIiwiaWF0IjoiMTYxMDgxMjI1MCIsIm5iZiI6IjE2MTA4MTI1NTAiLCJleHAiOiIxNjEwODk4NjUwIn0=." + expected_expiration_timestamp = 1610898650 # 1/17/2021 3:50:50 PM UTC + + actual = retrieve_jwt_expiration_timestamp(jwt_value) + + assert actual is not None + assert actual == expected_expiration_timestamp + + def test_retrieve_jwt_expiration_timestamp_invalid_parameter(self): + with pytest.raises(ValueError): + retrieve_jwt_expiration_timestamp(None) + + def test_retrieve_jwt_expiration_timestamp_invalid_structure(self): + # JWT value with missing signature section on the end. + jwt_value = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJlbWFpbCI6IkJvYkBjb250b3NvLmNvbSIsImdpdmVuX25hbWUiOiJCb2IiLCJpc3MiOiJodHRwOi8vRGVmYXVsdC5Jc3N1ZXIuY29tIiwiYXVkIjoiaHR0cDovL0RlZmF1bHQuQXVkaWVuY2UuY29tIiwiaWF0IjoiMTYxMDgxMjI1MCIsIm5iZiI6IjE2MTA4MTI1NTAiLCJleHAiOiIxNjEwODk4NjUwIn0=" + + with pytest.raises(ValueError): + retrieve_jwt_expiration_timestamp(jwt_value) + + def test_retrieve_jwt_expiration_timestamp_invalid_payload(self): + # JWT value with missing payload. + jwt_value = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.." + + with pytest.raises(ValueError): + retrieve_jwt_expiration_timestamp(jwt_value) + + def test_retrieve_jwt_expiration_timestamp_invalid_exp(self): + # JWT value with missing expiration field. + jwt_value = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJlbWFpbCI6IkJvYkBjb250b3NvLmNvbSIsImdpdmVuX25hbWUiOiJCb2IiLCJpc3MiOiJodHRwOi8vRGVmYXVsdC5Jc3N1ZXIuY29tIiwiYXVkIjoiaHR0cDovL0RlZmF1bHQuQXVkaWVuY2UuY29tIiwiaWF0IjoiMTYxMDgxMjI1MCIsIm5iZiI6IjE2MTA4MTI1NTAifQ==." + + with pytest.raises(ValueError): + retrieve_jwt_expiration_timestamp(jwt_value) diff --git a/sdk/mixedreality/azure-mixedreality-nspkg/CHANGELOG.md b/sdk/mixedreality/azure-mixedreality-nspkg/CHANGELOG.md new file mode 100644 index 000000000000..627c355468b5 --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-nspkg/CHANGELOG.md @@ -0,0 +1,5 @@ +# Release History + +## 1.0.0 (Unreleased) + +- Initial release diff --git a/sdk/mixedreality/azure-mixedreality-nspkg/MANIFEST.in b/sdk/mixedreality/azure-mixedreality-nspkg/MANIFEST.in new file mode 100644 index 000000000000..092b64f9159b --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-nspkg/MANIFEST.in @@ -0,0 +1,3 @@ +include *.md +include azure/__init__.py +include azure/mixedreality/__init__.py \ No newline at end of file diff --git a/sdk/mixedreality/azure-mixedreality-nspkg/README.md b/sdk/mixedreality/azure-mixedreality-nspkg/README.md new file mode 100644 index 000000000000..995038c70840 --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-nspkg/README.md @@ -0,0 +1,11 @@ +# Microsoft Azure Mixed Reality SDK for Python + +This is the Microsoft Azure Mixed Reality namespace package. + +This package is not intended to be installed directly by the end user. + +It provides the necessary files for other packages to extend the +azure.mixedreality namespace. + +The complete list of available packages can be found at: +https://aka.ms/azsdk/python/all diff --git a/sdk/mixedreality/azure-mixedreality-nspkg/azure/__init__.py b/sdk/mixedreality/azure-mixedreality-nspkg/azure/__init__.py new file mode 100644 index 000000000000..69e3be50dac4 --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-nspkg/azure/__init__.py @@ -0,0 +1 @@ +__path__ = __import__('pkgutil').extend_path(__path__, __name__) diff --git a/sdk/mixedreality/azure-mixedreality-nspkg/azure/mixedreality/__init__.py b/sdk/mixedreality/azure-mixedreality-nspkg/azure/mixedreality/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/sdk/mixedreality/azure-mixedreality-nspkg/setup.cfg b/sdk/mixedreality/azure-mixedreality-nspkg/setup.cfg new file mode 100644 index 000000000000..3480374bc2f2 --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-nspkg/setup.cfg @@ -0,0 +1,2 @@ +[bdist_wheel] +universal=1 \ No newline at end of file diff --git a/sdk/mixedreality/azure-mixedreality-nspkg/setup.py b/sdk/mixedreality/azure-mixedreality-nspkg/setup.py new file mode 100644 index 000000000000..a80cc7d1344c --- /dev/null +++ b/sdk/mixedreality/azure-mixedreality-nspkg/setup.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python + +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +from setuptools import setup + +setup( + name='azure-mixedreality-nspkg', + version='1.0.0', + description='Microsoft Azure Mixed Reality Namespace Package [Internal]', + long_description=open('README.md', 'r').read(), + license='MIT License', + author='Microsoft Corporation', + author_email='azurepysdk@microsoft.com', + url='https://github.com/Azure/azure-sdk-for-python/', + classifiers=[ + 'Development Status :: 5 - Production/Stable', + + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'License :: OSI Approved :: MIT License', + ], + zip_safe=False, + packages=[ + 'azure.mixedreality', + ], + install_requires=[ + 'azure-nspkg>=2.0.0', + ] +) \ No newline at end of file diff --git a/sdk/mixedreality/ci.yml b/sdk/mixedreality/ci.yml index 991d84a936a7..f3ae04fd79eb 100644 --- a/sdk/mixedreality/ci.yml +++ b/sdk/mixedreality/ci.yml @@ -28,5 +28,9 @@ extends: parameters: ServiceDirectory: mixedreality Artifacts: + - name: azure_mixedreality_nspkg + safeName: azuremixedrealitynspkg - name: azure_mgmt_mixedreality safeName: azuremgmtmixedreality + - name: azure_mixedreality_authentication + safeName: azuremixedrealityauthentication diff --git a/sdk/mixedreality/test-resources.json b/sdk/mixedreality/test-resources.json new file mode 100644 index 000000000000..30e81531cdfd --- /dev/null +++ b/sdk/mixedreality/test-resources.json @@ -0,0 +1,141 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "baseName": { + "type": "string", + "defaultValue": "[resourceGroup().name]", + "metadata": { + "description": "The base resource name." + } + }, + "tenantId": { + "type": "string", + "defaultValue": "72f988bf-86f1-41af-91ab-2d7cd011db47", + "metadata": { + "description": "The tenant ID to which the application and resources belong." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "The location of the resource. By default, this is the same as the resource group." + } + }, + "baseTime": { + "type": "string", + "defaultValue": "[utcNow('u')]" + } + }, + "variables": { + "apiVersion": "2020-05-01", + "asaAccountName": "[concat(parameters('baseName'), '-asa-account')]", + "arrApiVersion": "2020-04-06-preview", + "arrAccountName": "[concat(parameters('baseName'), '-arr-account')]", + "storageApiVersion": "2019-06-01", + "storageAccountName": "[parameters('baseName')]", + "blobContainerName": "test", + "blobContainerResourceName": "[concat(variables('storageAccountName'), '/default/', variables('blobContainerName'))]", + "sasProperties": { + "signedPermission": "rwl", + "signedExpiry": "[dateTimeAdd(parameters('baseTime'), 'P1D')]", + "signedResource": "c", + "canonicalizedResource": "[concat('/blob/', variables('storageAccountName'), '/', variables('blobContainerName'))]" + } + }, + "resources": [ + { + "type": "Microsoft.MixedReality/spatialAnchorsAccounts", + "name": "[variables('asaAccountName')]", + "apiVersion": "[variables('apiVersion')]", + "location": "[parameters('location')]", + "properties": {} + }, + { + "type": "Microsoft.MixedReality/remoteRenderingAccounts", + "name": "[variables('arrAccountName')]", + "apiVersion": "[variables('arrApiVersion')]", + "location": "[parameters('location')]", + "properties": {}, + "identity": { "type": "systemAssigned" } + }, + { + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "[variables('storageApiVersion')]", + "name": "[variables('storageAccountName')]", + "location": "[parameters('location')]", + "sku": { + "name": "Standard_RAGRS", + "tier": "Standard" + }, + "kind": "StorageV2", + "properties": { + "supportsHttpsTrafficOnly": true, + "encryption": { + "keySource": "Microsoft.Storage", + "services": { + "blob": { + "enabled": true + } + }, + }, + "accessTier": "Hot" + } + }, + { + "type": "Microsoft.Storage/storageAccounts/blobServices/containers", + "apiVersion": "[variables('storageApiVersion')]", + "name": "[variables('blobContainerResourceName')]", + "dependsOn": [ + "[variables('storageAccountName')]" + ] + } + ], + "outputs": { + "MIXEDREALITY_ACCOUNT_ID": { + "type": "string", + "value": "[reference(variables('asaAccountName')).accountId]" + }, + "MIXEDREALITY_ACCOUNT_DOMAIN": { + "type": "string", + "value": "[reference(variables('asaAccountName')).accountDomain]" + }, + "MIXEDREALITY_ACCOUNT_KEY": { + "type": "string", + "value": "[listKeys(resourceId('Microsoft.MixedReality/spatialAnchorsAccounts', variables('asaAccountName')), variables('apiVersion')).primaryKey]" + }, + "MIXEDREALITY_ARR_ACCOUNT_ID": { + "type": "string", + "value": "[reference(variables('arrAccountName')).accountId]" + }, + "MIXEDREALITY_ARR_ACCOUNT_DOMAIN": { + "type": "string", + "value": "[reference(variables('arrAccountName')).accountDomain]" + }, + "MIXEDREALITY_ARR_ACCOUNT_KEY": { + "type": "string", + "value": "[listKeys(resourceId('Microsoft.MixedReality/remoteRenderingAccounts', variables('arrAccountName')), variables('arrApiVersion')).primaryKey]" + }, + "MIXEDREALITY_ARR_STORAGE_ACCOUNT_NAME": { + "type": "string", + "value": "[variables('storageAccountName')]" + }, + "MIXEDREALITY_ARR_STORAGE_ACCOUNT_KEY": { + "type": "string", + "value": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), variables('storageApiVersion')).keys[0].value]" + }, + "MIXEDREALITY_ARR_BLOB_CONTAINER_NAME": { + "type": "string", + "value": "[variables('blobContainerName')]" + }, + "MIXEDREALITY_ARR_SAS_TOKEN": { + "type": "string", + "value": "[listServiceSas(variables('storageAccountName'), variables('storageApiVersion'), variables('sasProperties')).serviceSasToken]" + }, + "MIXEDREALITY_ARR_SERVICE_ENDPOINT": { + "type": "string", + "value": "[concat('https://remoterendering.', parameters('location'), '.mixedreality.azure.com')]" + } + } +} \ No newline at end of file diff --git a/sdk/mixedreality/tests.yml b/sdk/mixedreality/tests.yml new file mode 100644 index 000000000000..8755e0615f27 --- /dev/null +++ b/sdk/mixedreality/tests.yml @@ -0,0 +1,13 @@ +trigger: none + +stages: + - template: ../../eng/pipelines/templates/stages/archetype-sdk-tests.yml + parameters: + AllocateResourceGroup: false + ServiceDirectory: mixedreality + DeployArmTemplate: true + EnvVars: + AZURE_CLIENT_ID: $(aad-azure-sdk-test-client-id) + AZURE_CLIENT_SECRET: $(aad-azure-sdk-test-client-secret) + AZURE_TENANT_ID: $(aad-azure-sdk-test-tenant-id) + TEST_MODE: 'RunLiveNoRecord' \ No newline at end of file diff --git a/shared_requirements.txt b/shared_requirements.txt index 37e9dbf4621d..a9cffd7596bc 100644 --- a/shared_requirements.txt +++ b/shared_requirements.txt @@ -84,6 +84,7 @@ azure-mgmt-storage~=2.0 azure-mgmt-subscription~=0.2.0 azure-mgmt-trafficmanager~=0.50.0 azure-mgmt-web~=0.35.0 +azure-mixedreality-nspkg azure-nspkg azure-keyvault-nspkg azure-media-nspkg