provider/azurerm: Upgrading to AutoRest v8 (#15006)
* Upgrading to AutoRest 8 * Upgrading to AutoRest v8 * Updating the Remote State to v8
This commit is contained in:
parent
483b849448
commit
13583b4b8b
|
@ -24,6 +24,7 @@ import (
|
|||
"github.com/Azure/azure-sdk-for-go/arm/trafficmanager"
|
||||
mainStorage "github.com/Azure/azure-sdk-for-go/storage"
|
||||
"github.com/Azure/go-autorest/autorest"
|
||||
"github.com/Azure/go-autorest/autorest/adal"
|
||||
"github.com/Azure/go-autorest/autorest/azure"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
riviera "github.com/jen20/riviera/azure"
|
||||
|
@ -173,7 +174,7 @@ func (c *Config) getArmClient() (*ArmClient, error) {
|
|||
}
|
||||
client.rivieraClient = rivieraClient
|
||||
|
||||
oauthConfig, err := env.OAuthConfigForTenant(c.TenantID)
|
||||
oauthConfig, err := adal.NewOAuthConfig(env.ActiveDirectoryEndpoint, c.TenantID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -183,294 +184,295 @@ func (c *Config) getArmClient() (*ArmClient, error) {
|
|||
return nil, fmt.Errorf("Unable to configure OAuthConfig for tenant %s", c.TenantID)
|
||||
}
|
||||
|
||||
spt, err := azure.NewServicePrincipalToken(*oauthConfig, c.ClientID, c.ClientSecret, env.ResourceManagerEndpoint)
|
||||
spt, err := adal.NewServicePrincipalToken(*oauthConfig, c.ClientID, c.ClientSecret, env.ResourceManagerEndpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
endpoint := env.ResourceManagerEndpoint
|
||||
auth := autorest.NewBearerAuthorizer(spt)
|
||||
|
||||
// NOTE: these declarations should be left separate for clarity should the
|
||||
// clients be wished to be configured with custom Responders/PollingModess etc...
|
||||
asc := compute.NewAvailabilitySetsClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&asc.Client)
|
||||
asc.Authorizer = spt
|
||||
asc.Authorizer = auth
|
||||
asc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.availSetClient = asc
|
||||
|
||||
uoc := compute.NewUsageClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&uoc.Client)
|
||||
uoc.Authorizer = spt
|
||||
uoc.Authorizer = auth
|
||||
uoc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.usageOpsClient = uoc
|
||||
|
||||
vmeic := compute.NewVirtualMachineExtensionImagesClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&vmeic.Client)
|
||||
vmeic.Authorizer = spt
|
||||
vmeic.Authorizer = auth
|
||||
vmeic.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.vmExtensionImageClient = vmeic
|
||||
|
||||
vmec := compute.NewVirtualMachineExtensionsClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&vmec.Client)
|
||||
vmec.Authorizer = spt
|
||||
vmec.Authorizer = auth
|
||||
vmec.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.vmExtensionClient = vmec
|
||||
|
||||
vmic := compute.NewVirtualMachineImagesClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&vmic.Client)
|
||||
vmic.Authorizer = spt
|
||||
vmic.Authorizer = auth
|
||||
vmic.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.vmImageClient = vmic
|
||||
|
||||
vmssc := compute.NewVirtualMachineScaleSetsClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&vmssc.Client)
|
||||
vmssc.Authorizer = spt
|
||||
vmssc.Authorizer = auth
|
||||
vmssc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.vmScaleSetClient = vmssc
|
||||
|
||||
vmc := compute.NewVirtualMachinesClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&vmc.Client)
|
||||
vmc.Authorizer = spt
|
||||
vmc.Authorizer = auth
|
||||
vmc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.vmClient = vmc
|
||||
|
||||
agc := network.NewApplicationGatewaysClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&agc.Client)
|
||||
agc.Authorizer = spt
|
||||
agc.Authorizer = auth
|
||||
agc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.appGatewayClient = agc
|
||||
|
||||
crc := containerregistry.NewRegistriesClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&crc.Client)
|
||||
crc.Authorizer = spt
|
||||
crc.Authorizer = auth
|
||||
crc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.containerRegistryClient = crc
|
||||
|
||||
csc := containerservice.NewContainerServicesClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&csc.Client)
|
||||
csc.Authorizer = spt
|
||||
csc.Authorizer = auth
|
||||
csc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.containerServicesClient = csc
|
||||
|
||||
dkc := disk.NewDisksClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&dkc.Client)
|
||||
dkc.Authorizer = spt
|
||||
dkc.Authorizer = auth
|
||||
dkc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.diskClient = dkc
|
||||
|
||||
ehc := eventhub.NewEventHubsClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&ehc.Client)
|
||||
ehc.Authorizer = spt
|
||||
ehc.Authorizer = auth
|
||||
ehc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.eventHubClient = ehc
|
||||
|
||||
chcgc := eventhub.NewConsumerGroupsClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&chcgc.Client)
|
||||
chcgc.Authorizer = spt
|
||||
chcgc.Authorizer = auth
|
||||
chcgc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.eventHubConsumerGroupClient = chcgc
|
||||
|
||||
ehnc := eventhub.NewNamespacesClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&ehnc.Client)
|
||||
ehnc.Authorizer = spt
|
||||
ehnc.Authorizer = auth
|
||||
ehnc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.eventHubNamespacesClient = ehnc
|
||||
|
||||
ifc := network.NewInterfacesClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&ifc.Client)
|
||||
ifc.Authorizer = spt
|
||||
ifc.Authorizer = auth
|
||||
ifc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.ifaceClient = ifc
|
||||
|
||||
erc := network.NewExpressRouteCircuitsClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&erc.Client)
|
||||
erc.Authorizer = spt
|
||||
erc.Authorizer = auth
|
||||
erc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.expressRouteCircuitClient = erc
|
||||
|
||||
lbc := network.NewLoadBalancersClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&lbc.Client)
|
||||
lbc.Authorizer = spt
|
||||
lbc.Authorizer = auth
|
||||
lbc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.loadBalancerClient = lbc
|
||||
|
||||
lgc := network.NewLocalNetworkGatewaysClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&lgc.Client)
|
||||
lgc.Authorizer = spt
|
||||
lgc.Authorizer = auth
|
||||
lgc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.localNetConnClient = lgc
|
||||
|
||||
pipc := network.NewPublicIPAddressesClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&pipc.Client)
|
||||
pipc.Authorizer = spt
|
||||
pipc.Authorizer = auth
|
||||
pipc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.publicIPClient = pipc
|
||||
|
||||
sgc := network.NewSecurityGroupsClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&sgc.Client)
|
||||
sgc.Authorizer = spt
|
||||
sgc.Authorizer = auth
|
||||
sgc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.secGroupClient = sgc
|
||||
|
||||
src := network.NewSecurityRulesClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&src.Client)
|
||||
src.Authorizer = spt
|
||||
src.Authorizer = auth
|
||||
src.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.secRuleClient = src
|
||||
|
||||
snc := network.NewSubnetsClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&snc.Client)
|
||||
snc.Authorizer = spt
|
||||
snc.Authorizer = auth
|
||||
snc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.subnetClient = snc
|
||||
|
||||
vgcc := network.NewVirtualNetworkGatewayConnectionsClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&vgcc.Client)
|
||||
vgcc.Authorizer = spt
|
||||
vgcc.Authorizer = auth
|
||||
vgcc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.vnetGatewayConnectionsClient = vgcc
|
||||
|
||||
vgc := network.NewVirtualNetworkGatewaysClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&vgc.Client)
|
||||
vgc.Authorizer = spt
|
||||
vgc.Authorizer = auth
|
||||
vgc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.vnetGatewayClient = vgc
|
||||
|
||||
vnc := network.NewVirtualNetworksClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&vnc.Client)
|
||||
vnc.Authorizer = spt
|
||||
vnc.Authorizer = auth
|
||||
vnc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.vnetClient = vnc
|
||||
|
||||
vnpc := network.NewVirtualNetworkPeeringsClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&vnpc.Client)
|
||||
vnpc.Authorizer = spt
|
||||
vnpc.Authorizer = auth
|
||||
vnpc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.vnetPeeringsClient = vnpc
|
||||
|
||||
rtc := network.NewRouteTablesClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&rtc.Client)
|
||||
rtc.Authorizer = spt
|
||||
rtc.Authorizer = auth
|
||||
rtc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.routeTablesClient = rtc
|
||||
|
||||
rc := network.NewRoutesClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&rc.Client)
|
||||
rc.Authorizer = spt
|
||||
rc.Authorizer = auth
|
||||
rc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.routesClient = rc
|
||||
|
||||
rgc := resources.NewGroupsClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&rgc.Client)
|
||||
rgc.Authorizer = spt
|
||||
rgc.Authorizer = auth
|
||||
rgc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.resourceGroupClient = rgc
|
||||
|
||||
pc := resources.NewProvidersClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&pc.Client)
|
||||
pc.Authorizer = spt
|
||||
pc.Authorizer = auth
|
||||
pc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.providers = pc
|
||||
|
||||
tc := resources.NewTagsClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&tc.Client)
|
||||
tc.Authorizer = spt
|
||||
tc.Authorizer = auth
|
||||
tc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.tagsClient = tc
|
||||
|
||||
rf := resources.NewGroupClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&rf.Client)
|
||||
rf.Authorizer = spt
|
||||
rf.Authorizer = auth
|
||||
rf.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.resourceFindClient = rf
|
||||
|
||||
jc := scheduler.NewJobsClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&jc.Client)
|
||||
jc.Authorizer = spt
|
||||
jc.Authorizer = auth
|
||||
jc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.jobsClient = jc
|
||||
|
||||
jcc := scheduler.NewJobCollectionsClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&jcc.Client)
|
||||
jcc.Authorizer = spt
|
||||
jcc.Authorizer = auth
|
||||
jcc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.jobsCollectionsClient = jcc
|
||||
|
||||
ssc := storage.NewAccountsClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&ssc.Client)
|
||||
ssc.Authorizer = spt
|
||||
ssc.Authorizer = auth
|
||||
ssc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.storageServiceClient = ssc
|
||||
|
||||
suc := storage.NewUsageClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&suc.Client)
|
||||
suc.Authorizer = spt
|
||||
suc.Authorizer = auth
|
||||
suc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.storageUsageClient = suc
|
||||
|
||||
cpc := cdn.NewProfilesClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&cpc.Client)
|
||||
cpc.Authorizer = spt
|
||||
cpc.Authorizer = auth
|
||||
cpc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.cdnProfilesClient = cpc
|
||||
|
||||
cec := cdn.NewEndpointsClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&cec.Client)
|
||||
cec.Authorizer = spt
|
||||
cec.Authorizer = auth
|
||||
cec.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.cdnEndpointsClient = cec
|
||||
|
||||
dc := resources.NewDeploymentsClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&dc.Client)
|
||||
dc.Authorizer = spt
|
||||
dc.Authorizer = auth
|
||||
dc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.deploymentsClient = dc
|
||||
|
||||
tmpc := trafficmanager.NewProfilesClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&tmpc.Client)
|
||||
tmpc.Authorizer = spt
|
||||
tmpc.Authorizer = auth
|
||||
tmpc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.trafficManagerProfilesClient = tmpc
|
||||
|
||||
tmec := trafficmanager.NewEndpointsClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&tmec.Client)
|
||||
tmec.Authorizer = spt
|
||||
tmec.Authorizer = auth
|
||||
tmec.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.trafficManagerEndpointsClient = tmec
|
||||
|
||||
rdc := redis.NewGroupClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&rdc.Client)
|
||||
rdc.Authorizer = spt
|
||||
rdc.Authorizer = auth
|
||||
rdc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.redisClient = rdc
|
||||
|
||||
sbnc := servicebus.NewNamespacesClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&sbnc.Client)
|
||||
sbnc.Authorizer = spt
|
||||
sbnc.Authorizer = auth
|
||||
sbnc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.serviceBusNamespacesClient = sbnc
|
||||
|
||||
sbtc := servicebus.NewTopicsClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&sbtc.Client)
|
||||
sbtc.Authorizer = spt
|
||||
sbtc.Authorizer = auth
|
||||
sbtc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.serviceBusTopicsClient = sbtc
|
||||
|
||||
sbsc := servicebus.NewSubscriptionsClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&sbsc.Client)
|
||||
sbsc.Authorizer = spt
|
||||
sbsc.Authorizer = auth
|
||||
sbsc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.serviceBusSubscriptionsClient = sbsc
|
||||
|
||||
kvc := keyvault.NewVaultsClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&kvc.Client)
|
||||
kvc.Authorizer = spt
|
||||
kvc.Authorizer = auth
|
||||
kvc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.keyVaultClient = kvc
|
||||
|
||||
sqlepc := sql.NewElasticPoolsClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&sqlepc.Client)
|
||||
sqlepc.Authorizer = spt
|
||||
sqlepc.Authorizer = auth
|
||||
sqlepc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.sqlElasticPoolsClient = sqlepc
|
||||
|
||||
|
|
|
@ -8,6 +8,8 @@ import (
|
|||
|
||||
"github.com/Azure/azure-sdk-for-go/arm/storage"
|
||||
mainStorage "github.com/Azure/azure-sdk-for-go/storage"
|
||||
"github.com/Azure/go-autorest/autorest"
|
||||
"github.com/Azure/go-autorest/autorest/adal"
|
||||
"github.com/Azure/go-autorest/autorest/azure"
|
||||
riviera "github.com/jen20/riviera/azure"
|
||||
)
|
||||
|
@ -68,7 +70,7 @@ func getStorageAccountAccessKey(conf map[string]string, resourceGroupName, stora
|
|||
return "", err
|
||||
}
|
||||
|
||||
oauthConfig, err := env.OAuthConfigForTenant(creds.TenantID)
|
||||
oauthConfig, err := adal.NewOAuthConfig(env.ActiveDirectoryEndpoint, creds.TenantID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -76,13 +78,13 @@ func getStorageAccountAccessKey(conf map[string]string, resourceGroupName, stora
|
|||
return "", fmt.Errorf("Unable to configure OAuthConfig for tenant %s", creds.TenantID)
|
||||
}
|
||||
|
||||
spt, err := azure.NewServicePrincipalToken(*oauthConfig, creds.ClientID, creds.ClientSecret, env.ResourceManagerEndpoint)
|
||||
spt, err := adal.NewServicePrincipalToken(*oauthConfig, creds.ClientID, creds.ClientSecret, env.ResourceManagerEndpoint)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
accountsClient := storage.NewAccountsClientWithBaseURI(env.ResourceManagerEndpoint, creds.SubscriptionID)
|
||||
accountsClient.Authorizer = spt
|
||||
accountsClient.Authorizer = autorest.NewBearerAuthorizer(spt)
|
||||
|
||||
keys, err := accountsClient.ListKeys(resourceGroupName, storageAccountName)
|
||||
if err != nil {
|
||||
|
|
|
@ -0,0 +1,253 @@
|
|||
# Azure Active Directory library for Go
|
||||
|
||||
This project provides a stand alone Azure Active Directory library for Go. The code was extracted
|
||||
from [go-autorest](https://github.com/Azure/go-autorest/) project, which is used as a base for
|
||||
[azure-sdk-for-go](https://github.com/Azure/azure-sdk-for-go).
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
go get -u github.com/Azure/go-autorest/autorest/adal
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
An Active Directory application is required in order to use this library. An application can be registered in the [Azure Portal](https://portal.azure.com/) follow these [guidelines](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-integrating-applications) or using the [Azure CLI](https://github.com/Azure/azure-cli).
|
||||
|
||||
### Register an Azure AD Application with secret
|
||||
|
||||
|
||||
1. Register a new application with a `secret` credential
|
||||
|
||||
```
|
||||
az ad app create \
|
||||
--display-name example-app \
|
||||
--homepage https://example-app/home \
|
||||
--identifier-uris https://example-app/app \
|
||||
--password secret
|
||||
```
|
||||
|
||||
2. Create a service principal using the `Application ID` from previous step
|
||||
|
||||
```
|
||||
az ad sp create --id "Application ID"
|
||||
```
|
||||
|
||||
* Replace `Application ID` with `appId` from step 1.
|
||||
|
||||
### Register an Azure AD Application with certificate
|
||||
|
||||
1. Create a private key
|
||||
|
||||
```
|
||||
openssl genrsa -out "example-app.key" 2048
|
||||
```
|
||||
|
||||
2. Create the certificate
|
||||
|
||||
```
|
||||
openssl req -new -key "example-app.key" -subj "/CN=example-app" -out "example-app.csr"
|
||||
openssl x509 -req -in "example-app.csr" -signkey "example-app.key" -out "example-app.crt" -days 10000
|
||||
```
|
||||
|
||||
3. Create the PKCS12 version of the certificate containing also the private key
|
||||
|
||||
```
|
||||
openssl pkcs12 -export -out "example-app.pfx" -inkey "example-app.key" -in "example-app.crt" -passout pass:
|
||||
|
||||
```
|
||||
|
||||
4. Register a new application with the certificate content form `example-app.crt`
|
||||
|
||||
```
|
||||
certificateContents="$(tail -n+2 "example-app.crt" | head -n-1)"
|
||||
|
||||
az ad app create \
|
||||
--display-name example-app \
|
||||
--homepage https://example-app/home \
|
||||
--identifier-uris https://example-app/app \
|
||||
--key-usage Verify --end-date 2018-01-01 \
|
||||
--key-value "${certificateContents}"
|
||||
```
|
||||
|
||||
5. Create a service principal using the `Application ID` from previous step
|
||||
|
||||
```
|
||||
az ad sp create --id "APPLICATION_ID"
|
||||
```
|
||||
|
||||
* Replace `APPLICATION_ID` with `appId` from step 4.
|
||||
|
||||
|
||||
### Grant the necessary permissions
|
||||
|
||||
Azure relies on a Role-Based Access Control (RBAC) model to manage the access to resources at a fine-grained
|
||||
level. There is a set of [pre-defined roles](https://docs.microsoft.com/en-us/azure/active-directory/role-based-access-built-in-roles)
|
||||
which can be assigned to a service principal of an Azure AD application depending of your needs.
|
||||
|
||||
```
|
||||
az role assignment create --assigner "SERVICE_PRINCIPAL_ID" --role "ROLE_NAME"
|
||||
```
|
||||
|
||||
* Replace the `SERVICE_PRINCIPAL_ID` with the `appId` from previous step.
|
||||
* Replace the `ROLE_NAME` with a role name of your choice.
|
||||
|
||||
It is also possible to define custom role definitions.
|
||||
|
||||
```
|
||||
az role definition create --role-definition role-definition.json
|
||||
```
|
||||
|
||||
* Check [custom roles](https://docs.microsoft.com/en-us/azure/active-directory/role-based-access-control-custom-roles) for more details regarding the content of `role-definition.json` file.
|
||||
|
||||
|
||||
### Acquire Access Token
|
||||
|
||||
The common configuration used by all flows:
|
||||
|
||||
```Go
|
||||
const activeDirectoryEndpoint = "https://login.microsoftonline.com/"
|
||||
tenantID := "TENANT_ID"
|
||||
oauthConfig, err := adal.NewOAuthConfig(activeDirectoryEndpoint, tenantID)
|
||||
|
||||
applicationID := "APPLICATION_ID"
|
||||
|
||||
callback := func(token adal.Token) error {
|
||||
// This is called after the token is acquired
|
||||
}
|
||||
|
||||
// The resource for which the token is acquired
|
||||
resource := "https://management.core.windows.net/"
|
||||
```
|
||||
|
||||
* Replace the `TENANT_ID` with your tenant ID.
|
||||
* Replace the `APPLICATION_ID` with the value from previous section.
|
||||
|
||||
#### Client Credentials
|
||||
|
||||
```Go
|
||||
applicationSecret := "APPLICATION_SECRET"
|
||||
|
||||
spt, err := adal.NewServicePrincipalToken(
|
||||
oauthConfig,
|
||||
appliationID,
|
||||
applicationSecret,
|
||||
resource,
|
||||
callbacks...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Acquire a new access token
|
||||
err = spt.Refresh()
|
||||
if (err == nil) {
|
||||
token := spt.Token
|
||||
}
|
||||
```
|
||||
|
||||
* Replace the `APPLICATION_SECRET` with the `password` value from previous section.
|
||||
|
||||
#### Client Certificate
|
||||
|
||||
```Go
|
||||
certificatePath := "./example-app.pfx"
|
||||
|
||||
certData, err := ioutil.ReadFile(certificatePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read the certificate file (%s): %v", certificatePath, err)
|
||||
}
|
||||
|
||||
// Get the certificate and private key from pfx file
|
||||
certificate, rsaPrivateKey, err := decodePkcs12(certData, "")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode pkcs12 certificate while creating spt: %v", err)
|
||||
}
|
||||
|
||||
spt, err := adal.NewServicePrincipalTokenFromCertificate(
|
||||
oauthConfig,
|
||||
applicationID,
|
||||
certificate,
|
||||
rsaPrivateKey,
|
||||
resource,
|
||||
callbacks...)
|
||||
|
||||
// Acquire a new access token
|
||||
err = spt.Refresh()
|
||||
if (err == nil) {
|
||||
token := spt.Token
|
||||
}
|
||||
```
|
||||
|
||||
* Update the certificate path to point to the example-app.pfx file which was created in previous section.
|
||||
|
||||
|
||||
#### Device Code
|
||||
|
||||
```Go
|
||||
oauthClient := &http.Client{}
|
||||
|
||||
// Acquire the device code
|
||||
deviceCode, err := adal.InitiateDeviceAuth(
|
||||
oauthClient,
|
||||
oauthConfig,
|
||||
applicationID,
|
||||
resource)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to start device auth flow: %s", err)
|
||||
}
|
||||
|
||||
// Display the authentication message
|
||||
fmt.Println(*deviceCode.Message)
|
||||
|
||||
// Wait here until the user is authenticated
|
||||
token, err := adal.WaitForUserCompletion(oauthClient, deviceCode)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to finish device auth flow: %s", err)
|
||||
}
|
||||
|
||||
spt, err := adal.NewServicePrincipalTokenFromManualToken(
|
||||
oauthConfig,
|
||||
applicationID,
|
||||
resource,
|
||||
*token,
|
||||
callbacks...)
|
||||
|
||||
if (err == nil) {
|
||||
token := spt.Token
|
||||
}
|
||||
```
|
||||
|
||||
### Command Line Tool
|
||||
|
||||
A command line tool is available in `cmd/adal.go` that can acquire a token for a given resource. It supports all flows mentioned above.
|
||||
|
||||
```
|
||||
adal -h
|
||||
|
||||
Usage of ./adal:
|
||||
-applicationId string
|
||||
application id
|
||||
-certificatePath string
|
||||
path to pk12/PFC application certificate
|
||||
-mode string
|
||||
authentication mode (device, secret, cert, refresh) (default "device")
|
||||
-resource string
|
||||
resource for which the token is requested
|
||||
-secret string
|
||||
application secret
|
||||
-tenantId string
|
||||
tenant id
|
||||
-tokenCachePath string
|
||||
location of oath token cache (default "/home/cgc/.adal/accessToken.json")
|
||||
```
|
||||
|
||||
Example acquire a token for `https://management.core.windows.net/` using device code flow:
|
||||
|
||||
```
|
||||
adal -mode device \
|
||||
-applicationId "APPLICATION_ID" \
|
||||
-tenantId "TENANT_ID" \
|
||||
-resource https://management.core.windows.net/
|
||||
|
||||
```
|
|
@ -0,0 +1,51 @@
|
|||
package adal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
const (
|
||||
activeDirectoryAPIVersion = "1.0"
|
||||
)
|
||||
|
||||
// OAuthConfig represents the endpoints needed
|
||||
// in OAuth operations
|
||||
type OAuthConfig struct {
|
||||
AuthorityEndpoint url.URL
|
||||
AuthorizeEndpoint url.URL
|
||||
TokenEndpoint url.URL
|
||||
DeviceCodeEndpoint url.URL
|
||||
}
|
||||
|
||||
// NewOAuthConfig returns an OAuthConfig with tenant specific urls
|
||||
func NewOAuthConfig(activeDirectoryEndpoint, tenantID string) (*OAuthConfig, error) {
|
||||
const activeDirectoryEndpointTemplate = "%s/oauth2/%s?api-version=%s"
|
||||
u, err := url.Parse(activeDirectoryEndpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authorityURL, err := u.Parse(tenantID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authorizeURL, err := u.Parse(fmt.Sprintf(activeDirectoryEndpointTemplate, tenantID, "authorize", activeDirectoryAPIVersion))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tokenURL, err := u.Parse(fmt.Sprintf(activeDirectoryEndpointTemplate, tenantID, "token", activeDirectoryAPIVersion))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
deviceCodeURL, err := u.Parse(fmt.Sprintf(activeDirectoryEndpointTemplate, tenantID, "devicecode", activeDirectoryAPIVersion))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &OAuthConfig{
|
||||
AuthorityEndpoint: *authorityURL,
|
||||
AuthorizeEndpoint: *authorizeURL,
|
||||
TokenEndpoint: *tokenURL,
|
||||
DeviceCodeEndpoint: *deviceCodeURL,
|
||||
}, nil
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package azure
|
||||
package adal
|
||||
|
||||
/*
|
||||
This file is largely based on rjw57/oauth2device's code, with the follow differences:
|
||||
|
@ -10,16 +10,17 @@ package azure
|
|||
*/
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest"
|
||||
)
|
||||
|
||||
const (
|
||||
logPrefix = "autorest/azure/devicetoken:"
|
||||
logPrefix = "autorest/adal/devicetoken:"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -38,10 +39,17 @@ var (
|
|||
// ErrDeviceSlowDown represents the service telling us we're polling too often during device flow
|
||||
ErrDeviceSlowDown = fmt.Errorf("%s Error while retrieving OAuth token: Slow Down", logPrefix)
|
||||
|
||||
// ErrDeviceCodeEmpty represents an empty device code from the device endpoint while using device flow
|
||||
ErrDeviceCodeEmpty = fmt.Errorf("%s Error while retrieving device code: Device Code Empty", logPrefix)
|
||||
|
||||
// ErrOAuthTokenEmpty represents an empty OAuth token from the token endpoint when using device flow
|
||||
ErrOAuthTokenEmpty = fmt.Errorf("%s Error while retrieving OAuth token: Token Empty", logPrefix)
|
||||
|
||||
errCodeSendingFails = "Error occurred while sending request for Device Authorization Code"
|
||||
errCodeHandlingFails = "Error occurred while handling response from the Device Endpoint"
|
||||
errTokenSendingFails = "Error occurred while sending request with device code for a token"
|
||||
errTokenHandlingFails = "Error occurred while handling response from the Token Endpoint (during device flow)"
|
||||
errStatusNotOK = "Error HTTP status != 200"
|
||||
)
|
||||
|
||||
// DeviceCode is the object returned by the device auth endpoint
|
||||
|
@ -79,31 +87,45 @@ type deviceToken struct {
|
|||
|
||||
// InitiateDeviceAuth initiates a device auth flow. It returns a DeviceCode
|
||||
// that can be used with CheckForUserCompletion or WaitForUserCompletion.
|
||||
func InitiateDeviceAuth(client *autorest.Client, oauthConfig OAuthConfig, clientID, resource string) (*DeviceCode, error) {
|
||||
req, _ := autorest.Prepare(
|
||||
&http.Request{},
|
||||
autorest.AsPost(),
|
||||
autorest.AsFormURLEncoded(),
|
||||
autorest.WithBaseURL(oauthConfig.DeviceCodeEndpoint.String()),
|
||||
autorest.WithFormData(url.Values{
|
||||
"client_id": []string{clientID},
|
||||
"resource": []string{resource},
|
||||
}),
|
||||
)
|
||||
func InitiateDeviceAuth(sender Sender, oauthConfig OAuthConfig, clientID, resource string) (*DeviceCode, error) {
|
||||
v := url.Values{
|
||||
"client_id": []string{clientID},
|
||||
"resource": []string{resource},
|
||||
}
|
||||
|
||||
resp, err := autorest.SendWithSender(client, req)
|
||||
s := v.Encode()
|
||||
body := ioutil.NopCloser(strings.NewReader(s))
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, oauthConfig.DeviceCodeEndpoint.String(), body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s %s: %s", logPrefix, errCodeSendingFails, err)
|
||||
return nil, fmt.Errorf("%s %s: %s", logPrefix, errCodeSendingFails, err.Error())
|
||||
}
|
||||
|
||||
req.ContentLength = int64(len(s))
|
||||
req.Header.Set(contentType, mimeTypeFormPost)
|
||||
resp, err := sender.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s %s: %s", logPrefix, errCodeSendingFails, err.Error())
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
rb, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s %s: %s", logPrefix, errCodeHandlingFails, err.Error())
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("%s %s: %s", logPrefix, errCodeHandlingFails, errStatusNotOK)
|
||||
}
|
||||
|
||||
if len(strings.Trim(string(rb), " ")) == 0 {
|
||||
return nil, ErrDeviceCodeEmpty
|
||||
}
|
||||
|
||||
var code DeviceCode
|
||||
err = autorest.Respond(
|
||||
resp,
|
||||
autorest.WithErrorUnlessStatusCode(http.StatusOK),
|
||||
autorest.ByUnmarshallingJSON(&code),
|
||||
autorest.ByClosing())
|
||||
err = json.Unmarshal(rb, &code)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s %s: %s", logPrefix, errCodeHandlingFails, err)
|
||||
return nil, fmt.Errorf("%s %s: %s", logPrefix, errCodeHandlingFails, err.Error())
|
||||
}
|
||||
|
||||
code.ClientID = clientID
|
||||
|
@ -115,33 +137,46 @@ func InitiateDeviceAuth(client *autorest.Client, oauthConfig OAuthConfig, client
|
|||
|
||||
// CheckForUserCompletion takes a DeviceCode and checks with the Azure AD OAuth endpoint
|
||||
// to see if the device flow has: been completed, timed out, or otherwise failed
|
||||
func CheckForUserCompletion(client *autorest.Client, code *DeviceCode) (*Token, error) {
|
||||
req, _ := autorest.Prepare(
|
||||
&http.Request{},
|
||||
autorest.AsPost(),
|
||||
autorest.AsFormURLEncoded(),
|
||||
autorest.WithBaseURL(code.OAuthConfig.TokenEndpoint.String()),
|
||||
autorest.WithFormData(url.Values{
|
||||
"client_id": []string{code.ClientID},
|
||||
"code": []string{*code.DeviceCode},
|
||||
"grant_type": []string{OAuthGrantTypeDeviceCode},
|
||||
"resource": []string{code.Resource},
|
||||
}),
|
||||
)
|
||||
func CheckForUserCompletion(sender Sender, code *DeviceCode) (*Token, error) {
|
||||
v := url.Values{
|
||||
"client_id": []string{code.ClientID},
|
||||
"code": []string{*code.DeviceCode},
|
||||
"grant_type": []string{OAuthGrantTypeDeviceCode},
|
||||
"resource": []string{code.Resource},
|
||||
}
|
||||
|
||||
resp, err := autorest.SendWithSender(client, req)
|
||||
s := v.Encode()
|
||||
body := ioutil.NopCloser(strings.NewReader(s))
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, code.OAuthConfig.TokenEndpoint.String(), body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s %s: %s", logPrefix, errTokenSendingFails, err)
|
||||
return nil, fmt.Errorf("%s %s: %s", logPrefix, errTokenSendingFails, err.Error())
|
||||
}
|
||||
|
||||
req.ContentLength = int64(len(s))
|
||||
req.Header.Set(contentType, mimeTypeFormPost)
|
||||
resp, err := sender.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s %s: %s", logPrefix, errTokenSendingFails, err.Error())
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
rb, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s %s: %s", logPrefix, errTokenHandlingFails, err.Error())
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK && len(strings.Trim(string(rb), " ")) == 0 {
|
||||
return nil, fmt.Errorf("%s %s: %s", logPrefix, errTokenHandlingFails, errStatusNotOK)
|
||||
}
|
||||
if len(strings.Trim(string(rb), " ")) == 0 {
|
||||
return nil, ErrOAuthTokenEmpty
|
||||
}
|
||||
|
||||
var token deviceToken
|
||||
err = autorest.Respond(
|
||||
resp,
|
||||
autorest.WithErrorUnlessStatusCode(http.StatusOK, http.StatusBadRequest),
|
||||
autorest.ByUnmarshallingJSON(&token),
|
||||
autorest.ByClosing())
|
||||
err = json.Unmarshal(rb, &token)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s %s: %s", logPrefix, errTokenHandlingFails, err)
|
||||
return nil, fmt.Errorf("%s %s: %s", logPrefix, errTokenHandlingFails, err.Error())
|
||||
}
|
||||
|
||||
if token.Error == nil {
|
||||
|
@ -164,12 +199,12 @@ func CheckForUserCompletion(client *autorest.Client, code *DeviceCode) (*Token,
|
|||
|
||||
// WaitForUserCompletion calls CheckForUserCompletion repeatedly until a token is granted or an error state occurs.
|
||||
// This prevents the user from looping and checking against 'ErrDeviceAuthorizationPending'.
|
||||
func WaitForUserCompletion(client *autorest.Client, code *DeviceCode) (*Token, error) {
|
||||
func WaitForUserCompletion(sender Sender, code *DeviceCode) (*Token, error) {
|
||||
intervalDuration := time.Duration(*code.Interval) * time.Second
|
||||
waitDuration := intervalDuration
|
||||
|
||||
for {
|
||||
token, err := CheckForUserCompletion(client, code)
|
||||
token, err := CheckForUserCompletion(sender, code)
|
||||
|
||||
if err == nil {
|
||||
return token, nil
|
|
@ -1,4 +1,4 @@
|
|||
package azure
|
||||
package adal
|
||||
|
||||
import (
|
||||
"encoding/json"
|
|
@ -0,0 +1,46 @@
|
|||
package adal
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const (
|
||||
contentType = "Content-Type"
|
||||
mimeTypeFormPost = "application/x-www-form-urlencoded"
|
||||
)
|
||||
|
||||
// Sender is the interface that wraps the Do method to send HTTP requests.
|
||||
//
|
||||
// The standard http.Client conforms to this interface.
|
||||
type Sender interface {
|
||||
Do(*http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
// SenderFunc is a method that implements the Sender interface.
|
||||
type SenderFunc func(*http.Request) (*http.Response, error)
|
||||
|
||||
// Do implements the Sender interface on SenderFunc.
|
||||
func (sf SenderFunc) Do(r *http.Request) (*http.Response, error) {
|
||||
return sf(r)
|
||||
}
|
||||
|
||||
// SendDecorator takes and possibily decorates, by wrapping, a Sender. Decorators may affect the
|
||||
// http.Request and pass it along or, first, pass the http.Request along then react to the
|
||||
// http.Response result.
|
||||
type SendDecorator func(Sender) Sender
|
||||
|
||||
// CreateSender creates, decorates, and returns, as a Sender, the default http.Client.
|
||||
func CreateSender(decorators ...SendDecorator) Sender {
|
||||
return DecorateSender(&http.Client{}, decorators...)
|
||||
}
|
||||
|
||||
// DecorateSender accepts a Sender and a, possibly empty, set of SendDecorators, which is applies to
|
||||
// the Sender. Decorators are applied in the order received, but their affect upon the request
|
||||
// depends on whether they are a pre-decorator (change the http.Request and then pass it along) or a
|
||||
// post-decorator (pass the http.Request along and react to the results in http.Response).
|
||||
func DecorateSender(s Sender, decorators ...SendDecorator) Sender {
|
||||
for _, decorate := range decorators {
|
||||
s = decorate(s)
|
||||
}
|
||||
return s
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package azure
|
||||
package adal
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
|
@ -6,13 +6,15 @@ import (
|
|||
"crypto/sha1"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
)
|
||||
|
||||
|
@ -28,6 +30,9 @@ const (
|
|||
|
||||
// OAuthGrantTypeRefreshToken is the "grant_type" identifier used in refresh token flows
|
||||
OAuthGrantTypeRefreshToken = "refresh_token"
|
||||
|
||||
// managedIdentitySettingsPath is the path to the MSI Extension settings file (to discover the endpoint)
|
||||
managedIdentitySettingsPath = "/var/lib/waagent/ManagedIdentity-Settings"
|
||||
)
|
||||
|
||||
var expirationBase time.Time
|
||||
|
@ -36,6 +41,18 @@ func init() {
|
|||
expirationBase, _ = time.Parse(time.RFC3339, tokenBaseDate)
|
||||
}
|
||||
|
||||
// OAuthTokenProvider is an interface which should be implemented by an access token retriever
|
||||
type OAuthTokenProvider interface {
|
||||
OAuthToken() string
|
||||
}
|
||||
|
||||
// Refresher is an interface for token refresh functionality
|
||||
type Refresher interface {
|
||||
Refresh() error
|
||||
RefreshExchange(resource string) error
|
||||
EnsureFresh() error
|
||||
}
|
||||
|
||||
// TokenRefreshCallback is the type representing callbacks that will be called after
|
||||
// a successful token refresh
|
||||
type TokenRefreshCallback func(Token) error
|
||||
|
@ -73,14 +90,9 @@ func (t Token) WillExpireIn(d time.Duration) bool {
|
|||
return !t.Expires().After(time.Now().Add(d))
|
||||
}
|
||||
|
||||
// WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose
|
||||
// value is "Bearer " followed by the AccessToken of the Token.
|
||||
func (t *Token) WithAuthorization() autorest.PrepareDecorator {
|
||||
return func(p autorest.Preparer) autorest.Preparer {
|
||||
return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) {
|
||||
return (autorest.WithBearerAuthorization(t.AccessToken)(p)).Prepare(r)
|
||||
})
|
||||
}
|
||||
//OAuthToken return the current access token
|
||||
func (t *Token) OAuthToken() string {
|
||||
return t.AccessToken
|
||||
}
|
||||
|
||||
// ServicePrincipalNoSecret represents a secret type that contains no secret
|
||||
|
@ -118,6 +130,17 @@ type ServicePrincipalCertificateSecret struct {
|
|||
PrivateKey *rsa.PrivateKey
|
||||
}
|
||||
|
||||
// ServicePrincipalMSISecret implements ServicePrincipalSecret for machines running the MSI Extension.
|
||||
type ServicePrincipalMSISecret struct {
|
||||
}
|
||||
|
||||
// SetAuthenticationValues is a method of the interface ServicePrincipalSecret.
|
||||
// MSI extension requires the authority field to be set to the real tenant authority endpoint
|
||||
func (msiSecret *ServicePrincipalMSISecret) SetAuthenticationValues(spt *ServicePrincipalToken, v *url.Values) error {
|
||||
v.Set("authority", spt.oauthConfig.AuthorityEndpoint.String())
|
||||
return nil
|
||||
}
|
||||
|
||||
// SignJwt returns the JWT signed with the certificate's private key.
|
||||
func (secret *ServicePrincipalCertificateSecret) SignJwt(spt *ServicePrincipalToken) (string, error) {
|
||||
hasher := sha1.New()
|
||||
|
@ -173,7 +196,7 @@ type ServicePrincipalToken struct {
|
|||
resource string
|
||||
autoRefresh bool
|
||||
refreshWithin time.Duration
|
||||
sender autorest.Sender
|
||||
sender Sender
|
||||
|
||||
refreshCallbacks []TokenRefreshCallback
|
||||
}
|
||||
|
@ -238,10 +261,56 @@ func NewServicePrincipalTokenFromCertificate(oauthConfig OAuthConfig, clientID s
|
|||
)
|
||||
}
|
||||
|
||||
// NewServicePrincipalTokenFromMSI creates a ServicePrincipalToken via the MSI VM Extension.
|
||||
func NewServicePrincipalTokenFromMSI(oauthConfig OAuthConfig, resource string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) {
|
||||
return newServicePrincipalTokenFromMSI(oauthConfig, resource, managedIdentitySettingsPath, callbacks...)
|
||||
}
|
||||
|
||||
func newServicePrincipalTokenFromMSI(oauthConfig OAuthConfig, resource, settingsPath string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) {
|
||||
// Read MSI settings
|
||||
bytes, err := ioutil.ReadFile(settingsPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
msiSettings := struct {
|
||||
URL string `json:"url"`
|
||||
}{}
|
||||
err = json.Unmarshal(bytes, &msiSettings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// We set the oauth config token endpoint to be MSI's endpoint
|
||||
// We leave the authority as-is so MSI can POST it with the token request
|
||||
msiEndpointURL, err := url.Parse(msiSettings.URL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
msiTokenEndpointURL, err := msiEndpointURL.Parse("/oauth2/token")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
oauthConfig.TokenEndpoint = *msiTokenEndpointURL
|
||||
|
||||
spt := &ServicePrincipalToken{
|
||||
oauthConfig: oauthConfig,
|
||||
secret: &ServicePrincipalMSISecret{},
|
||||
resource: resource,
|
||||
autoRefresh: true,
|
||||
refreshWithin: defaultRefresh,
|
||||
sender: &http.Client{},
|
||||
refreshCallbacks: callbacks,
|
||||
}
|
||||
|
||||
return spt, nil
|
||||
}
|
||||
|
||||
// EnsureFresh will refresh the token if it will expire within the refresh window (as set by
|
||||
// RefreshWithin).
|
||||
// RefreshWithin) and autoRefresh flag is on.
|
||||
func (spt *ServicePrincipalToken) EnsureFresh() error {
|
||||
if spt.WillExpireIn(spt.refreshWithin) {
|
||||
if spt.autoRefresh && spt.WillExpireIn(spt.refreshWithin) {
|
||||
return spt.Refresh()
|
||||
}
|
||||
return nil
|
||||
|
@ -253,8 +322,7 @@ func (spt *ServicePrincipalToken) InvokeRefreshCallbacks(token Token) error {
|
|||
for _, callback := range spt.refreshCallbacks {
|
||||
err := callback(spt.Token)
|
||||
if err != nil {
|
||||
return autorest.NewErrorWithError(err,
|
||||
"azure.ServicePrincipalToken", "InvokeRefreshCallbacks", nil, "A TokenRefreshCallback handler returned an error")
|
||||
return fmt.Errorf("adal: TokenRefreshCallback handler failed. Error = '%v'", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -287,39 +355,40 @@ func (spt *ServicePrincipalToken) refreshInternal(resource string) error {
|
|||
}
|
||||
}
|
||||
|
||||
req, _ := autorest.Prepare(&http.Request{},
|
||||
autorest.AsPost(),
|
||||
autorest.AsFormURLEncoded(),
|
||||
autorest.WithBaseURL(spt.oauthConfig.TokenEndpoint.String()),
|
||||
autorest.WithFormData(v))
|
||||
|
||||
resp, err := autorest.SendWithSender(spt.sender, req)
|
||||
s := v.Encode()
|
||||
body := ioutil.NopCloser(strings.NewReader(s))
|
||||
req, err := http.NewRequest(http.MethodPost, spt.oauthConfig.TokenEndpoint.String(), body)
|
||||
if err != nil {
|
||||
return autorest.NewErrorWithError(err,
|
||||
"azure.ServicePrincipalToken", "Refresh", resp, "Failure sending request for Service Principal %s",
|
||||
spt.clientID)
|
||||
return fmt.Errorf("adal: Failed to build the refresh request. Error = '%v'", err)
|
||||
}
|
||||
|
||||
var newToken Token
|
||||
err = autorest.Respond(resp,
|
||||
autorest.WithErrorUnlessStatusCode(http.StatusOK),
|
||||
autorest.ByUnmarshallingJSON(&newToken),
|
||||
autorest.ByClosing())
|
||||
req.ContentLength = int64(len(s))
|
||||
req.Header.Set(contentType, mimeTypeFormPost)
|
||||
resp, err := spt.sender.Do(req)
|
||||
if err != nil {
|
||||
return autorest.NewErrorWithError(err,
|
||||
"azure.ServicePrincipalToken", "Refresh", resp, "Failure handling response to Service Principal %s request",
|
||||
spt.clientID)
|
||||
return fmt.Errorf("adal: Failed to execute the refresh request. Error = '%v'", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("adal: Refresh request failed. Status Code = '%d'", resp.StatusCode)
|
||||
}
|
||||
|
||||
spt.Token = newToken
|
||||
|
||||
err = spt.InvokeRefreshCallbacks(newToken)
|
||||
rb, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
// its already wrapped inside InvokeRefreshCallbacks
|
||||
return err
|
||||
return fmt.Errorf("adal: Failed to read a new service principal token during refresh. Error = '%v'", err)
|
||||
}
|
||||
if len(strings.Trim(string(rb), " ")) == 0 {
|
||||
return fmt.Errorf("adal: Empty service principal token received during refresh")
|
||||
}
|
||||
var token Token
|
||||
err = json.Unmarshal(rb, &token)
|
||||
if err != nil {
|
||||
return fmt.Errorf("adal: Failed to unmarshal the service principal token during refresh. Error = '%v' JSON = '%s'", err, string(rb))
|
||||
}
|
||||
|
||||
return nil
|
||||
spt.Token = token
|
||||
|
||||
return spt.InvokeRefreshCallbacks(token)
|
||||
}
|
||||
|
||||
// SetAutoRefresh enables or disables automatic refreshing of stale tokens.
|
||||
|
@ -334,30 +403,6 @@ func (spt *ServicePrincipalToken) SetRefreshWithin(d time.Duration) {
|
|||
return
|
||||
}
|
||||
|
||||
// SetSender sets the autorest.Sender used when obtaining the Service Principal token. An
|
||||
// SetSender sets the http.Client used when obtaining the Service Principal token. An
|
||||
// undecorated http.Client is used by default.
|
||||
func (spt *ServicePrincipalToken) SetSender(s autorest.Sender) {
|
||||
spt.sender = s
|
||||
}
|
||||
|
||||
// WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose
|
||||
// value is "Bearer " followed by the AccessToken of the ServicePrincipalToken.
|
||||
//
|
||||
// By default, the token will automatically refresh if nearly expired (as determined by the
|
||||
// RefreshWithin interval). Use the AutoRefresh method to enable or disable automatically refreshing
|
||||
// tokens.
|
||||
func (spt *ServicePrincipalToken) WithAuthorization() autorest.PrepareDecorator {
|
||||
return func(p autorest.Preparer) autorest.Preparer {
|
||||
return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) {
|
||||
if spt.autoRefresh {
|
||||
err := spt.EnsureFresh()
|
||||
if err != nil {
|
||||
return r, autorest.NewErrorWithError(err,
|
||||
"azure.ServicePrincipalToken", "WithAuthorization", nil, "Failed to refresh Service Principal Token for request to %s",
|
||||
r.URL)
|
||||
}
|
||||
}
|
||||
return (autorest.WithBearerAuthorization(spt.AccessToken)(p)).Prepare(r)
|
||||
})
|
||||
}
|
||||
}
|
||||
func (spt *ServicePrincipalToken) SetSender(s Sender) { spt.sender = s }
|
|
@ -0,0 +1,57 @@
|
|||
package autorest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest/adal"
|
||||
)
|
||||
|
||||
// Authorizer is the interface that provides a PrepareDecorator used to supply request
|
||||
// authorization. Most often, the Authorizer decorator runs last so it has access to the full
|
||||
// state of the formed HTTP request.
|
||||
type Authorizer interface {
|
||||
WithAuthorization() PrepareDecorator
|
||||
}
|
||||
|
||||
// NullAuthorizer implements a default, "do nothing" Authorizer.
|
||||
type NullAuthorizer struct{}
|
||||
|
||||
// WithAuthorization returns a PrepareDecorator that does nothing.
|
||||
func (na NullAuthorizer) WithAuthorization() PrepareDecorator {
|
||||
return WithNothing()
|
||||
}
|
||||
|
||||
// BearerAuthorizer implements the bearer authorization
|
||||
type BearerAuthorizer struct {
|
||||
tokenProvider adal.OAuthTokenProvider
|
||||
}
|
||||
|
||||
// NewBearerAuthorizer crates a BearerAuthorizer using the given token provider
|
||||
func NewBearerAuthorizer(tp adal.OAuthTokenProvider) *BearerAuthorizer {
|
||||
return &BearerAuthorizer{tokenProvider: tp}
|
||||
}
|
||||
|
||||
func (ba *BearerAuthorizer) withBearerAuthorization() PrepareDecorator {
|
||||
return WithHeader(headerAuthorization, fmt.Sprintf("Bearer %s", ba.tokenProvider.OAuthToken()))
|
||||
}
|
||||
|
||||
// WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose
|
||||
// value is "Bearer " followed by the token.
|
||||
//
|
||||
// By default, the token will be automatically refreshed through the Refresher interface.
|
||||
func (ba *BearerAuthorizer) WithAuthorization() PrepareDecorator {
|
||||
return func(p Preparer) Preparer {
|
||||
return PreparerFunc(func(r *http.Request) (*http.Request, error) {
|
||||
refresher, ok := ba.tokenProvider.(adal.Refresher)
|
||||
if ok {
|
||||
err := refresher.EnsureFresh()
|
||||
if err != nil {
|
||||
return r, NewErrorWithError(err, "azure.BearerAuthorizer", "WithAuthorization", nil,
|
||||
"Failed to refresh the Token for request to %s", r.URL)
|
||||
}
|
||||
}
|
||||
return (ba.withBearerAuthorization()(p)).Prepare(r)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -17,12 +17,6 @@ const (
|
|||
)
|
||||
|
||||
const (
|
||||
methodDelete = "DELETE"
|
||||
methodPatch = "PATCH"
|
||||
methodPost = "POST"
|
||||
methodPut = "PUT"
|
||||
methodGet = "GET"
|
||||
|
||||
operationInProgress string = "InProgress"
|
||||
operationCanceled string = "Canceled"
|
||||
operationFailed string = "Failed"
|
||||
|
@ -226,7 +220,7 @@ func updatePollingState(resp *http.Response, ps *pollingState) error {
|
|||
// Lastly, requests against an existing resource, use the last request URI
|
||||
if ps.uri == "" {
|
||||
m := strings.ToUpper(req.Method)
|
||||
if m == methodPatch || m == methodPut || m == methodGet {
|
||||
if m == http.MethodPatch || m == http.MethodPut || m == http.MethodGet {
|
||||
ps.uri = req.URL.String()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
package azure
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// OAuthConfig represents the endpoints needed
|
||||
// in OAuth operations
|
||||
type OAuthConfig struct {
|
||||
AuthorizeEndpoint url.URL
|
||||
TokenEndpoint url.URL
|
||||
DeviceCodeEndpoint url.URL
|
||||
}
|
|
@ -2,14 +2,9 @@ package azure
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
activeDirectoryAPIVersion = "1.0"
|
||||
)
|
||||
|
||||
var environments = map[string]Environment{
|
||||
"AZURECHINACLOUD": ChinaCloud,
|
||||
"AZUREGERMANCLOUD": GermanCloud,
|
||||
|
@ -133,35 +128,3 @@ func EnvironmentFromName(name string) (Environment, error) {
|
|||
}
|
||||
return env, nil
|
||||
}
|
||||
|
||||
// OAuthConfigForTenant returns an OAuthConfig with tenant specific urls
|
||||
func (env Environment) OAuthConfigForTenant(tenantID string) (*OAuthConfig, error) {
|
||||
return OAuthConfigForTenant(env.ActiveDirectoryEndpoint, tenantID)
|
||||
}
|
||||
|
||||
// OAuthConfigForTenant returns an OAuthConfig with tenant specific urls for target cloud auth endpoint
|
||||
func OAuthConfigForTenant(activeDirectoryEndpoint, tenantID string) (*OAuthConfig, error) {
|
||||
template := "%s/oauth2/%s?api-version=%s"
|
||||
u, err := url.Parse(activeDirectoryEndpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authorizeURL, err := u.Parse(fmt.Sprintf(template, tenantID, "authorize", activeDirectoryAPIVersion))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tokenURL, err := u.Parse(fmt.Sprintf(template, tenantID, "token", activeDirectoryAPIVersion))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
deviceCodeURL, err := u.Parse(fmt.Sprintf(template, tenantID, "devicecode", activeDirectoryAPIVersion))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &OAuthConfig{
|
||||
AuthorizeEndpoint: *authorizeURL,
|
||||
TokenEndpoint: *tokenURL,
|
||||
DeviceCodeEndpoint: *deviceCodeURL,
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
package date
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
// unixEpoch is the moment in time that should be treated as timestamp 0.
|
||||
var unixEpoch = time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC)
|
||||
|
||||
// UnixTime marshals and unmarshals a time that is represented as the number
|
||||
// of seconds (ignoring skip-seconds) since the Unix Epoch.
|
||||
type UnixTime time.Time
|
||||
|
||||
// Duration returns the time as a Duration since the UnixEpoch.
|
||||
func (t UnixTime) Duration() time.Duration {
|
||||
return time.Time(t).Sub(unixEpoch)
|
||||
}
|
||||
|
||||
// NewUnixTimeFromSeconds creates a UnixTime as a number of seconds from the UnixEpoch.
|
||||
func NewUnixTimeFromSeconds(seconds float64) UnixTime {
|
||||
return NewUnixTimeFromDuration(time.Duration(seconds * float64(time.Second)))
|
||||
}
|
||||
|
||||
// NewUnixTimeFromNanoseconds creates a UnixTime as a number of nanoseconds from the UnixEpoch.
|
||||
func NewUnixTimeFromNanoseconds(nanoseconds int64) UnixTime {
|
||||
return NewUnixTimeFromDuration(time.Duration(nanoseconds))
|
||||
}
|
||||
|
||||
// NewUnixTimeFromDuration creates a UnixTime as a duration of time since the UnixEpoch.
|
||||
func NewUnixTimeFromDuration(dur time.Duration) UnixTime {
|
||||
return UnixTime(unixEpoch.Add(dur))
|
||||
}
|
||||
|
||||
// UnixEpoch retreives the moment considered the Unix Epoch. I.e. The time represented by '0'
|
||||
func UnixEpoch() time.Time {
|
||||
return unixEpoch
|
||||
}
|
||||
|
||||
// MarshalJSON preserves the UnixTime as a JSON number conforming to Unix Timestamp requirements.
|
||||
// (i.e. the number of seconds since midnight January 1st, 1970 not considering leap seconds.)
|
||||
func (t UnixTime) MarshalJSON() ([]byte, error) {
|
||||
buffer := &bytes.Buffer{}
|
||||
enc := json.NewEncoder(buffer)
|
||||
err := enc.Encode(float64(time.Time(t).UnixNano()) / 1e9)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buffer.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON reconstitures a UnixTime saved as a JSON number of the number of seconds since
|
||||
// midnight January 1st, 1970.
|
||||
func (t *UnixTime) UnmarshalJSON(text []byte) error {
|
||||
dec := json.NewDecoder(bytes.NewReader(text))
|
||||
|
||||
var secondsSinceEpoch float64
|
||||
if err := dec.Decode(&secondsSinceEpoch); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*t = NewUnixTimeFromSeconds(secondsSinceEpoch)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalText stores the number of seconds since the Unix Epoch as a textual floating point number.
|
||||
func (t UnixTime) MarshalText() ([]byte, error) {
|
||||
cast := time.Time(t)
|
||||
return cast.MarshalText()
|
||||
}
|
||||
|
||||
// UnmarshalText populates a UnixTime with a value stored textually as a floating point number of seconds since the Unix Epoch.
|
||||
func (t *UnixTime) UnmarshalText(raw []byte) error {
|
||||
var unmarshaled time.Time
|
||||
|
||||
if err := unmarshaled.UnmarshalText(raw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*t = UnixTime(unmarshaled)
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary converts a UnixTime into a binary.LittleEndian float64 of nanoseconds since the epoch.
|
||||
func (t UnixTime) MarshalBinary() ([]byte, error) {
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
payload := int64(t.Duration())
|
||||
|
||||
if err := binary.Write(buf, binary.LittleEndian, &payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary converts a from a binary.LittleEndian float64 of nanoseconds since the epoch into a UnixTime.
|
||||
func (t *UnixTime) UnmarshalBinary(raw []byte) error {
|
||||
var nanosecondsSinceEpoch int64
|
||||
|
||||
if err := binary.Read(bytes.NewReader(raw), binary.LittleEndian, &nanosecondsSinceEpoch); err != nil {
|
||||
return err
|
||||
}
|
||||
*t = NewUnixTimeFromNanoseconds(nanosecondsSinceEpoch)
|
||||
return nil
|
||||
}
|
|
@ -426,18 +426,3 @@ func WithQueryParameters(queryParameters map[string]interface{}) PrepareDecorato
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Authorizer is the interface that provides a PrepareDecorator used to supply request
|
||||
// authorization. Most often, the Authorizer decorator runs last so it has access to the full
|
||||
// state of the formed HTTP request.
|
||||
type Authorizer interface {
|
||||
WithAuthorization() PrepareDecorator
|
||||
}
|
||||
|
||||
// NullAuthorizer implements a default, "do nothing" Authorizer.
|
||||
type NullAuthorizer struct{}
|
||||
|
||||
// WithAuthorization returns a PrepareDecorator that does nothing.
|
||||
func (na NullAuthorizer) WithAuthorization() PrepareDecorator {
|
||||
return WithNothing()
|
||||
}
|
||||
|
|
|
@ -1,23 +1,35 @@
|
|||
package autorest
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
major = "7"
|
||||
minor = "3"
|
||||
patch = "0"
|
||||
tag = ""
|
||||
semVerFormat = "%s.%s.%s%s"
|
||||
major = 8
|
||||
minor = 0
|
||||
patch = 0
|
||||
tag = ""
|
||||
)
|
||||
|
||||
var once sync.Once
|
||||
var version string
|
||||
|
||||
// Version returns the semantic version (see http://semver.org).
|
||||
func Version() string {
|
||||
if version == "" {
|
||||
version = fmt.Sprintf(semVerFormat, major, minor, patch, tag)
|
||||
}
|
||||
once.Do(func() {
|
||||
semver := fmt.Sprintf("%d.%d.%d", major, minor, patch)
|
||||
verBuilder := bytes.NewBufferString(semver)
|
||||
if tag != "" && tag != "-" {
|
||||
updated := strings.TrimPrefix(tag, "-")
|
||||
_, err := verBuilder.WriteString("-" + updated)
|
||||
if err == nil {
|
||||
verBuilder = bytes.NewBufferString(semver)
|
||||
}
|
||||
}
|
||||
version = verBuilder.String()
|
||||
})
|
||||
return version
|
||||
}
|
||||
|
|
|
@ -265,48 +265,58 @@
|
|||
"revisionTime": "2016-06-22T17:32:16Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "+sxS3YEvxf1VxlRskIdXFRWq9rY=",
|
||||
"comment": "v7.0.5",
|
||||
"checksumSHA1": "NwbvjCz9Xo4spo0C96Tq6WCLX7U=",
|
||||
"comment": "v8.0.0",
|
||||
"path": "github.com/Azure/go-autorest/autorest",
|
||||
"revision": "ec5f4903f77ed9927ac95b19ab8e44ada64c1356",
|
||||
"revisionTime": "2017-02-15T19:58:14Z",
|
||||
"version": "v7.3.1",
|
||||
"versionExact": "v7.3.1"
|
||||
"revision": "58f6f26e200fa5dfb40c9cd1c83f3e2c860d779d",
|
||||
"revisionTime": "2017-04-28T17:52:31Z",
|
||||
"version": "v8.0.0",
|
||||
"versionExact": "v8.0.0"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "ghrnc4vZv6q8zzeakZnrS8CGFhE=",
|
||||
"comment": "v7.0.5",
|
||||
"checksumSHA1": "KOETWLsF6QW+lrPVPsMNHDZP+xA=",
|
||||
"comment": "v8.0.0",
|
||||
"path": "github.com/Azure/go-autorest/autorest/adal",
|
||||
"revision": "58f6f26e200fa5dfb40c9cd1c83f3e2c860d779d",
|
||||
"revisionTime": "2017-04-28T17:52:31Z",
|
||||
"version": "v8.0.0",
|
||||
"versionExact": "v8.0.0"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "2KdBFgT4qY+fMOkBTa5vA9V0AiM=",
|
||||
"comment": "v8.0.0",
|
||||
"path": "github.com/Azure/go-autorest/autorest/azure",
|
||||
"revision": "ec5f4903f77ed9927ac95b19ab8e44ada64c1356",
|
||||
"revisionTime": "2017-02-15T19:58:14Z",
|
||||
"version": "v7.3.1",
|
||||
"versionExact": "v7.3.1"
|
||||
"revision": "58f6f26e200fa5dfb40c9cd1c83f3e2c860d779d",
|
||||
"revisionTime": "2017-04-28T17:52:31Z",
|
||||
"version": "v8.0.0",
|
||||
"versionExact": "v8.0.0"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "q9Qz8PAxK5FTOZwgYKe5Lj38u4c=",
|
||||
"comment": "v7.0.5",
|
||||
"checksumSHA1": "LSF/pNrjhIxl6jiS6bKooBFCOxI=",
|
||||
"comment": "v8.0.0",
|
||||
"path": "github.com/Azure/go-autorest/autorest/date",
|
||||
"revision": "ec5f4903f77ed9927ac95b19ab8e44ada64c1356",
|
||||
"revisionTime": "2017-02-15T19:58:14Z",
|
||||
"version": "v7.3.1",
|
||||
"versionExact": "v7.3.1"
|
||||
"revision": "58f6f26e200fa5dfb40c9cd1c83f3e2c860d779d",
|
||||
"revisionTime": "2017-04-28T17:52:31Z",
|
||||
"version": "v8.0.0",
|
||||
"versionExact": "v8.0.0"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "Ev8qCsbFjDlMlX0N2tYAhYQFpUc=",
|
||||
"comment": "v7.0.5",
|
||||
"comment": "v8.0.0",
|
||||
"path": "github.com/Azure/go-autorest/autorest/to",
|
||||
"revision": "ec5f4903f77ed9927ac95b19ab8e44ada64c1356",
|
||||
"revisionTime": "2017-02-15T19:58:14Z",
|
||||
"version": "v7.3.1",
|
||||
"versionExact": "v7.3.1"
|
||||
"revision": "58f6f26e200fa5dfb40c9cd1c83f3e2c860d779d",
|
||||
"revisionTime": "2017-04-28T17:52:31Z",
|
||||
"version": "v8.0.0",
|
||||
"versionExact": "v8.0.0"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "oBixceM+55gdk47iff8DSEIh3po=",
|
||||
"comment": "v8.0.0",
|
||||
"path": "github.com/Azure/go-autorest/autorest/validation",
|
||||
"revision": "ec5f4903f77ed9927ac95b19ab8e44ada64c1356",
|
||||
"revisionTime": "2017-02-15T19:58:14Z",
|
||||
"version": "v7.3.1",
|
||||
"versionExact": "v7.3.1"
|
||||
"revision": "58f6f26e200fa5dfb40c9cd1c83f3e2c860d779d",
|
||||
"revisionTime": "2017-04-28T17:52:31Z",
|
||||
"version": "v8.0.0",
|
||||
"versionExact": "v8.0.0"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "ICScouhAqYHoJEpJlJMYg7EzgyY=",
|
||||
|
|
Loading…
Reference in New Issue