-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactoring the provider block to support determining the TenantID/En…
…vironment from the SubscriptionID Splitting out the authentication logic into a helpers folder Also adding unit tests for these - which pass: ``` $ go test . -v === RUN TestAzureFindValidAccessTokenForTenant_InvalidDate --- PASS: TestAzureFindValidAccessTokenForTenant_InvalidDate (0.00s) === RUN TestAzureFindValidAccessTokenForTenant_Expired 2017/11/30 15:02:01 [DEBUG] Token "7cabcf30-8dca-43f9-91e6-fd56dfb8632f" has expired --- PASS: TestAzureFindValidAccessTokenForTenant_Expired (0.00s) === RUN TestAzureFindValidAccessTokenForTenant_ExpiringIn --- PASS: TestAzureFindValidAccessTokenForTenant_ExpiringIn (0.00s) === RUN TestAzureFindValidAccessTokenForTenant_InvalidManagementDomain 2017/11/30 15:02:01 [DEBUG] Resource "https://portal.azure.com/" isn't a management domain --- PASS: TestAzureFindValidAccessTokenForTenant_InvalidManagementDomain (0.00s) === RUN TestAzureFindValidAccessTokenForTenant_DifferentTenant 2017/11/30 15:02:01 [DEBUG] Resource "https://management.core.windows.net/" isn't for the correct Tenant --- PASS: TestAzureFindValidAccessTokenForTenant_DifferentTenant (0.00s) === RUN TestAzureFindValidAccessTokenForTenant_ValidFromCloudShell --- PASS: TestAzureFindValidAccessTokenForTenant_ValidFromCloudShell (0.00s) === RUN TestAzureFindValidAccessTokenForTenant_ValidFromAzureCLI --- PASS: TestAzureFindValidAccessTokenForTenant_ValidFromAzureCLI (0.00s) === RUN TestAzureFindValidAccessTokenForTenant_NoTokens --- PASS: TestAzureFindValidAccessTokenForTenant_NoTokens (0.00s) === RUN TestAzureCLIProfileFindDefaultSubscription --- PASS: TestAzureCLIProfileFindDefaultSubscription (0.00s) === RUN TestAzureCLIProfileFindSubscription --- PASS: TestAzureCLIProfileFindSubscription (0.00s) === RUN TestAzurePopulateSubscriptionFromCLIProfile_Missing --- PASS: TestAzurePopulateSubscriptionFromCLIProfile_Missing (0.00s) === RUN TestAzurePopulateSubscriptionFromCLIProfile_NoDefault --- PASS: TestAzurePopulateSubscriptionFromCLIProfile_NoDefault (0.00s) === RUN TestAzurePopulateSubscriptionFromCLIProfile_Default --- PASS: TestAzurePopulateSubscriptionFromCLIProfile_Default (0.00s) === RUN TestAzurePopulateTenantAndEnvironmentFromCLIProfile_Empty --- PASS: TestAzurePopulateTenantAndEnvironmentFromCLIProfile_Empty (0.00s) === RUN TestAzurePopulateTenantAndEnvironmentFromCLIProfile_MissingSubscription --- PASS: TestAzurePopulateTenantAndEnvironmentFromCLIProfile_MissingSubscription (0.00s) === RUN TestAzurePopulateTenantAndEnvironmentFromCLIProfile_PopulateEnvironment --- PASS: TestAzurePopulateTenantAndEnvironmentFromCLIProfile_PopulateEnvironment (0.00s) === RUN TestAzurePopulateTenantAndEnvironmentFromCLIProfile_NormaliseAndPopulateEnvironment --- PASS: TestAzurePopulateTenantAndEnvironmentFromCLIProfile_NormaliseAndPopulateEnvironment (0.00s) === RUN TestAzurePopulateTenantAndEnvironmentFromCLIProfile_PopulateTenantId --- PASS: TestAzurePopulateTenantAndEnvironmentFromCLIProfile_PopulateTenantId (0.00s) === RUN TestAzurePopulateTenantAndEnvironmentFromCLIProfile_Complete --- PASS: TestAzurePopulateTenantAndEnvironmentFromCLIProfile_Complete (0.00s) === RUN TestAzurePopulateFromAccessToken_Missing --- PASS: TestAzurePopulateFromAccessToken_Missing (0.00s) === RUN TestAzurePopulateFromAccessToken_Exists --- PASS: TestAzurePopulateFromAccessToken_Exists (0.00s) === RUN TestAzureEnvironmentNames --- PASS: TestAzureEnvironmentNames (0.00s) === RUN TestAzureValidateBearerAuth --- PASS: TestAzureValidateBearerAuth (0.00s) === RUN TestAzureValidateServicePrincipal --- PASS: TestAzureValidateServicePrincipal (0.00s) PASS ok github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/authentication 0.012s ```
- Loading branch information
1 parent
cd6457b
commit fe1e0f5
Showing
13 changed files
with
1,185 additions
and
175 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package authentication | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"strings" | ||
"time" | ||
|
||
"github.com/Azure/go-autorest/autorest/adal" | ||
"github.com/Azure/go-autorest/autorest/azure/cli" | ||
) | ||
|
||
type AccessToken struct { | ||
ClientID string | ||
AccessToken *adal.Token | ||
IsCloudShell bool | ||
} | ||
|
||
func findValidAccessTokenForTenant(tokens []cli.Token, tenantId string) (*AccessToken, error) { | ||
for _, accessToken := range tokens { | ||
token, err := accessToken.ToADALToken() | ||
if err != nil { | ||
return nil, fmt.Errorf("[DEBUG] Error converting access token to token: %+v", err) | ||
} | ||
|
||
expirationDate, err := cli.ParseExpirationDate(accessToken.ExpiresOn) | ||
if err != nil { | ||
return nil, fmt.Errorf("Error parsing expiration date: %q", accessToken.ExpiresOn) | ||
} | ||
|
||
if expirationDate.UTC().Before(time.Now().UTC()) { | ||
log.Printf("[DEBUG] Token %q has expired", token.AccessToken) | ||
continue | ||
} | ||
|
||
if !strings.Contains(accessToken.Resource, "management") { | ||
log.Printf("[DEBUG] Resource %q isn't a management domain", accessToken.Resource) | ||
continue | ||
} | ||
|
||
if !strings.HasSuffix(accessToken.Authority, tenantId) { | ||
log.Printf("[DEBUG] Resource %q isn't for the correct Tenant", accessToken.Resource) | ||
continue | ||
} | ||
|
||
validAccessToken := AccessToken{ | ||
ClientID: accessToken.ClientID, | ||
AccessToken: &token, | ||
IsCloudShell: accessToken.RefreshToken == "", | ||
} | ||
return &validAccessToken, nil | ||
} | ||
|
||
return nil, fmt.Errorf("No Access Token was found for the Tenant ID %q", tenantId) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,217 @@ | ||
package authentication | ||
|
||
import ( | ||
"testing" | ||
"time" | ||
|
||
"github.com/Azure/go-autorest/autorest/azure/cli" | ||
) | ||
|
||
func TestAzureFindValidAccessTokenForTenant_InvalidDate(t *testing.T) { | ||
tenantId := "c056adac-c6a6-4ddf-ab20-0f26d47f7eea" | ||
expectedToken := cli.Token{ | ||
ExpiresOn: "invalid date", | ||
AccessToken: "7cabcf30-8dca-43f9-91e6-fd56dfb8632f", | ||
TokenType: "9b10b986-7a61-4542-8d5a-9fcd96112585", | ||
RefreshToken: "4ec3874d-ee2e-4980-ba47-b5bac11ddb94", | ||
Resource: "https://management.core.windows.net/", | ||
Authority: tenantId, | ||
} | ||
tokens := []cli.Token{expectedToken} | ||
token, err := findValidAccessTokenForTenant(tokens, tenantId) | ||
|
||
if err == nil { | ||
t.Fatalf("Expected an error to be returned but got nil") | ||
} | ||
|
||
if token != nil { | ||
t.Fatalf("Expected Token to be nil but got: %+v", token) | ||
} | ||
} | ||
|
||
func TestAzureFindValidAccessTokenForTenant_Expired(t *testing.T) { | ||
expirationDate := time.Now().Add(time.Minute * -1) | ||
tenantId := "c056adac-c6a6-4ddf-ab20-0f26d47f7eea" | ||
expectedToken := cli.Token{ | ||
ExpiresOn: expirationDate.Format("2006-01-02 15:04:05.999999"), | ||
AccessToken: "7cabcf30-8dca-43f9-91e6-fd56dfb8632f", | ||
TokenType: "9b10b986-7a61-4542-8d5a-9fcd96112585", | ||
RefreshToken: "4ec3874d-ee2e-4980-ba47-b5bac11ddb94", | ||
Resource: "https://management.core.windows.net/", | ||
Authority: tenantId, | ||
} | ||
tokens := []cli.Token{expectedToken} | ||
token, err := findValidAccessTokenForTenant(tokens, tenantId) | ||
|
||
if err == nil { | ||
t.Fatalf("Expected an error to be returned but got nil") | ||
} | ||
|
||
if token != nil { | ||
t.Fatalf("Expected Token to be nil but got: %+v", token) | ||
} | ||
} | ||
|
||
func TestAzureFindValidAccessTokenForTenant_ExpiringIn(t *testing.T) { | ||
minutesToVerify := []int{1, 30, 60} | ||
|
||
for _, minute := range minutesToVerify { | ||
expirationDate := time.Now().Add(time.Minute * time.Duration(minute)) | ||
tenantId := "c056adac-c6a6-4ddf-ab20-0f26d47f7eea" | ||
expectedToken := cli.Token{ | ||
ExpiresOn: expirationDate.Format("2006-01-02 15:04:05.999999"), | ||
AccessToken: "7cabcf30-8dca-43f9-91e6-fd56dfb8632f", | ||
TokenType: "9b10b986-7a61-4542-8d5a-9fcd96112585", | ||
RefreshToken: "4ec3874d-ee2e-4980-ba47-b5bac11ddb94", | ||
Resource: "https://management.core.windows.net/", | ||
Authority: tenantId, | ||
} | ||
tokens := []cli.Token{expectedToken} | ||
token, err := findValidAccessTokenForTenant(tokens, tenantId) | ||
|
||
if err != nil { | ||
t.Fatalf("Expected no error to be returned for minute %d but got %+v", minute, err) | ||
} | ||
|
||
if token == nil { | ||
t.Fatalf("Expected Token to have a value for minute %d but it was nil", minute) | ||
} | ||
|
||
if token.AccessToken.AccessToken != expectedToken.AccessToken { | ||
t.Fatalf("Expected the Access Token to be %q for minute %d but got %q", expectedToken.AccessToken, minute, token.AccessToken.AccessToken) | ||
} | ||
|
||
if token.ClientID != expectedToken.ClientID { | ||
t.Fatalf("Expected the Client ID to be %q for minute %d but got %q", expectedToken.ClientID, minute, token.ClientID) | ||
} | ||
|
||
if token.IsCloudShell != false { | ||
t.Fatalf("Expected `IsCloudShell` to be false for minute %d but got true", minute) | ||
} | ||
} | ||
} | ||
|
||
func TestAzureFindValidAccessTokenForTenant_InvalidManagementDomain(t *testing.T) { | ||
expirationDate := time.Now().Add(1 * time.Hour) | ||
tenantId := "c056adac-c6a6-4ddf-ab20-0f26d47f7eea" | ||
expectedToken := cli.Token{ | ||
ExpiresOn: expirationDate.Format("2006-01-02 15:04:05.999999"), | ||
AccessToken: "7cabcf30-8dca-43f9-91e6-fd56dfb8632f", | ||
TokenType: "9b10b986-7a61-4542-8d5a-9fcd96112585", | ||
Resource: "https://portal.azure.com/", | ||
Authority: tenantId, | ||
} | ||
tokens := []cli.Token{expectedToken} | ||
token, err := findValidAccessTokenForTenant(tokens, tenantId) | ||
|
||
if err == nil { | ||
t.Fatalf("Expected an error but didn't get one") | ||
} | ||
|
||
if token != nil { | ||
t.Fatalf("Expected Token to be nil but got: %+v", token) | ||
} | ||
} | ||
|
||
func TestAzureFindValidAccessTokenForTenant_DifferentTenant(t *testing.T) { | ||
expirationDate := time.Now().Add(1 * time.Hour) | ||
expectedToken := cli.Token{ | ||
ExpiresOn: expirationDate.Format("2006-01-02 15:04:05.999999"), | ||
AccessToken: "7cabcf30-8dca-43f9-91e6-fd56dfb8632f", | ||
TokenType: "9b10b986-7a61-4542-8d5a-9fcd96112585", | ||
Resource: "https://management.core.windows.net/", | ||
Authority: "9b5095de-5496-4b5e-9bc6-ef2c017b9d35", | ||
} | ||
tokens := []cli.Token{expectedToken} | ||
token, err := findValidAccessTokenForTenant(tokens, "c056adac-c6a6-4ddf-ab20-0f26d47f7eea") | ||
|
||
if err == nil { | ||
t.Fatalf("Expected an error but didn't get one") | ||
} | ||
|
||
if token != nil { | ||
t.Fatalf("Expected Token to be nil but got: %+v", token) | ||
} | ||
} | ||
|
||
func TestAzureFindValidAccessTokenForTenant_ValidFromCloudShell(t *testing.T) { | ||
expirationDate := time.Now().Add(1 * time.Hour) | ||
tenantId := "c056adac-c6a6-4ddf-ab20-0f26d47f7eea" | ||
expectedToken := cli.Token{ | ||
ExpiresOn: expirationDate.Format(time.RFC3339), | ||
AccessToken: "7cabcf30-8dca-43f9-91e6-fd56dfb8632f", | ||
TokenType: "9b10b986-7a61-4542-8d5a-9fcd96112585", | ||
Resource: "https://management.core.windows.net/", | ||
Authority: tenantId, | ||
} | ||
tokens := []cli.Token{expectedToken} | ||
token, err := findValidAccessTokenForTenant(tokens, tenantId) | ||
|
||
if err != nil { | ||
t.Fatalf("Expected no error to be returned but got %+v", err) | ||
} | ||
|
||
if token == nil { | ||
t.Fatalf("Expected Token to have a value but it was nil") | ||
} | ||
|
||
if token.AccessToken.AccessToken != expectedToken.AccessToken { | ||
t.Fatalf("Expected the Access Token to be %q but got %q", expectedToken.AccessToken, token.AccessToken.AccessToken) | ||
} | ||
|
||
if token.ClientID != expectedToken.ClientID { | ||
t.Fatalf("Expected the Client ID to be %q but got %q", expectedToken.ClientID, token.ClientID) | ||
} | ||
|
||
if token.IsCloudShell != true { | ||
t.Fatalf("Expected `IsCloudShell` to be true but got false") | ||
} | ||
} | ||
|
||
func TestAzureFindValidAccessTokenForTenant_ValidFromAzureCLI(t *testing.T) { | ||
expirationDate := time.Now().Add(1 * time.Hour) | ||
tenantId := "c056adac-c6a6-4ddf-ab20-0f26d47f7eea" | ||
expectedToken := cli.Token{ | ||
ExpiresOn: expirationDate.Format("2006-01-02 15:04:05.999999"), | ||
AccessToken: "7cabcf30-8dca-43f9-91e6-fd56dfb8632f", | ||
TokenType: "9b10b986-7a61-4542-8d5a-9fcd96112585", | ||
RefreshToken: "4ec3874d-ee2e-4980-ba47-b5bac11ddb94", | ||
Resource: "https://management.core.windows.net/", | ||
Authority: tenantId, | ||
} | ||
tokens := []cli.Token{expectedToken} | ||
token, err := findValidAccessTokenForTenant(tokens, tenantId) | ||
|
||
if err != nil { | ||
t.Fatalf("Expected no error to be returned but got %+v", err) | ||
} | ||
|
||
if token == nil { | ||
t.Fatalf("Expected Token to have a value but it was nil") | ||
} | ||
|
||
if token.AccessToken.AccessToken != expectedToken.AccessToken { | ||
t.Fatalf("Expected the Access Token to be %q but got %q", expectedToken.AccessToken, token.AccessToken.AccessToken) | ||
} | ||
|
||
if token.ClientID != expectedToken.ClientID { | ||
t.Fatalf("Expected the Client ID to be %q but got %q", expectedToken.ClientID, token.ClientID) | ||
} | ||
|
||
if token.IsCloudShell != false { | ||
t.Fatalf("Expected `IsCloudShell` to be false but got true") | ||
} | ||
} | ||
|
||
func TestAzureFindValidAccessTokenForTenant_NoTokens(t *testing.T) { | ||
tokens := make([]cli.Token, 0) | ||
token, err := findValidAccessTokenForTenant(tokens, "abc123") | ||
|
||
if err == nil { | ||
t.Fatalf("Expected an error but didn't get one") | ||
} | ||
|
||
if token != nil { | ||
t.Fatalf("Expected a null token to be returned but got: %+v", token) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package authentication | ||
|
||
import ( | ||
"strings" | ||
|
||
"fmt" | ||
|
||
"github.com/Azure/go-autorest/autorest/azure/cli" | ||
) | ||
|
||
type AzureCLIProfile struct { | ||
cli.Profile | ||
} | ||
|
||
func (a AzureCLIProfile) FindDefaultSubscriptionId() (string, error) { | ||
for _, subscription := range a.Subscriptions { | ||
if subscription.IsDefault { | ||
return subscription.ID, nil | ||
} | ||
} | ||
|
||
return "", fmt.Errorf("No Subscription was Marked as Default in the Azure Profile.") | ||
} | ||
|
||
func (a AzureCLIProfile) FindSubscription(subscriptionId string) (*cli.Subscription, error) { | ||
for _, subscription := range a.Subscriptions { | ||
if strings.EqualFold(subscription.ID, subscriptionId) { | ||
return &subscription, nil | ||
} | ||
} | ||
|
||
return nil, fmt.Errorf("Subscription %q was not found in your Azure CLI credentials. Please verify it exists in `az account list`.", subscriptionId) | ||
} |
Oops, something went wrong.