├── .env.tmp
├── .gitignore
├── LICENSE
├── README.md
├── go
└── azidext
│ ├── azure_identity_credential_adapter.go
│ ├── azure_identity_credential_adapter_test.go
│ ├── go.mod
│ └── go.sum
├── iac
└── terraform
│ ├── main.tf
│ ├── providers.tf
│ └── variables.tf
├── java
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── com
│ │ └── azure
│ │ └── identity
│ │ └── extensions
│ │ ├── AzureIdentityCredentialAdapter.java
│ │ └── AzureIdentityServiceBusCredential.java
│ └── test
│ └── java
│ └── com
│ └── azure
│ └── identity
│ └── extensions
│ ├── AzureIdentityCredentialAdapterTest.java
│ └── AzureIdentityServiceBusCredentialTest.java
├── js
├── azureIdentityCredentialAdapter.spec.ts
├── azureIdentityCredentialAdapter.ts
├── package.json
└── tsconfig.json
├── net
├── .gitignore
├── Azure.Identity.Extensions.Tests
│ ├── Azure.Identity.Extensions.Tests.csproj
│ ├── Fluent
│ │ └── ResourceGroupTests.cs
│ ├── Mgmt
│ │ ├── AppInsightsTests.cs
│ │ ├── CosmosDbTests.cs
│ │ ├── InteractiveBrowserTests.cs
│ │ ├── ResourceGroupTests.cs
│ │ └── StorageTests.cs
│ └── ServiceBus
│ │ └── ServiceBusTests.cs
└── Azure.Identity.Extensions
│ ├── Azure.Identity.Extensions.csproj
│ ├── Azure.Identity.Extensions.sln
│ ├── AzureIdentityCredentialAdapter.cs
│ ├── AzureIdentityFluentCredentialAdapter.cs
│ ├── AzureIdentityServiceBusCredentialAdapter.cs
│ └── AzureIdentityTokenProvider.cs
└── python
├── .gitignore
├── azure_identity_credential_adapter.py
├── dev_requirements.txt
└── test_azure_identity_credential_adapter.py
/.env.tmp:
--------------------------------------------------------------------------------
1 | AZURE_CLIENT_ID=
2 | AZURE_CLIENT_SECRET=
3 | AZURE_TENANT_ID=
4 |
5 | AZURE_SUBSCRIPTION_ID=
6 |
7 | AZURE_BASE_NAME=azidexttest1
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .env
2 | *.classpath
3 | *.factorypath
4 | *.project
5 | *.prefs
6 | .vscode/*
7 | terraform.
8 | .terraform
9 | terraform.tfstate*
10 | tf.plan
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2020 Jon Gallant
2 |
3 | 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:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | 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.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Azure Identity Extensions
2 |
3 | This repo is a place for us to share ideas and extensions to the Azure Identity libraries.
4 |
5 | > **DISCLAIMER**: The code in this repo is not officially supported or intended for production use. The intention of this repo it to unblock customers who would like to use the Azure.Identity capabilities in the Fluent, Resource Management, and Service Bus SDKs before they have been migrated to the new SDK Azure.Core and officially support TokenCredential. We have included minimal tests in this repo, so please take it upon yourself to fully test this code to ensure it works in your environment.
6 |
7 | ## Languages
8 |
9 | We currently have included examples for [.NET](#.NET), [Java](#Java), [JavaScript/TypeScript](#TypeScript), [Golang](#Golang), and [Python](#Python). Please file an issue if you would like examples for other languages as well.
10 |
11 | ## Usage
12 |
13 | The classes contained in this repo are only meant to be a temporary stopgap between now and when the Resource Management, Fluent, and Service Bus SDKs support Azure.Core. Since those efforts are currently underway, we think it would be best for you to copy the classes in this project to your class instead of releasing them via a package manager.
14 |
15 | 1. Clone the repo `git clone https://github.com/jongio/azidext`
16 | 1. Either reference the project or copy the classes you need into your solution.
17 |
18 | ## .NET
19 |
20 | ### AzureIdentityCredentialAdapter.cs
21 |
22 | The `AzureIdentityCredentialAdapter` class allows you to use all the goodness of `Azure.Identity.DefaultAzureCredential` in the Azure Management libraries. You can use it in place of `ServiceClientCredential` when calling your Azure Management APIs. The Azure Management libraries will be updated to support Azure Identity and Azure Core in early 2020, so this should just be used a a stopgap between now and then.
23 |
24 | ```cmd
25 | dotnet add package Microsoft.Azure.Management.ApplicationInsights --version 0.2.0-preview
26 | ```
27 |
28 | Use `AzureIdentityCredentialAdapter` in place of `ServiceClientCredential`:
29 |
30 | ```csharp
31 | using Azure.Identity.Extensions;
32 | using Microsoft.Azure.Management.ApplicationInsights.Management;
33 |
34 | var appInsightsClient = new ApplicationInsightsManagementClient(new AzureIdentityCredentialAdapter());
35 | ```
36 |
37 | ### AzureIdentityFluentCredentialAdapter.cs
38 |
39 | The `AzureIdentityFluentCredentialAdapter` class allows you to use all the goodness of `Azure.Identity.DefaultAzureCredential` in the [Azure Management **Fluent** libraries](https://github.com/Azure/azure-libraries-for-net). You can use it in place of `AzureCredentials` when calling your Azure Management Fluent APIs.
40 |
41 | ```cmd
42 | dotnet add package Microsoft.Azure.Management.Fluent --version 1.30.0
43 | ```
44 |
45 | Use `AzureIdentityFluentCredentialAdapter` in place of `AzureCredentials`:
46 |
47 | ```csharp
48 | using Azure.Identity.Extensions;
49 | using Microsoft.Azure.Management.ResourceManager.Fluent;
50 |
51 | var creds = new AzureIdentityFluentCredentialAdapter(tenantId, AzureEnvironment.AzureGlobalCloud);
52 |
53 | var resourceGroup = Azure.Authenticate(creds)
54 | .WithSubscription(subId)
55 | .ResourceGroups
56 | .Define(name)
57 | .WithRegion(region)
58 | .Create();
59 | ```
60 |
61 | ### AzureIdentityServiceBusCredentialAdapter.cs
62 |
63 | The `AzureIdentityServiceBusCredentialAdapter` class allows you to use all of the goodness of `DefaultAzureCredential` from [azure-identity](https://mvnrepository.com/artifact/com.azure/azure-identity) with the Service Bus SDKs. Service Bus will officially be supported by the new SDKs soon, this is a stopgap that enables you to use the same credential flow throughout your application.
64 |
65 | ```cmd
66 | dotnet add package Microsoft.Azure.ServiceBus --version 4.1.1
67 | ```
68 |
69 | ```csharp
70 | using Azure.Identity.Extensions;
71 | using Microsoft.Azure.ServiceBus;
72 |
73 | var client = new TopicClient("sbendpoint", "entitypath", new AzureIdentityServiceBusCredentialAdapter());
74 | ```
75 |
76 | ## Testing .NET
77 |
78 | 1. Setup test resources with "Test Setup" section below.
79 | 2. Open the .Tests project and run dotnet build.
80 |
81 | ## Java
82 |
83 | ### AzureIdentityCredentialAdapter.java
84 |
85 | The `AzureIdentityCredentialAdapter` class provides a simple bridge to use `DefaultAzureCredential` from `com.azure` namespace in `com.microsoft.azure` SDKs. This is a convenient mechanism to authenticate all fluent Azure Management Resources and a some data plane SDKs that use `ServiceClientCredential` family of credentials.
86 |
87 | To use this type, just copy `AzureIdentityCredentialAdapter.java` file located in `java/src/main/java/com/azure/identity/extensions` directory into your application and make necessary package name updates.
88 |
89 | After you have created this type, you can reference it in your code as shown below:
90 |
91 | ```java
92 | Azure azure = Azure.authenticate(new AzureIdentityCredentialAdapter(tenantId)).withDefaultSubscription();
93 | ```
94 |
95 | Above code will provide an instance of `Azure` fluent type from which you can access all Azure Resource Managers.
96 |
97 | ### AzureIdentityServiceBusCredential.java
98 |
99 | The `AzureIdentityServiceBusCredential` class allows you to use all of the goodness of `DefaultAzureCredential` from [azure-identity](https://mvnrepository.com/artifact/com.azure/azure-identity) with the Service Bus SDKs. Service Bus will officially be supported by the new SDKs soon, this is a stopgap that enables you to use the same credential flow throughout your application.
100 |
101 | To use this type, just copy `AzureIdentityServiceBusCredential.java` file located in `java/src/main/java/com/azure/identity/extensions` directory into your application and make necessary package name updates.
102 |
103 | Sample code to create a new topic client:
104 |
105 | ```java
106 | ClientSettings clientSettings = new ClientSettings(new AzureIdentityServiceBusCredential());
107 | TopicClient topicClient = new TopicClient("servicebus-endpoint", "servicebus-entitypath", clientSettings);
108 | ```
109 |
110 | #### Testing AzureIdentityCredentialAdapter
111 |
112 | This repository has a test class called `AzureIdentityCredentialAdapterTest` that tests creation of a storage account, listing all storage accounts in a resource group to validate successful creation, then deleting the account created earlier in this test and listing again to ensure successful deletion.
113 |
114 | To run `AzureIdentityCredentialAdapterTest`, ensure you have `.env` file created and accessible from your classpath. Your `.env` file should have the following properties set:
115 |
116 | - AZURE_TENANT_ID
117 | - AZURE_STORAGE_ACCOUNT_NAME
118 | - AZURE_RESOURCE_GROUP
119 |
120 | Once you have the `.env` file configured, run the test using JUnit 5 runner.
121 |
122 | ## TypeScript
123 |
124 | ### AzureIdentityCredentialAdapter
125 |
126 | The `AzureIdentityCredentialAdapter` class provides a simple adapter to use TokenCredential from [@azure/identity](https://www.npmjs.com/package/@azure/identity) with any SDK
127 | that accepts ServiceClientCredentials from packages like `@azure/arm-*` or `@azure/ms-rest-*`.
128 |
129 | To use this type, just copy `azureIdentityCredentialAdapter.ts`, `package.json`, and `tsconfig.json` file located in `js` directory into your application and install packages in `package.json`.
130 |
131 | After you have created this type, you can reference it in your code as shown below:
132 |
133 | ```TypeScript
134 | # Example for azure-mgmt-resource client
135 | const cred = new AzureIdentityCredentialAdapter();
136 | const client = new ResourceManagementClient(cred, subscriptionId);
137 | ```
138 |
139 | The above code will instantiate an Azure.Identity compatible TokenCredential object based on DefaultAzureCredential and pass that to the ResourceManagementClient instance.
140 |
141 | #### Testing AzureIdentityCredentialAdapter
142 |
143 | This repository has a test that creates a resource group in a given subscription.
144 |
145 | To run this test, ensure you have `.env` file created and accessible from the root of your repo. Your `.env` file should have the following properties set:
146 |
147 | - AZURE_SUBSCRIPTION_ID
148 | - AZURE_TENANT_ID
149 | - AZURE_CLIENT_ID
150 | - AZURE_CLIENT_SECRET
151 |
152 | Install the test dependencies using npm under the path of `package.json`.
153 |
154 | ```
155 | npm i
156 | ```
157 | Then install mocha.
158 |
159 | ```
160 | npm i -g mocha
161 | ```
162 | compile ts to js using tsc.
163 |
164 | ```
165 | tsc azureIdentityCredentialAdapter.spec.ts --esModuleInterop
166 | ```
167 |
168 | Once you have the `.env` file configured and js compiled, run the test simply calling `mocha azureIdentityCredentialAdapter.spec.js --timeout 10000`.
169 |
170 | ## Golang
171 |
172 | ### NewAzureIdentityCredentialAdapter
173 |
174 | The `NewAzureIdentityCredentialAdapter` function allows you to use all the goodness of `azidentity` in the Azure Management libraries. You can use it in place of `Authorizer` when calling your Azure Management APIs.
175 |
176 | To use this type, just import package github.com/jongio/azidext/go/azidext and using follow command to get package.
177 |
178 | ```
179 | go get -u github.com/jongio/azidext/go/azidext
180 | ```
181 |
182 | Use `NewAzureIdentityCredentialAdapter` in place of `Authorizer`:
183 |
184 | ```go
185 | import "github.com/jongio/azidext/go/azidext"
186 |
187 | groupsClient := resources.NewGroupsClient(subscriptionID)
188 | a, err := NewDefaultAzureCredentialAdapter(nil)
189 | if err != nil {
190 | }
191 | groupsClient.Authorizer = a
192 | ```
193 |
194 | #### Testing NewAzureIdentityCredentialAdapter
195 |
196 | This repository has a test that creates a resource group in a given subscription.
197 |
198 | To run this test, ensure you have `.env` file created and accessible from the root of your repo. Your `.env` file should have the following properties set:
199 |
200 | - AZURE_SUBSCRIPTION_ID
201 | - AZURE_TENANT_ID
202 | - AZURE_CLIENT_ID
203 | - AZURE_CLIENT_SECRET
204 |
205 | Once you have the `.env` file configured, run the test simply calling `go test`.
206 |
207 | ## Python
208 |
209 | ### AzureIdentityCredentialAdapter
210 |
211 | The `AzureIdentityCredentialAdapter` class provides a simple adapter to use any credential from [azure-identity](https://pypi.org/project/azure-identity/) with any SDK
212 | that accepts credentials from `azure.common.credentials` or `msrestazure.azure_active_directory`.
213 |
214 | To use this type, just copy the `azure_identity_credential_adapter.py` file located in the `python` directory into your application and make necessary package name updates.
215 |
216 | After you have created this type, you can reference it in your code as shown below:
217 |
218 | ```python
219 | # Example for azure-mgmt-resource client
220 | from azure_identity_credential_adapter import AzureIdentityCredentialAdapter
221 | credentials = AzureIdentityCredentialAdapter()
222 |
223 | from azure.mgmt.resource import ResourceManagementClient
224 | client = ResourceManagementClient(credentials, subscription_id)
225 | ```
226 |
227 | The above code will provide an instance of `ResourceManagementClient` from which you can access ARM resources. You can use any type of client, like `ComputeManagementClient`, etc.
228 |
229 | #### Testing AzureIdentityCredentialAdapter
230 |
231 | This repository has a test that list the resource groups in a given subscription.
232 |
233 | To run this test, ensure you have `.env` file created and accessible from the root of your repo. Your `.env` file should have the following properties set:
234 |
235 | - AZURE_SUBSCRIPTION_ID
236 | - AZURE_TENANT_ID
237 | - AZURE_CLIENT_ID
238 | - AZURE_CLIENT_SECRET
239 |
240 | General recommendation for Python development is to use a Virtual Environment. For more information, see https://docs.python.org/3/tutorial/venv.html
241 |
242 | Install and initialize the virtual environment with the "venv" module on Python 3 (you must install [virtualenv](https://pypi.python.org/pypi/virtualenv) for Python 2.7):
243 |
244 | ```
245 | python -m venv venv # Might be "python3" or "py -3.6" depending on your Python installation
246 | source venv/bin/activate # Linux shell (Bash, ZSH, etc.) only
247 | ./venv/scripts/activate # PowerShell only
248 | ./venv/scripts/activate.bat # Windows CMD only
249 | ```
250 |
251 | Install the test dependencies using pip
252 |
253 | ```
254 | pip install -r python\dev_requirements.txt
255 | ```
256 |
257 | Once you have the `.env` file configured and the venv loaded, run the tests simply calling `pytest`
258 |
259 |
260 | More to come soon. Please file a GitHub issue with any questions/suggestions.
261 |
262 |
263 | ## Test Setup
264 |
265 | 1. Create a service principal with `az ad sp create-for-rbac`
266 | 2. Rename .env.tmp to .env and update the the following values from the SP
267 |
268 | `AZURE_CLIENT_ID=appId`
269 |
270 | `AZURE_CLIENT_SECRET=password`
271 |
272 | `AZURE_TENANT_ID=tenantId`
273 |
274 | 3. Run `az account show` to get your subscription id and update the .env file with that.
275 |
276 | `AZURE_SUBSCRIPTION_ID=`
277 |
278 | 4. Deploy the Service Bus resources with terraform files in iac/terraform
279 |
280 | - Open variables.tf and change the basename value to something unique.
281 | - Run the following commands:
282 | - `terraform init`
283 | - `terraform plan --out tf.plan`
284 | - `terraform apply tf.plan`
285 |
286 | 5. Update AZURE_BASE_NAME in .env file to the base name you used for terraform deployment
287 |
288 | - AZURE_BASE_NAME=azidexttest1
289 |
290 |
291 | 6. See each language "Test" section above for instructions on how to run the tests.
292 |
--------------------------------------------------------------------------------
/go/azidext/azure_identity_credential_adapter.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | // Licensed under the MIT License.
3 |
4 | package azidext
5 |
6 | import (
7 | "errors"
8 | "net/http"
9 |
10 | "github.com/Azure/azure-sdk-for-go/sdk/azcore"
11 | "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
12 | "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
13 | "github.com/Azure/azure-sdk-for-go/sdk/azidentity"
14 | "github.com/Azure/go-autorest/autorest"
15 | )
16 |
17 | // NewTokenCredentialAdapter is used to adapt an azcore.TokenCredential to an autorest.Authorizer
18 | func NewTokenCredentialAdapter(credential azcore.TokenCredential, scopes []string) autorest.Authorizer {
19 | tkPolicy := runtime.NewBearerTokenPolicy(credential, scopes, nil)
20 | return &policyAdapter{
21 | pl: runtime.NewPipeline("azidext", "v0.4.0", runtime.PipelineOptions{
22 | PerRetry: []policy.Policy{tkPolicy, nullPolicy{}},
23 | }, nil),
24 | }
25 | }
26 |
27 | type policyAdapter struct {
28 | pl runtime.Pipeline
29 | }
30 |
31 | // WithAuthorization implements the autorest.Authorizer interface for type policyAdapter.
32 | func (ca *policyAdapter) WithAuthorization() autorest.PrepareDecorator {
33 | return func(p autorest.Preparer) autorest.Preparer {
34 | return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) {
35 | r, err := p.Prepare(r)
36 | if err != nil {
37 | return r, err
38 | }
39 | // create a dummy request
40 | req, err := runtime.NewRequest(r.Context(), r.Method, r.URL.String())
41 | if err != nil {
42 | return r, err
43 | }
44 | resp, err := ca.pl.Do(req)
45 | // if the authentication failed due to invalid/missing credentials
46 | // return a wrapped error so the retry policy won't kick in.
47 | type nonRetriable interface {
48 | NonRetriable()
49 | }
50 | var nre nonRetriable
51 | if errors.As(err, &nre) {
52 | return r, &tokenRefreshError{
53 | inner: err,
54 | }
55 | }
56 | // some other error
57 | if err != nil {
58 | return r, err
59 | }
60 | // copy the authorization header to the real request
61 | const authHeader = "Authorization"
62 | r.Header.Set(authHeader, resp.Request.Header.Get(authHeader))
63 | return r, err
64 | })
65 | }
66 | }
67 |
68 | // DefaultManagementScope is the default credential scope for Azure Resource Management.
69 | const DefaultManagementScope = "https://management.azure.com//.default"
70 |
71 | // DefaultAzureCredentialOptions contains credential and authentication policy options.
72 | type DefaultAzureCredentialOptions struct {
73 | // DefaultCredential contains configuration options passed to azidentity.NewDefaultAzureCredential().
74 | // Set this to nil to accept the underlying default behavior.
75 | DefaultCredential *azidentity.DefaultAzureCredentialOptions
76 |
77 | // Scopes contains the list of permission scopes required for the token.
78 | // Setting this to nil will use the DefaultManagementScope when acquiring a token.
79 | Scopes []string
80 | }
81 |
82 | // NewDefaultAzureCredentialAdapter adapts azcore.NewDefaultAzureCredential to an autorest.Authorizer.
83 | func NewDefaultAzureCredentialAdapter(options *DefaultAzureCredentialOptions) (autorest.Authorizer, error) {
84 | if options == nil {
85 | options = &DefaultAzureCredentialOptions{
86 | Scopes: []string{DefaultManagementScope},
87 | }
88 | }
89 | cred, err := azidentity.NewDefaultAzureCredential(options.DefaultCredential)
90 | if err != nil {
91 | return nil, err
92 | }
93 | return NewTokenCredentialAdapter(cred, options.Scopes), nil
94 | }
95 |
96 | // dummy policy to terminate the pipeline
97 | type nullPolicy struct{}
98 |
99 | func (nullPolicy) Do(req *policy.Request) (*http.Response, error) {
100 | return &http.Response{Request: req.Raw(), StatusCode: http.StatusOK}, nil
101 | }
102 |
103 | // error type returned to prevent the retry policy from retrying the request
104 | type tokenRefreshError struct {
105 | inner error
106 | }
107 |
108 | func (t *tokenRefreshError) Error() string {
109 | return t.inner.Error()
110 | }
111 |
112 | func (t *tokenRefreshError) Response() *http.Response {
113 | return nil
114 | }
115 |
116 | func (t *tokenRefreshError) Unwrap() error {
117 | return t.inner
118 | }
119 |
--------------------------------------------------------------------------------
/go/azidext/azure_identity_credential_adapter_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | // Licensed under the MIT License.
3 |
4 | package azidext
5 |
6 | import (
7 | "context"
8 | "math/rand"
9 | "os"
10 | "strconv"
11 | "testing"
12 | "time"
13 |
14 | "github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
15 | "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2019-05-01/resources"
16 | "github.com/joho/godotenv"
17 | )
18 |
19 | func Test_CreateResouceGroup(t *testing.T) {
20 | err := godotenv.Load("../../.env")
21 | if err != nil {
22 | t.Fatalf("Loading environment variable from .env fail, error: %v", err)
23 | }
24 | subscriptionID := os.Getenv("AZURE_SUBSCRIPTION_ID")
25 | if subscriptionID == "" {
26 | t.Fatalf("Missing environment variable AZURE_SUBSCRIPTION_ID")
27 | }
28 | groupsClient := resources.NewGroupsClient(subscriptionID)
29 | a, err := NewDefaultAzureCredentialAdapter(nil)
30 | if err != nil {
31 | t.Fatalf("Create DefaultAzureIdentityTokenAapter fail, error: %v", err)
32 | }
33 | groupsClient.Authorizer = a
34 | resourceGroupname := "azidextrg" + strconv.FormatInt(int64(rand.New(rand.NewSource(time.Now().UnixNano())).Int31n(100000000)), 10)
35 |
36 | _, err = groupsClient.CreateOrUpdate(
37 | context.Background(),
38 | resourceGroupname,
39 | resources.Group{
40 | Location: to.Ptr("Central US"),
41 | })
42 | if err != nil {
43 | t.Fatalf("Create ResourceGroup fail, error: %v", err)
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/go/azidext/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/jongio/azidext/go/azidext
2 |
3 | go 1.18
4 |
5 | require (
6 | github.com/Azure/azure-sdk-for-go v46.0.0+incompatible
7 | github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1
8 | github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0
9 | github.com/Azure/go-autorest/autorest v0.11.29
10 | github.com/joho/godotenv v1.3.0
11 | )
12 |
13 | require (
14 | github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect
15 | github.com/Azure/go-autorest v14.2.0+incompatible // indirect
16 | github.com/Azure/go-autorest/autorest/adal v0.9.23 // indirect
17 | github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
18 | github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
19 | github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
20 | github.com/Azure/go-autorest/logger v0.2.1 // indirect
21 | github.com/Azure/go-autorest/tracing v0.6.0 // indirect
22 | github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 // indirect
23 | github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
24 | github.com/google/uuid v1.3.0 // indirect
25 | github.com/kylelemons/godebug v1.1.0 // indirect
26 | github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
27 | golang.org/x/crypto v0.10.0 // indirect
28 | golang.org/x/net v0.11.0 // indirect
29 | golang.org/x/sys v0.9.0 // indirect
30 | golang.org/x/text v0.10.0 // indirect
31 | )
32 |
--------------------------------------------------------------------------------
/go/azidext/go.sum:
--------------------------------------------------------------------------------
1 | github.com/Azure/azure-sdk-for-go v46.0.0+incompatible h1:4qlEOCDcDQZTGczYGzbGYCdJfVpZLIs8AEo5+MoXBPw=
2 | github.com/Azure/azure-sdk-for-go v46.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
3 | github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1 h1:SEy2xmstIphdPwNBUi7uhvjyjhVKISfwjfOJmuy7kg4=
4 | github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
5 | github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 h1:vcYCAze6p19qBW7MhZybIsqD8sMV8js0NyQM8JDnVtg=
6 | github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U=
7 | github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY=
8 | github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM=
9 | github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
10 | github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
11 | github.com/Azure/go-autorest/autorest v0.11.29 h1:I4+HL/JDvErx2LjyzaVxllw2lRDB5/BT2Bm4g20iqYw=
12 | github.com/Azure/go-autorest/autorest v0.11.29/go.mod h1:ZtEzC4Jy2JDrZLxvWs8LrBWEBycl1hbT1eknI8MtfAs=
13 | github.com/Azure/go-autorest/autorest/adal v0.9.22/go.mod h1:XuAbAEUv2Tta//+voMI038TrJBqjKam0me7qR+L8Cmk=
14 | github.com/Azure/go-autorest/autorest/adal v0.9.23 h1:Yepx8CvFxwNKpH6ja7RZ+sKX+DWYNldbLiALMC3BTz8=
15 | github.com/Azure/go-autorest/autorest/adal v0.9.23/go.mod h1:5pcMqFkdPhviJdlEy3kC/v1ZLnQl0MH6XA5YCcMhy4c=
16 | github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
17 | github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
18 | github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
19 | github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw=
20 | github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU=
21 | github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk=
22 | github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE=
23 | github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac=
24 | github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E=
25 | github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg=
26 | github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
27 | github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
28 | github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
29 | github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 h1:OBhqkivkhkMqLPymWEppkm7vgPQY2XsHoEkaMQ0AdZY=
30 | github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o=
31 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
32 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
33 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
34 | github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
35 | github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
36 | github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
37 | github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
38 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
39 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
40 | github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
41 | github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
42 | github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
43 | github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
44 | github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
45 | github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
46 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
47 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
48 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
49 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
50 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
51 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
52 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
53 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
54 | github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
55 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
56 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
57 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
58 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
59 | golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
60 | golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
61 | golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
62 | golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
63 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
64 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
65 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
66 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
67 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
68 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
69 | golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=
70 | golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
71 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
72 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
73 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
74 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
75 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
76 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
77 | golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
78 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
79 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
80 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
81 | golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
82 | golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
83 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
84 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
85 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
86 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
87 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
88 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
89 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
90 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
91 | golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
92 | golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
93 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
94 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
95 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
96 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
97 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
98 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
99 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
100 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
101 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
102 |
--------------------------------------------------------------------------------
/iac/terraform/main.tf:
--------------------------------------------------------------------------------
1 | resource "azurerm_resource_group" "rg" {
2 | name = "${var.basename}rg"
3 | location = var.location
4 | }
5 |
6 | resource "azurerm_servicebus_namespace" "sbns" {
7 | name = "${var.basename}sbns"
8 | location = azurerm_resource_group.rg.location
9 | resource_group_name = azurerm_resource_group.rg.name
10 | sku = "Standard"
11 | }
12 |
13 | resource "azurerm_servicebus_topic" "sbtopic" {
14 | name = "topic1"
15 | resource_group_name = azurerm_resource_group.rg.name
16 | namespace_name = azurerm_servicebus_namespace.sbns.name
17 | }
18 |
19 | resource "azurerm_servicebus_subscription" "sbsub" {
20 | name = "sub1"
21 | resource_group_name = azurerm_resource_group.rg.name
22 | namespace_name = azurerm_servicebus_namespace.sbns.name
23 | topic_name = azurerm_servicebus_topic.sbtopic.name
24 | max_delivery_count = 1
25 | }
26 |
27 | output "sb_connection_string" {
28 | value = azurerm_servicebus_namespace.sbns.default_primary_connection_string
29 | }
30 |
--------------------------------------------------------------------------------
/iac/terraform/providers.tf:
--------------------------------------------------------------------------------
1 | #Set the terraform required version
2 | terraform {
3 | required_version = ">= 0.12.6"
4 | }
5 |
6 | # Configure the Azure Provider
7 | provider "azurerm" {
8 | # It is recommended to pin to a given version of the Provider
9 | version = "~>2"
10 | features {}
11 | }
12 |
13 | provider "random" {
14 | version = "~>2"
15 | }
16 |
17 | provider "null" {
18 | version = "~> 2.1"
19 | }
20 |
21 | # Data
22 |
23 | # Make client_id, tenant_id, subscription_id and object_id variables
24 | data "azurerm_client_config" "current" {}
25 |
--------------------------------------------------------------------------------
/iac/terraform/variables.tf:
--------------------------------------------------------------------------------
1 | variable "basename" {
2 | type = string
3 | description = "The base name for all resources"
4 | default = "azidexttest1"
5 | }
6 |
7 | variable "location" {
8 | type = string
9 | description = "Azure region where to create resources."
10 | default = "West US"
11 | }
12 |
--------------------------------------------------------------------------------
/java/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.jongallant
8 | azure-extensions
9 | 1.0-SNAPSHOT
10 |
11 |
12 |
13 | com.microsoft.azure
14 | azure-servicebus
15 | 3.1.4
16 |
17 |
18 | com.azure
19 | azure-identity
20 | 1.1.0
21 |
22 |
23 | com.microsoft.rest
24 | client-runtime
25 | 1.7.0
26 |
27 |
28 | com.microsoft.azure
29 | azure
30 | 1.30.0
31 |
32 |
33 |
34 |
35 | io.github.cdimascio
36 | java-dotenv
37 | 5.2.0
38 | test
39 |
40 |
41 | org.junit.jupiter
42 | junit-jupiter
43 | 5.5.2
44 |
45 |
46 | org.junit.jupiter
47 | junit-jupiter-api
48 | 5.5.2
49 |
50 |
51 | org.junit.jupiter
52 | junit-jupiter-engine
53 | 5.5.2
54 |
55 |
56 |
57 |
58 |
59 |
60 | org.apache.maven.plugins
61 | maven-compiler-plugin
62 |
63 | 11
64 | 11
65 |
66 |
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/java/src/main/java/com/azure/identity/extensions/AzureIdentityCredentialAdapter.java:
--------------------------------------------------------------------------------
1 | package com.azure.identity.extensions;
2 |
3 | import com.azure.core.credential.AccessToken;
4 | import com.azure.core.credential.TokenCredential;
5 | import com.azure.core.credential.TokenRequestContext;
6 | import com.azure.identity.DefaultAzureCredentialBuilder;
7 | import com.microsoft.azure.AzureEnvironment;
8 | import com.microsoft.azure.credentials.AzureTokenCredentials;
9 | import com.microsoft.rest.credentials.ServiceClientCredentials;
10 | import java.util.Map;
11 | import java.util.concurrent.ConcurrentHashMap;
12 |
13 | /**
14 | * This class provides a simple extension to use {@link TokenCredential} from com.azure:azure-identity library to
15 | * use with legacy Azure SDKs that accept {@link ServiceClientCredentials} family of credentials for authentication.
16 | */
17 | public class AzureIdentityCredentialAdapter extends AzureTokenCredentials {
18 |
19 | public static final String MANAGEMENT_SCOPE = "https://management.azure.com/.default";
20 | private final TokenCredential tokenCredential;
21 | private final Map accessTokenCache = new ConcurrentHashMap<>();
22 | private final String[] scopes;
23 |
24 | /**
25 | * Creates an instance with {@link AzureEnvironment#AZURE} environment.
26 | *
27 | * @param tenantId The tenant id for the token credentials.
28 | */
29 | public AzureIdentityCredentialAdapter(String tenantId) {
30 | this(AzureEnvironment.AZURE, tenantId);
31 | }
32 |
33 | /**
34 | * Creates an instance with {@link AzureEnvironment#AZURE} environment and given {@link TokenCredential}
35 | * instance.
36 | *
37 | * @param tenantId The tenant id for the token credentials.
38 | * @param tokenCredential The {@link TokenCredential} instance to use.
39 | */
40 | public AzureIdentityCredentialAdapter(String tenantId, TokenCredential tokenCredential) {
41 | this(AzureEnvironment.AZURE, tenantId, tokenCredential, new String[]{MANAGEMENT_SCOPE});
42 | }
43 |
44 | /**
45 | * Creates an instance for the given environment.
46 | *
47 | * @param environment The {@link AzureEnvironment} for which the credentials will be created.
48 | * @param tenantId The tenant id for the token credentials.
49 | */
50 | public AzureIdentityCredentialAdapter(AzureEnvironment environment, String tenantId) {
51 | this(environment, tenantId, new DefaultAzureCredentialBuilder().build(), new String[]{MANAGEMENT_SCOPE});
52 | }
53 |
54 | /**
55 | * Creates an instance for the given environment, tenant id and {@link TokenCredential}.
56 | *
57 | * @param environment The {@link AzureEnvironment} for which the credentials will be created.
58 | * @param tenantId The tenant id for the token credentials.
59 | * @param tokenCredential The {@link TokenCredential} instance to use.
60 | * @param scopes The scopes for the credential.
61 | */
62 | public AzureIdentityCredentialAdapter(AzureEnvironment environment, String tenantId,
63 | TokenCredential tokenCredential, String[] scopes) {
64 | super(environment, tenantId);
65 | this.tokenCredential = tokenCredential;
66 | this.scopes = scopes;
67 | }
68 |
69 | @Override
70 | public String getToken(String endpoint) {
71 | if (!accessTokenCache.containsKey(endpoint) || accessTokenCache.get(endpoint).isExpired()) {
72 | accessTokenCache.put(endpoint,
73 | this.tokenCredential.getToken(new TokenRequestContext().addScopes(scopes)).block());
74 | }
75 | return accessTokenCache.get(endpoint).getToken();
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/java/src/main/java/com/azure/identity/extensions/AzureIdentityServiceBusCredential.java:
--------------------------------------------------------------------------------
1 | package com.azure.identity.extensions;
2 |
3 | import com.azure.core.credential.TokenCredential;
4 | import com.microsoft.azure.servicebus.security.SecurityToken;
5 | import com.microsoft.azure.servicebus.security.SecurityTokenType;
6 | import com.microsoft.azure.servicebus.security.TokenProvider;
7 | import com.azure.core.credential.TokenRequestContext;
8 | import com.azure.identity.DefaultAzureCredentialBuilder;
9 | import java.util.Map;
10 | import java.util.concurrent.ConcurrentHashMap;
11 | import reactor.core.publisher.Mono;
12 |
13 | import java.time.Instant;
14 | import java.util.concurrent.CompletableFuture;
15 |
16 | /**
17 | * A Service Bus {@link TokenProvider} that uses {@link TokenCredential} that is available in com
18 | * .azure:azure-identity module. This class provides a convenient mechanism to authenticate service bus using the latest
19 | * Azure Identity SDK.
20 | */
21 | public class AzureIdentityServiceBusCredential extends TokenProvider {
22 |
23 | private static final String SERVICEBUS_SCOPE = "https://servicebus.azure.net/.default";
24 | private final TokenCredential tokenCredential;
25 | private final Map tokenCache = new ConcurrentHashMap<>();
26 |
27 | /**
28 | * Creates an instance of DefaultAzureServiceBusCredential.
29 | */
30 | public AzureIdentityServiceBusCredential() {
31 | this(new DefaultAzureCredentialBuilder().build());
32 | }
33 |
34 | /**
35 | * Creates an instance of DefaultAzureServiceBusCredential using the provided {@link TokenCredential}.
36 | *
37 | * @param tokenCredential The {@link TokenCredential} to use.
38 | */
39 | public AzureIdentityServiceBusCredential(TokenCredential tokenCredential) {
40 | this.tokenCredential = tokenCredential;
41 | }
42 |
43 | /**
44 | * {@inheritDoc}
45 | */
46 | @Override
47 | public CompletableFuture getSecurityTokenAsync(String audience) {
48 | TokenRequestContext tokenRequestContext = new TokenRequestContext().addScopes(SERVICEBUS_SCOPE);
49 | if (tokenCache.containsKey(audience) && tokenCache.get(audience).getValidUntil().isAfter(Instant.now())) {
50 | return Mono.just(tokenCache.get(audience)).toFuture();
51 | }
52 |
53 | return tokenCredential
54 | .getToken(tokenRequestContext)
55 | .flatMap(accessToken -> {
56 | SecurityToken securityToken = new SecurityToken(SecurityTokenType.JWT, audience, accessToken.getToken(),
57 | Instant.now(), accessToken.getExpiresAt().toInstant());
58 | tokenCache.put(audience, securityToken);
59 | return Mono.just(securityToken);
60 | }).toFuture();
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/java/src/test/java/com/azure/identity/extensions/AzureIdentityCredentialAdapterTest.java:
--------------------------------------------------------------------------------
1 | package com.azure.identity.extensions;
2 |
3 | import com.azure.identity.extensions.AzureIdentityCredentialAdapter;
4 | import com.microsoft.azure.management.Azure;
5 | import com.microsoft.azure.management.resources.fluentcore.arm.Region;
6 | import com.microsoft.azure.management.storage.StorageAccount;
7 | import io.github.cdimascio.dotenv.Dotenv;
8 | import org.junit.jupiter.api.BeforeAll;
9 | import org.junit.jupiter.api.Test;
10 | import rx.observers.TestSubscriber;
11 |
12 | /**
13 | * Tests for {@link AzureIdentityCredentialAdapter}. These tests run against live Azure services to ensure credentials
14 | * work correctly.
15 | */
16 | public class AzureIdentityCredentialAdapterTest {
17 |
18 | private static final String AZURE_TENANT_ID = "AZURE_TENANT_ID";
19 | private static final String AZURE_BASE_NAME = "AZURE_BASE_NAME";
20 | private static Dotenv ENVIRONMENT;
21 |
22 | /**
23 | * Loads environment variables from a properties file named ".env" that should be available in the classpath.
24 | *
25 | * Template for this file can be found at the root of this repository name .env.tmp
26 | */
27 | @BeforeAll
28 | static void loadEnvironmentProperties() {
29 | ENVIRONMENT = Dotenv.load();
30 | }
31 |
32 | /**
33 | * Tests creating a new storage account, list all accounts and verify that the new account is part of the list and
34 | * then deletes the newly created account. This test requires the environment variables (either via .env file or
35 | * through system environment variables) to contain the tenant id, the resource group and storage account name that
36 | * can be used to create and delete.
37 | *
38 | * @throws Exception If there's any error during any of the operations.
39 | */
40 | @Test
41 | public void testStorageAccountCreation() throws Exception {
42 | String tenantId = ENVIRONMENT.get(AZURE_TENANT_ID);
43 | String baseName = ENVIRONMENT.get(AZURE_BASE_NAME);
44 | // Create an instance of fluent Azure type which can be used for managing various Azure resources
45 | Azure azure = Azure.authenticate(new AzureIdentityCredentialAdapter(tenantId)).withDefaultSubscription();
46 |
47 | // create a test storage account
48 |
49 | StorageAccount createdAccount =
50 | azure.storageAccounts().define(baseName + "storageaccount").withRegion(Region.US_WEST2)
51 | .withExistingResourceGroup(baseName + "rg").create();
52 |
53 | // list all storage accounts and verify that the test account created above is part of the list
54 | TestSubscriber testCreateSubscriber = new TestSubscriber<>();
55 | azure.storageAccounts()
56 | .listAsync()
57 | .filter(account -> account.id().equals(createdAccount.id()))
58 | .map(account -> account.id())
59 | .subscribe(testCreateSubscriber);
60 |
61 | testCreateSubscriber.assertCompleted();
62 | testCreateSubscriber.assertNoErrors();
63 | testCreateSubscriber.assertValue(createdAccount.id());
64 |
65 | // delete the test storage account
66 | azure.storageAccounts().deleteByIdAsync(createdAccount.id()).get();
67 |
68 | // list all storage accounts and verify that the test storage account no longer exists
69 | TestSubscriber testDeleteSubscriber = new TestSubscriber<>();
70 | azure.storageAccounts()
71 | .listAsync()
72 | .filter(account -> account.id().equals(createdAccount.id()))
73 | .subscribe(testDeleteSubscriber);
74 |
75 | testDeleteSubscriber.assertCompleted();
76 | testDeleteSubscriber.assertNoErrors();
77 | testDeleteSubscriber.assertNoValues();
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/java/src/test/java/com/azure/identity/extensions/AzureIdentityServiceBusCredentialTest.java:
--------------------------------------------------------------------------------
1 | package com.azure.identity.extensions;
2 |
3 | import com.azure.identity.DefaultAzureCredential;
4 | import com.azure.identity.extensions.AzureIdentityServiceBusCredential;
5 | import com.microsoft.azure.servicebus.ClientSettings;
6 | import com.microsoft.azure.servicebus.ExceptionPhase;
7 | import com.microsoft.azure.servicebus.IMessage;
8 | import com.microsoft.azure.servicebus.IMessageHandler;
9 | import com.microsoft.azure.servicebus.Message;
10 | import com.microsoft.azure.servicebus.ReceiveMode;
11 | import com.microsoft.azure.servicebus.SubscriptionClient;
12 | import com.microsoft.azure.servicebus.TopicClient;
13 | import com.microsoft.azure.servicebus.primitives.ServiceBusException;
14 | import io.github.cdimascio.dotenv.Dotenv;
15 | import java.nio.charset.StandardCharsets;
16 | import java.util.UUID;
17 | import java.util.concurrent.CompletableFuture;
18 | import java.util.concurrent.CountDownLatch;
19 | import java.util.concurrent.Executors;
20 | import java.util.concurrent.TimeUnit;
21 | import org.junit.jupiter.api.Assertions;
22 | import org.junit.jupiter.api.BeforeAll;
23 | import org.junit.jupiter.api.Test;
24 |
25 | /**
26 | * Unit test for {@link AzureIdentityServiceBusCredential}.
27 | */
28 | public class AzureIdentityServiceBusCredentialTest {
29 |
30 | private static final String AZURE_BASE_NAME = "AZURE_BASE_NAME";
31 | private static Dotenv ENVIRONMENT;
32 |
33 | /**
34 | * Loads environment variables from a properties file named ".env" that should be available in the classpath. The "
35 | * .env" file should have definition for AZURE_BASE_NAME which has the prefix for Service Bus namespace.
36 | *
37 | * Template for this file can be found at the root of this repository name .env.temp
38 | */
39 | @BeforeAll
40 | static void loadEnvironmentProperties() {
41 | ENVIRONMENT = Dotenv.load();
42 | }
43 |
44 | /**
45 | * Unit test to use {@link DefaultAzureCredential} to create a {@link TopicClient} and send a message.
46 | *
47 | * @throws ServiceBusException
48 | * @throws InterruptedException
49 | */
50 | @Test
51 | public void testDefaultCredential() throws Exception {
52 | ClientSettings clientSettings = new ClientSettings(new AzureIdentityServiceBusCredential());
53 | String endpoint = ENVIRONMENT.get(AZURE_BASE_NAME) + "sbns";
54 | String topicName = "topic1";
55 | TopicClient topicClient = new TopicClient(endpoint, topicName, clientSettings);
56 | String message = "hello " + UUID.randomUUID().toString().substring(0, 8);
57 |
58 | // send a message
59 | topicClient.sendAsync(new Message(message)).get();
60 | topicClient.closeAsync().get();
61 |
62 | // receive the message
63 | String subscriptionPath = "sub1";
64 | SubscriptionClient subscriptionClient = new SubscriptionClient(endpoint, subscriptionPath, clientSettings, ReceiveMode.RECEIVEANDDELETE);
65 |
66 | CountDownLatch countDownLatch = new CountDownLatch(1); // expect to receive just 1 message
67 | subscriptionClient.registerMessageHandler(
68 | new IMessageHandler() {
69 | @Override
70 | public CompletableFuture onMessageAsync(IMessage iMessage) {
71 | Assertions.assertEquals(message,
72 | new String(iMessage.getMessageBody().getBinaryData().get(0), StandardCharsets.UTF_8));
73 | countDownLatch.countDown();
74 | return CompletableFuture.completedFuture(null);
75 | }
76 |
77 | @Override
78 | public void notifyException(Throwable throwable, ExceptionPhase exceptionPhase) {
79 | Assertions.fail(throwable);
80 | }
81 | }, Executors.newCachedThreadPool());
82 | Assertions.assertTrue(countDownLatch.await(5, TimeUnit.SECONDS));
83 | subscriptionClient.closeAsync().get();
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/js/azureIdentityCredentialAdapter.spec.ts:
--------------------------------------------------------------------------------
1 | import * as assert from "assert";
2 | import * as dotenv from "dotenv";
3 | import { ResourceManagementClient } from "@azure/arm-resources";
4 | import { AzureIdentityCredentialAdapter } from "./azureIdentityCredentialAdapter";
5 |
6 | dotenv.config({ path: "../.env" });
7 |
8 | const subscriptionId = process.env.AZURE_SUBSCRIPTION_ID;
9 |
10 | describe("AzureIdentityCredentialAdapter ", function() {
11 | it("create resoucegroup and delete it", async () => {
12 | const cred = new AzureIdentityCredentialAdapter();
13 | const client = new ResourceManagementClient(cred, subscriptionId);
14 | const name = "azidextrg" + (Math.floor(Math.random()* 89999999) + 10000000);
15 | const result = await client.resourceGroups.createOrUpdate(name, { location: 'westus', name : name });
16 | assert.equal(result.name, name);
17 | await (await client.resourceGroups.beginDeleteMethod(name)).pollUntilFinished;
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/js/azureIdentityCredentialAdapter.ts:
--------------------------------------------------------------------------------
1 | import { ServiceClientCredentials } from "@azure/ms-rest-js";
2 | import { DefaultAzureCredential, TokenCredential } from "@azure/identity";
3 | import { Constants as MSRestConstants, WebResource } from "@azure/ms-rest-js";
4 | import { TokenResponse } from "@azure/ms-rest-nodeauth/dist/lib/credentials/tokenClientCredentials";
5 |
6 | const DEFAULT_AUTHORIZATION_SCHEME = "Bearer";
7 |
8 | /**
9 | * This class provides a simple extension to use {@link TokenCredential} from com.azure:azure-identity library to
10 | * use with legacy Azure SDKs that accept {@link ServiceClientCredentials} family of credentials for authentication.
11 | */
12 | export class AzureIdentityCredentialAdapter implements ServiceClientCredentials {
13 | private azureTokenCredential: TokenCredential;
14 | private scopes: string | string[];
15 | constructor(
16 | azureTokenCredential = new DefaultAzureCredential(),
17 | scopes: string | string[] = "https://management.azure.com/.default",
18 | ) {
19 | this.azureTokenCredential = azureTokenCredential;
20 | this.scopes = scopes;
21 | }
22 |
23 | public async getToken(): Promise{
24 | const accessToken = (await this.azureTokenCredential.getToken(this.scopes));
25 | const result: TokenResponse = {
26 | accessToken: accessToken.token,
27 | tokenType: DEFAULT_AUTHORIZATION_SCHEME,
28 | expiresOn: accessToken.expiresOnTimestamp,
29 | };
30 | return result;
31 | }
32 |
33 | public async signRequest(webResource: WebResource) {
34 | const tokenResponse = await this.getToken();
35 | webResource.headers.set(MSRestConstants.HeaderConstants.AUTHORIZATION, `${tokenResponse.tokenType} ${tokenResponse.accessToken}`);
36 | return Promise.resolve(webResource);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/js/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "azureidentitycredentialadapter",
3 | "version": "1.0.0",
4 | "description": "1.0.0",
5 | "main": "index.js",
6 | "scripts": {
7 | "build": "tsc",
8 | "dev": "tsc --watch & nodemon dist",
9 | "test": "tsc && mocha dist/*.spec.js --timeout 10000",
10 | "lint": "eslint src --ext ts",
11 | "tsc": "tsc",
12 | "start": "node dist/azureIdentityCredentialAdapter.js"
13 | },
14 | "dependencies": {
15 | "@azure/arm-resources": "^2.1.0",
16 | "@azure/identity": "^1.1.0",
17 | "@azure/ms-rest-nodeauth": "^2.0.5",
18 | "dotenv": "^8.2.0"
19 | },
20 | "devDependencies": {
21 | "mocha": "^7.2.0",
22 | "@types/mocha": "^7.0.2"
23 | },
24 | "author": "",
25 | "license": "MIT"
26 | }
27 |
--------------------------------------------------------------------------------
/js/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es6",
4 | "module": "commonjs",
5 | "outDir": "dist",
6 | "sourceMap": true,
7 | "esModuleInterop": true
8 | },
9 | "include": [
10 | "*.ts"
11 | ],
12 | "exclude": [
13 | "node_modules"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/net/.gitignore:
--------------------------------------------------------------------------------
1 | .env
2 |
3 | nugetpush.bat
4 |
5 | .vscode
6 | .vscode/*
7 | !.vscode/settings.json
8 | !.vscode/tasks.json
9 | !.vscode/launch.json
10 | !.vscode/extensions.json
11 | *.code-workspace
12 |
13 | ## Ignore Visual Studio temporary files, build results, and
14 | ## files generated by popular Visual Studio add-ons.
15 | ##
16 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
17 |
18 | # User-specific files
19 | *.rsuser
20 | *.suo
21 | *.user
22 | *.userosscache
23 | *.sln.docstates
24 |
25 | # User-specific files (MonoDevelop/Xamarin Studio)
26 | *.userprefs
27 |
28 | # Mono auto generated files
29 | mono_crash.*
30 |
31 | # Build results
32 | [Dd]ebug/
33 | [Dd]ebugPublic/
34 | [Rr]elease/
35 | [Rr]eleases/
36 | x64/
37 | x86/
38 | [Aa][Rr][Mm]/
39 | [Aa][Rr][Mm]64/
40 | bld/
41 | [Bb]in/
42 | [Oo]bj/
43 | [Ll]og/
44 | [Ll]ogs/
45 |
46 | # Visual Studio 2015/2017 cache/options directory
47 | .vs/
48 | # Uncomment if you have tasks that create the project's static files in wwwroot
49 | #wwwroot/
50 |
51 | # Visual Studio 2017 auto generated files
52 | Generated\ Files/
53 |
54 | # MSTest test Results
55 | [Tt]est[Rr]esult*/
56 | [Bb]uild[Ll]og.*
57 |
58 | # NUnit
59 | *.VisualState.xml
60 | TestResult.xml
61 | nunit-*.xml
62 |
63 | # Build Results of an ATL Project
64 | [Dd]ebugPS/
65 | [Rr]eleasePS/
66 | dlldata.c
67 |
68 | # Benchmark Results
69 | BenchmarkDotNet.Artifacts/
70 |
71 | # .NET Core
72 | project.lock.json
73 | project.fragment.lock.json
74 | artifacts/
75 |
76 | # StyleCop
77 | StyleCopReport.xml
78 |
79 | # Files built by Visual Studio
80 | *_i.c
81 | *_p.c
82 | *_h.h
83 | *.ilk
84 | *.meta
85 | *.obj
86 | *.iobj
87 | *.pch
88 | *.pdb
89 | *.ipdb
90 | *.pgc
91 | *.pgd
92 | *.rsp
93 | *.sbr
94 | *.tlb
95 | *.tli
96 | *.tlh
97 | *.tmp
98 | *.tmp_proj
99 | *_wpftmp.csproj
100 | *.log
101 | *.vspscc
102 | *.vssscc
103 | .builds
104 | *.pidb
105 | *.svclog
106 | *.scc
107 |
108 | # Chutzpah Test files
109 | _Chutzpah*
110 |
111 | # Visual C++ cache files
112 | ipch/
113 | *.aps
114 | *.ncb
115 | *.opendb
116 | *.opensdf
117 | *.sdf
118 | *.cachefile
119 | *.VC.db
120 | *.VC.VC.opendb
121 |
122 | # Visual Studio profiler
123 | *.psess
124 | *.vsp
125 | *.vspx
126 | *.sap
127 |
128 | # Visual Studio Trace Files
129 | *.e2e
130 |
131 | # TFS 2012 Local Workspace
132 | $tf/
133 |
134 | # Guidance Automation Toolkit
135 | *.gpState
136 |
137 | # ReSharper is a .NET coding add-in
138 | _ReSharper*/
139 | *.[Rr]e[Ss]harper
140 | *.DotSettings.user
141 |
142 | # TeamCity is a build add-in
143 | _TeamCity*
144 |
145 | # DotCover is a Code Coverage Tool
146 | *.dotCover
147 |
148 | # AxoCover is a Code Coverage Tool
149 | .axoCover/*
150 | !.axoCover/settings.json
151 |
152 | # Visual Studio code coverage results
153 | *.coverage
154 | *.coveragexml
155 |
156 | # NCrunch
157 | _NCrunch_*
158 | .*crunch*.local.xml
159 | nCrunchTemp_*
160 |
161 | # MightyMoose
162 | *.mm.*
163 | AutoTest.Net/
164 |
165 | # Web workbench (sass)
166 | .sass-cache/
167 |
168 | # Installshield output folder
169 | [Ee]xpress/
170 |
171 | # DocProject is a documentation generator add-in
172 | DocProject/buildhelp/
173 | DocProject/Help/*.HxT
174 | DocProject/Help/*.HxC
175 | DocProject/Help/*.hhc
176 | DocProject/Help/*.hhk
177 | DocProject/Help/*.hhp
178 | DocProject/Help/Html2
179 | DocProject/Help/html
180 |
181 | # Click-Once directory
182 | publish/
183 |
184 | # Publish Web Output
185 | *.[Pp]ublish.xml
186 | *.azurePubxml
187 | # Note: Comment the next line if you want to checkin your web deploy settings,
188 | # but database connection strings (with potential passwords) will be unencrypted
189 | *.pubxml
190 | *.publishproj
191 |
192 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
193 | # checkin your Azure Web App publish settings, but sensitive information contained
194 | # in these scripts will be unencrypted
195 | PublishScripts/
196 |
197 | # NuGet Packages
198 | *.nupkg
199 | # NuGet Symbol Packages
200 | *.snupkg
201 | # The packages folder can be ignored because of Package Restore
202 | **/[Pp]ackages/*
203 | # except build/, which is used as an MSBuild target.
204 | !**/[Pp]ackages/build/
205 | # Uncomment if necessary however generally it will be regenerated when needed
206 | #!**/[Pp]ackages/repositories.config
207 | # NuGet v3's project.json files produces more ignorable files
208 | *.nuget.props
209 | *.nuget.targets
210 |
211 | # Microsoft Azure Build Output
212 | csx/
213 | *.build.csdef
214 |
215 | # Microsoft Azure Emulator
216 | ecf/
217 | rcf/
218 |
219 | # Windows Store app package directories and files
220 | AppPackages/
221 | BundleArtifacts/
222 | Package.StoreAssociation.xml
223 | _pkginfo.txt
224 | *.appx
225 | *.appxbundle
226 | *.appxupload
227 |
228 | # Visual Studio cache files
229 | # files ending in .cache can be ignored
230 | *.[Cc]ache
231 | # but keep track of directories ending in .cache
232 | !?*.[Cc]ache/
233 |
234 | # Others
235 | ClientBin/
236 | ~$*
237 | *~
238 | *.dbmdl
239 | *.dbproj.schemaview
240 | *.jfm
241 | *.pfx
242 | *.publishsettings
243 | orleans.codegen.cs
244 |
245 | # Including strong name files can present a security risk
246 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
247 | #*.snk
248 |
249 | # Since there are multiple workflows, uncomment next line to ignore bower_components
250 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
251 | #bower_components/
252 |
253 | # RIA/Silverlight projects
254 | Generated_Code/
255 |
256 | # Backup & report files from converting an old project file
257 | # to a newer Visual Studio version. Backup files are not needed,
258 | # because we have git ;-)
259 | _UpgradeReport_Files/
260 | Backup*/
261 | UpgradeLog*.XML
262 | UpgradeLog*.htm
263 | ServiceFabricBackup/
264 | *.rptproj.bak
265 |
266 | # SQL Server files
267 | *.mdf
268 | *.ldf
269 | *.ndf
270 |
271 | # Business Intelligence projects
272 | *.rdl.data
273 | *.bim.layout
274 | *.bim_*.settings
275 | *.rptproj.rsuser
276 | *- [Bb]ackup.rdl
277 | *- [Bb]ackup ([0-9]).rdl
278 | *- [Bb]ackup ([0-9][0-9]).rdl
279 |
280 | # Microsoft Fakes
281 | FakesAssemblies/
282 |
283 | # GhostDoc plugin setting file
284 | *.GhostDoc.xml
285 |
286 | # Node.js Tools for Visual Studio
287 | .ntvs_analysis.dat
288 | node_modules/
289 |
290 | # Visual Studio 6 build log
291 | *.plg
292 |
293 | # Visual Studio 6 workspace options file
294 | *.opt
295 |
296 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
297 | *.vbw
298 |
299 | # Visual Studio LightSwitch build output
300 | **/*.HTMLClient/GeneratedArtifacts
301 | **/*.DesktopClient/GeneratedArtifacts
302 | **/*.DesktopClient/ModelManifest.xml
303 | **/*.Server/GeneratedArtifacts
304 | **/*.Server/ModelManifest.xml
305 | _Pvt_Extensions
306 |
307 | # Paket dependency manager
308 | .paket/paket.exe
309 | paket-files/
310 |
311 | # FAKE - F# Make
312 | .fake/
313 |
314 | # CodeRush personal settings
315 | .cr/personal
316 |
317 | # Python Tools for Visual Studio (PTVS)
318 | __pycache__/
319 | *.pyc
320 |
321 | # Cake - Uncomment if you are using it
322 | # tools/**
323 | # !tools/packages.config
324 |
325 | # Tabs Studio
326 | *.tss
327 |
328 | # Telerik's JustMock configuration file
329 | *.jmconfig
330 |
331 | # BizTalk build output
332 | *.btp.cs
333 | *.btm.cs
334 | *.odx.cs
335 | *.xsd.cs
336 |
337 | # OpenCover UI analysis results
338 | OpenCover/
339 |
340 | # Azure Stream Analytics local run output
341 | ASALocalRun/
342 |
343 | # MSBuild Binary and Structured Log
344 | *.binlog
345 |
346 | # NVidia Nsight GPU debugger configuration file
347 | *.nvuser
348 |
349 | # MFractors (Xamarin productivity tool) working folder
350 | .mfractor/
351 |
352 | # Local History for Visual Studio
353 | .localhistory/
354 |
355 | # BeatPulse healthcheck temp database
356 | healthchecksdb
357 |
358 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
359 | MigrationBackup/
360 |
361 | # Ionide (cross platform F# VS Code tools) working folder
362 | .ionide/
363 |
--------------------------------------------------------------------------------
/net/Azure.Identity.Extensions.Tests/Azure.Identity.Extensions.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 |
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/net/Azure.Identity.Extensions.Tests/Fluent/ResourceGroupTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.Azure.Management.ResourceManager.Fluent;
3 | using DotNetEnv;
4 | using Azure.Identity.Extensions;
5 | using Xunit;
6 | using System.Threading.Tasks;
7 | using static DotNetEnv.Env;
8 |
9 | namespace Azure.Identity.Extensions.Tests.Fluent
10 | {
11 | public class ResourceGroupTests
12 | {
13 | [Fact]
14 | public async Task CheckIfResourceGroupExistsTest()
15 | {
16 | Env.Load("../../../../../.env");
17 |
18 | var creds = new AzureIdentityFluentCredentialAdapter(
19 | Environment.GetEnvironmentVariable("AZURE_TENANT_ID"),
20 | AzureEnvironment.AzureGlobalCloud);
21 |
22 | var name = Guid.NewGuid().ToString("n").Substring(0, 8);
23 |
24 | var resourceGroupExists = await Microsoft.Azure.Management.Fluent.Azure.Authenticate(creds).
25 | WithSubscription(Environment.GetEnvironmentVariable("AZURE_SUBSCRIPTION_ID")).
26 | ResourceGroups.ContainAsync(name);
27 |
28 | Assert.False(resourceGroupExists);
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/net/Azure.Identity.Extensions.Tests/Mgmt/AppInsightsTests.cs:
--------------------------------------------------------------------------------
1 | using DotNetEnv;
2 | using Microsoft.Azure.Management.ApplicationInsights.Management;
3 | using Microsoft.Azure.Management.ApplicationInsights.Management.Models;
4 | using Microsoft.Azure.Management.ResourceManager;
5 | using Microsoft.Azure.Management.ResourceManager.Models;
6 | using System;
7 | using Xunit;
8 |
9 | namespace Azure.Identity.Extensions.Tests.Mgmt
10 | {
11 | public class AppInsightsTests
12 | {
13 | [Fact]
14 | public async void CreateAndDeleteAppInsightsTest()
15 | {
16 | Env.Load("../../../../../.env");
17 |
18 | var baseName = Environment.GetEnvironmentVariable("AZURE_BASE_NAME");
19 | var rgName = string.Format("{0}rg", baseName);
20 |
21 | // App Insights
22 | var client = new ApplicationInsightsManagementClient(new AzureIdentityCredentialAdapter());
23 | client.SubscriptionId = Environment.GetEnvironmentVariable("AZURE_SUBSCRIPTION_ID");
24 |
25 | var component = new ApplicationInsightsComponent("westus", "web", "web");
26 | var aiName = "appinsightsname" + Guid.NewGuid().ToString("n").Substring(0, 8);
27 |
28 | component = await client.Components.CreateOrUpdateAsync(rgName, aiName, component);
29 |
30 | Assert.NotNull(component.CreationDate);
31 |
32 | await client.Components.DeleteAsync(rgName, aiName);
33 |
34 |
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/net/Azure.Identity.Extensions.Tests/Mgmt/CosmosDbTests.cs:
--------------------------------------------------------------------------------
1 | using DotNetEnv;
2 | using Microsoft.Azure.Management.CosmosDB;
3 | using Microsoft.Azure.Management.CosmosDB.Models;
4 | using System;
5 | using System.Collections.Generic;
6 | using Xunit;
7 |
8 | namespace Azure.Identity.Extensions.Tests.Mgmt
9 | {
10 | public class CosmosDBTests
11 | {
12 | [Fact]
13 | public async void CheckCosmosNameExistsTest()
14 | {
15 | Env.Load("../../../../../.env");
16 |
17 |
18 | var client = new CosmosDBManagementClient(new AzureIdentityCredentialAdapter());
19 | client.SubscriptionId = Environment.GetEnvironmentVariable("AZURE_SUBSCRIPTION_ID");
20 |
21 | var name = "cosmos" + Guid.NewGuid().ToString("n").Substring(0, 8);
22 |
23 | var results = await client.DatabaseAccounts.CheckNameExistsAsync(name);
24 |
25 | Assert.False(results);
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/net/Azure.Identity.Extensions.Tests/Mgmt/InteractiveBrowserTests.cs:
--------------------------------------------------------------------------------
1 | using DotNetEnv;
2 | using Microsoft.Azure.Management.Storage;
3 | using System;
4 | using Xunit;
5 | using Azure.Identity;
6 |
7 | namespace Azure.Identity.Extensions.Tests.Mgmt
8 | {
9 | public class InteractiveBrowserTests
10 | {
11 | [Fact(Skip = "Requires user interaction")]
12 | public async void CheckIfStorageNameAvailableWithInteractiveBrowserTest()
13 | {
14 | // Pre-req: Storage account created.
15 | Env.Load("../../../../../.env");
16 | Environment.SetEnvironmentVariable("AZURE_CLIENT_ID", "");
17 | Environment.SetEnvironmentVariable("AZURE_CLIENT_SECRET", "");
18 | Environment.SetEnvironmentVariable("AZURE_TENANT_ID", "");
19 |
20 | var client = new StorageManagementClient(new AzureIdentityCredentialAdapter(new DefaultAzureCredential(true)));
21 | client.SubscriptionId = Environment.GetEnvironmentVariable("AZURE_SUBSCRIPTION_ID");
22 |
23 | var name = "azidext" + Guid.NewGuid().ToString("n").Substring(0, 8);
24 |
25 | var nameAvailable = await client.StorageAccounts.CheckNameAvailabilityAsync(name);
26 |
27 | Assert.True(nameAvailable.NameAvailable);
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/net/Azure.Identity.Extensions.Tests/Mgmt/ResourceGroupTests.cs:
--------------------------------------------------------------------------------
1 | using DotNetEnv;
2 | using Microsoft.Azure.Management.ResourceManager;
3 | using Microsoft.Azure.Management.ResourceManager.Models;
4 | using System;
5 | using Xunit;
6 |
7 | namespace Azure.Identity.Extensions.Tests.Mgmt
8 | {
9 | public class ResourceGroupTests
10 | {
11 | [Fact]
12 | public async void CreateAndDeleteResourceGroupTest()
13 | {
14 | Env.Load("../../../../../.env");
15 |
16 | var client = new ResourceManagementClient(new AzureIdentityCredentialAdapter());
17 | client.SubscriptionId = Environment.GetEnvironmentVariable("AZURE_SUBSCRIPTION_ID");
18 |
19 | var name = "azidextrg" + Guid.NewGuid().ToString("n").Substring(0, 8);
20 |
21 | var rg = new ResourceGroup(location:"westus", name:name);
22 |
23 | var result = await client.ResourceGroups.CreateOrUpdateAsync(name, rg);
24 |
25 | Assert.Equal(result.Name, name);
26 |
27 | await client.ResourceGroups.DeleteAsync(name);
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/net/Azure.Identity.Extensions.Tests/Mgmt/StorageTests.cs:
--------------------------------------------------------------------------------
1 | using DotNetEnv;
2 | using Microsoft.Azure.Management.Storage;
3 | using System;
4 | using Xunit;
5 |
6 | namespace Azure.Identity.Extensions.Tests.Mgmt
7 | {
8 | public class StorageTests
9 | {
10 | [Fact]
11 | public async void CheckIfStorageNameAvailableTest()
12 | {
13 | // Pre-req: Storage account created.
14 | Env.Load("../../../../../.env");
15 |
16 | var client = new StorageManagementClient(new AzureIdentityCredentialAdapter());
17 |
18 | client.SubscriptionId = Environment.GetEnvironmentVariable("AZURE_SUBSCRIPTION_ID");
19 |
20 | var name = "azidext" + Guid.NewGuid().ToString("n").Substring(0, 8);
21 |
22 | var nameAvailable = await client.StorageAccounts.CheckNameAvailabilityAsync(name);
23 |
24 | Assert.True(nameAvailable.NameAvailable);
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/net/Azure.Identity.Extensions.Tests/ServiceBus/ServiceBusTests.cs:
--------------------------------------------------------------------------------
1 | using DotNetEnv;
2 | using Microsoft.Azure.Management.ServiceBus;
3 | using Microsoft.Azure.ServiceBus;
4 | using System;
5 | using System.Text;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 | using Xunit;
9 |
10 | namespace Azure.Identity.Extensions.Tests.ServiceBus
11 | {
12 | public class ServiceBusTests
13 | {
14 | [Fact]
15 | public async void TopicClientTests()
16 | {
17 | Env.Load("../../../../../.env");
18 |
19 | var baseName = Environment.GetEnvironmentVariable("AZURE_BASE_NAME");
20 | var sbEndpoint = string.Format("{0}sbns.servicebus.windows.net", baseName);
21 |
22 | var client = new TopicClient(sbEndpoint,
23 | "topic1",
24 | new AzureIdentityServiceBusCredentialAdapter());
25 |
26 | var messageText = "Hello World " + Guid.NewGuid().ToString("n").Substring(0, 8);
27 |
28 | await client.SendAsync(new Message(Encoding.UTF8.GetBytes(messageText)));
29 |
30 | await client.CloseAsync();
31 |
32 | var subscription = new SubscriptionClient(sbEndpoint,
33 | "topic1",
34 | "sub1",
35 | new AzureIdentityServiceBusCredentialAdapter());
36 |
37 | var messageHandlerOptions = new MessageHandlerOptions((ExceptionReceivedEventArgs exceptionReceivedEventArgs) =>
38 | {
39 | Console.WriteLine($"Message handler encountered an exception {exceptionReceivedEventArgs.Exception}.");
40 | var context = exceptionReceivedEventArgs.ExceptionReceivedContext;
41 | Console.WriteLine("Exception context for troubleshooting:");
42 | Console.WriteLine($"- Endpoint: {context.Endpoint}");
43 | Console.WriteLine($"- Entity Path: {context.EntityPath}");
44 | Console.WriteLine($"- Executing Action: {context.Action}");
45 | return Task.CompletedTask;
46 | })
47 | {
48 | // Maximum number of concurrent calls to the callback ProcessMessagesAsync(), set to 1 for simplicity.
49 | // Set it according to how many messages the application wants to process in parallel.
50 | MaxConcurrentCalls = 1,
51 |
52 | // Indicates whether MessagePump should automatically complete the messages after returning from User Callback.
53 | // False below indicates the Complete will be handled by the User Callback as in `ProcessMessagesAsync` below.
54 | AutoComplete = false
55 | };
56 |
57 | subscription.RegisterMessageHandler(async (Message message, CancellationToken cancellationToken) =>
58 | {
59 | var body = Encoding.UTF8.GetString(message.Body);
60 | Console.WriteLine($"Received message: SequenceNumber:{message.SystemProperties.SequenceNumber} Body:{body}");
61 |
62 | Assert.Equal(body, messageText);
63 | // Complete the message so that it is not received again.
64 | // This can be done only if the subscriptionClient is created in ReceiveMode.PeekLock mode (which is the default).
65 |
66 | await subscription.CompleteAsync(message.SystemProperties.LockToken);
67 | }, messageHandlerOptions);
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/net/Azure.Identity.Extensions/Azure.Identity.Extensions.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 | Azure.Identity.Extensions
6 | A library that extends the base Azure.Identity functionality.
7 | 0.0.5-preview
8 | Jon Gallant
9 | Jon Gallant
10 | MIT
11 | https://github.com/jongio/azidext
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/net/Azure.Identity.Extensions/Azure.Identity.Extensions.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.30406.217
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Identity.Extensions", "Azure.Identity.Extensions.csproj", "{6DFCDFA1-A414-4CB3-986E-0DE4F4CEEF0D}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Identity.Extensions.Tests", "..\Azure.Identity.Extensions.Tests\Azure.Identity.Extensions.Tests.csproj", "{22DBEED5-53E9-45DB-B174-13949FFD1D97}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {6DFCDFA1-A414-4CB3-986E-0DE4F4CEEF0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {6DFCDFA1-A414-4CB3-986E-0DE4F4CEEF0D}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {6DFCDFA1-A414-4CB3-986E-0DE4F4CEEF0D}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {6DFCDFA1-A414-4CB3-986E-0DE4F4CEEF0D}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {22DBEED5-53E9-45DB-B174-13949FFD1D97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {22DBEED5-53E9-45DB-B174-13949FFD1D97}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {22DBEED5-53E9-45DB-B174-13949FFD1D97}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {22DBEED5-53E9-45DB-B174-13949FFD1D97}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {2F7656B0-B457-404B-92FC-F27CB9E2EB31}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/net/Azure.Identity.Extensions/AzureIdentityCredentialAdapter.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Rest;
2 | using Azure.Core;
3 |
4 | namespace Azure.Identity.Extensions
5 | {
6 | public class AzureIdentityCredentialAdapter : TokenCredentials
7 | {
8 | public AzureIdentityCredentialAdapter(string[] scopes = null) : base(new AzureIdentityTokenProvider(scopes))
9 | {
10 | }
11 |
12 | public AzureIdentityCredentialAdapter(TokenCredential tokenCredential, string[] scopes = null) : base(new AzureIdentityTokenProvider(tokenCredential, scopes))
13 | {
14 |
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/net/Azure.Identity.Extensions/AzureIdentityFluentCredentialAdapter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Concurrent;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Net.Http;
6 | using System.Text.RegularExpressions;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 | using Microsoft.Azure.Management.ResourceManager.Fluent;
10 | using Microsoft.Azure.Management.ResourceManager.Fluent.Authentication;
11 | using Microsoft.Rest;
12 | using Microsoft.Rest.Azure.Authentication;
13 | using Azure.Core;
14 | using Azure.Identity;
15 |
16 |
17 | namespace Azure.Identity.Extensions
18 | {
19 | public class AzureIdentityFluentCredentialAdapter : AzureCredentials
20 | {
21 | private IDictionary credentialsCache = new ConcurrentDictionary();
22 | private TokenCredential tokenCredential;
23 |
24 | public AzureIdentityFluentCredentialAdapter(TokenCredential tokenCredential, string tenantId, AzureEnvironment environment) : base(default(DeviceCredentialInformation), tenantId, environment)
25 | {
26 | this.tokenCredential = tokenCredential;
27 | }
28 |
29 | public AzureIdentityFluentCredentialAdapter(string tenantId, AzureEnvironment environment) : base(default(DeviceCredentialInformation), tenantId, environment)
30 | {
31 | this.tokenCredential = new DefaultAzureCredential();
32 | }
33 |
34 | public async override Task ProcessHttpRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken)
35 | {
36 |
37 | // BEING COPY FROM FLUENT
38 | var adSettings = new ActiveDirectoryServiceSettings
39 | {
40 | AuthenticationEndpoint = new Uri(Environment.AuthenticationEndpoint),
41 | TokenAudience = new Uri(Environment.ManagementEndpoint),
42 | ValidateAuthority = true
43 | };
44 |
45 | string url = request.RequestUri.ToString();
46 | if (url.StartsWith(Environment.GraphEndpoint, StringComparison.OrdinalIgnoreCase))
47 | {
48 | adSettings.TokenAudience = new Uri(Environment.GraphEndpoint);
49 | }
50 |
51 | string host = request.RequestUri.Host;
52 | if (host.EndsWith(Environment.KeyVaultSuffix, StringComparison.OrdinalIgnoreCase))
53 | {
54 | var resource = new Uri(Regex.Replace(Environment.KeyVaultSuffix, "^.", "https://"));
55 | if (credentialsCache.ContainsKey(new Uri(Regex.Replace(Environment.KeyVaultSuffix, "^.", "https://"))))
56 | {
57 | adSettings.TokenAudience = resource;
58 | }
59 | else
60 | {
61 | using (var r = new HttpRequestMessage(request.Method, url))
62 | {
63 | var response = await new HttpClient().SendAsync(r).ConfigureAwait(false);
64 |
65 | if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized && response.Headers.WwwAuthenticate != null)
66 | {
67 | var header = response.Headers.WwwAuthenticate.ElementAt(0).ToString();
68 | var regex = new Regex("authorization=\"([^\"]+)\"");
69 | var match = regex.Match(header);
70 | adSettings.AuthenticationEndpoint = new Uri(match.Groups[1].Value);
71 | regex = new Regex("resource=\"([^\"]+)\"");
72 | match = regex.Match(header);
73 | adSettings.TokenAudience = new Uri(match.Groups[1].Value);
74 | }
75 | }
76 | }
77 | }
78 |
79 | // END COPY FROM FLUENT
80 |
81 | if (!credentialsCache.ContainsKey(adSettings.TokenAudience))
82 | {
83 | credentialsCache[adSettings.TokenAudience] = new AzureIdentityCredentialAdapter(this.tokenCredential);
84 | }
85 | await credentialsCache[adSettings.TokenAudience].ProcessHttpRequestAsync(request, cancellationToken);
86 | }
87 | }
88 | }
--------------------------------------------------------------------------------
/net/Azure.Identity.Extensions/AzureIdentityServiceBusCredentialAdapter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using Microsoft.Azure.ServiceBus;
5 | using Microsoft.Azure.ServiceBus.Primitives;
6 | using Azure.Core;
7 | using Azure.Identity;
8 |
9 |
10 | namespace Azure.Identity.Extensions
11 | {
12 | public class AzureIdentityServiceBusCredentialAdapter : ITokenProvider
13 | {
14 | private TokenCredential tokenCredential;
15 |
16 | public AzureIdentityServiceBusCredentialAdapter() : this(new DefaultAzureCredential())
17 | {
18 | }
19 |
20 | public AzureIdentityServiceBusCredentialAdapter(TokenCredential tokenCredential)
21 | {
22 | this.tokenCredential = tokenCredential;
23 | }
24 |
25 | public async Task GetTokenAsync(string appliesTo, TimeSpan timeout)
26 | {
27 | var cts = new CancellationTokenSource();
28 | cts.CancelAfter((int)timeout.TotalMilliseconds);
29 |
30 | var token = await new AzureIdentityTokenProvider(
31 | this.tokenCredential,
32 | new string[] { "https://servicebus.azure.net/.default" })
33 | .GetTokenAsync(cts.Token);
34 |
35 | var appliesToUri = new Uri(appliesTo);
36 |
37 | return new JsonSecurityToken(token.Token, appliesToUri.Host);
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/net/Azure.Identity.Extensions/AzureIdentityTokenProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net.Http.Headers;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 | using Microsoft.Rest;
6 | using Azure.Core;
7 | using Azure.Identity;
8 |
9 |
10 | namespace Azure.Identity.Extensions
11 | {
12 | public class AzureIdentityTokenProvider : ITokenProvider
13 | {
14 | private AccessToken? accessToken;
15 | private static readonly TimeSpan ExpirationThreshold = TimeSpan.FromMinutes(5);
16 | private string[] scopes;
17 |
18 | private TokenCredential tokenCredential;
19 |
20 | public AzureIdentityTokenProvider(string[] scopes = null) : this(new DefaultAzureCredential(), scopes)
21 | {
22 | }
23 |
24 | public AzureIdentityTokenProvider(TokenCredential tokenCredential, string[] scopes = null)
25 | {
26 | if (scopes == null || scopes.Length == 0)
27 | {
28 | scopes = new string[] { "https://management.azure.com/.default" };
29 | }
30 |
31 | this.scopes = scopes;
32 | this.tokenCredential = tokenCredential;
33 | }
34 |
35 | public virtual async Task GetAuthenticationHeaderAsync(CancellationToken cancellationToken)
36 | {
37 | var accessToken = await GetTokenAsync(cancellationToken);
38 | return new AuthenticationHeaderValue("Bearer", accessToken.Token);
39 | }
40 |
41 | public virtual async Task GetTokenAsync(CancellationToken cancellationToken)
42 | {
43 | if (!this.accessToken.HasValue || AccessTokenExpired)
44 | {
45 | this.accessToken = await this.tokenCredential.GetTokenAsync(new TokenRequestContext(this.scopes), cancellationToken).ConfigureAwait(false);
46 | }
47 |
48 | return this.accessToken.Value;
49 | }
50 |
51 | protected virtual bool AccessTokenExpired
52 | {
53 | get { return !this.accessToken.HasValue ? true : (DateTime.UtcNow + ExpirationThreshold >= this.accessToken.Value.ExpiresOn); }
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/python/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__
2 | *.pyc
--------------------------------------------------------------------------------
/python/azure_identity_credential_adapter.py:
--------------------------------------------------------------------------------
1 | # ------------------------------------
2 | # Copyright (c) Microsoft Corporation.
3 | # Licensed under the MIT License.
4 | # ------------------------------------
5 |
6 | # Adapt credentials from azure-identity to be compatible with SDK that needs msrestazure or azure.common.credentials
7 | # Need msrest >= 0.6.0
8 | # See also https://pypi.org/project/azure-identity/
9 |
10 | from msrest.authentication import BasicTokenAuthentication
11 | from azure.core.pipeline.policies import BearerTokenCredentialPolicy
12 | from azure.core.pipeline import PipelineRequest, PipelineContext
13 | from azure.core.pipeline.transport import HttpRequest
14 |
15 | from azure.identity import DefaultAzureCredential
16 |
17 | class AzureIdentityCredentialAdapter(BasicTokenAuthentication):
18 | def __init__(self, credential=None, resource_id="https://management.azure.com/.default", **kwargs):
19 | """Adapt any azure-identity credential to work with SDK that needs azure.common.credentials or msrestazure.
20 |
21 | Default resource is ARM (syntax of endpoint v2)
22 |
23 | :param credential: Any azure-identity credential (DefaultAzureCredential by default)
24 | :param str resource_id: The scope to use to get the token (default ARM)
25 | """
26 | super(AzureIdentityCredentialAdapter, self).__init__(None)
27 | if credential is None:
28 | credential = DefaultAzureCredential()
29 | self._policy = BearerTokenCredentialPolicy(credential, resource_id, **kwargs)
30 |
31 | def _make_request(self):
32 | return PipelineRequest(
33 | HttpRequest(
34 | "AzureIdentityCredentialAdapter",
35 | "https://fakeurl"
36 | ),
37 | PipelineContext(None)
38 | )
39 |
40 | def set_token(self):
41 | """Ask the azure-core BearerTokenCredentialPolicy policy to get a token.
42 |
43 | Using the policy gives us for free the caching system of azure-core.
44 | We could make this code simpler by using private method, but by definition
45 | I can't assure they will be there forever, so mocking a fake call to the policy
46 | to extract the token, using 100% public API."""
47 | request = self._make_request()
48 | self._policy.on_request(request)
49 | # Read Authorization, and get the second part after Bearer
50 | token = request.http_request.headers["Authorization"].split(" ", 1)[1]
51 | self.token = {"access_token": token}
52 |
53 | def signed_session(self, session=None):
54 | self.set_token()
55 | return super(AzureIdentityCredentialAdapter, self).signed_session(session)
56 |
--------------------------------------------------------------------------------
/python/dev_requirements.txt:
--------------------------------------------------------------------------------
1 | # Installed by customer in real world part of their app. Any version works
2 | azure-identity
3 |
4 | # Test specific to ARM calls we want to do
5 | azure-mgmt-resource
6 |
7 | # Test infra
8 | pytest
9 | python-dotenv
10 |
--------------------------------------------------------------------------------
/python/test_azure_identity_credential_adapter.py:
--------------------------------------------------------------------------------
1 | # ------------------------------------
2 | # Copyright (c) Microsoft Corporation.
3 | # Licensed under the MIT License.
4 | # ------------------------------------
5 | import pytest
6 |
7 |
8 | @pytest.fixture(scope="session", autouse=True)
9 | def load_env():
10 | import os.path
11 | root_dir = os.path.abspath(os.path.join(os.path.abspath(__file__), "..", ".."))
12 | env_path = os.path.join(root_dir, ".env")
13 | if not os.path.exists(env_path):
14 | raise ValueError("Please create a .env file at the root of this repo using the .env.temp template: {}".format(root_dir))
15 | from dotenv import load_dotenv
16 | load_dotenv(dotenv_path=env_path)
17 |
18 |
19 | def test_list_resource_group():
20 | from azure_identity_credential_adapter import AzureIdentityCredentialAdapter
21 |
22 | import os
23 | subscription_id = os.getenv("AZURE_SUBSCRIPTION_ID")
24 |
25 | credentials = AzureIdentityCredentialAdapter()
26 |
27 | from azure.mgmt.resource import ResourceManagementClient
28 | client = ResourceManagementClient(credentials, subscription_id)
29 | # Not raising any exception means we were able to do it
30 | rg_list = list(client.resource_groups.list())
31 |
--------------------------------------------------------------------------------