github.com/yorinasub17/go-cloud@v0.27.40/secrets/azurekeyvault/akv_test.go (about)

     1  // Copyright 2019 The Go Cloud Development Kit Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     https://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package azurekeyvault
    16  
    17  import (
    18  	"context"
    19  	"errors"
    20  	"log"
    21  	"os"
    22  	"testing"
    23  
    24  	"github.com/Azure/azure-sdk-for-go/sdk/azcore"
    25  	"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
    26  	"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
    27  	"github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys"
    28  	"gocloud.dev/internal/testing/setup"
    29  	"gocloud.dev/internal/useragent"
    30  	"gocloud.dev/secrets"
    31  	"gocloud.dev/secrets/driver"
    32  	"gocloud.dev/secrets/drivertest"
    33  )
    34  
    35  // Prerequisites for --record mode
    36  //
    37  // 1. Sign-in to your Azure Subscription at http://portal.azure.com.
    38  //
    39  // 2. Create a KeyVault, see
    40  // https://docs.microsoft.com/en-us/azure/key-vault/quick-create-portal.
    41  //
    42  // 3. Choose an authentication model. This test uses Service Principal, see
    43  // https://docs.microsoft.com/en-us/rest/api/azure/index#register-your-client-application-with-azure-ad.
    44  // For documentation on acceptable auth models, see
    45  // https://docs.microsoft.com/en-us/azure/key-vault/key-vault-whatis.
    46  //
    47  // 4. Set your environment variables depending on the auth model selection.
    48  // Modify helper initEnv() as needed.
    49  // For Service Principal, please set the following, see
    50  // https://docs.microsoft.com/en-us/go/azure/azure-sdk-go-authorization.
    51  //
    52  // - AZURE_TENANT_ID: Go to "Azure Active Directory", then "Properties". The
    53  //     "Directory ID" property is your AZURE_TENANT_ID.
    54  // - AZURE_CLIENT_ID: Go to "Azure Active Directory", then "App Registrations",
    55  //     then "View all applications". The "Application ID" column shows your
    56  //     AZURE_CLIENT_ID.
    57  // - AZURE_CLIENT_SECRET: Click on the application from the previous step,
    58  //     then "Settings" and then "Keys". Create a key and use it as your
    59  //     AZURE_CLIENT_SECRET. Make sure to save the value as it's hidden after
    60  //     the initial creation.
    61  // - AZURE_ENVIRONMENT: (optional).
    62  // - AZURE_AD_RESOURCE: (optional).
    63  //
    64  // 5. Create/Import a Key. This can be done in the Azure Portal under "Key vaults".
    65  //
    66  // 6. Update constants below to match your Azure KeyVault settings.
    67  
    68  const (
    69  	keyID1 = "https://go-cdk.vault.azure.net/keys/test1"
    70  	keyID2 = "https://go-cdk.vault.azure.net/keys/test2"
    71  )
    72  
    73  type harness struct {
    74  	clientMaker ClientMakerT
    75  	close       func()
    76  }
    77  
    78  func (h *harness) MakeDriver(ctx context.Context) (driver.Keeper, driver.Keeper, error) {
    79  	keeper1, err := openKeeper(h.clientMaker, keyID1, nil)
    80  	if err != nil {
    81  		return nil, nil, err
    82  	}
    83  	keeper2, err := openKeeper(h.clientMaker, keyID2, nil)
    84  	if err != nil {
    85  		return nil, nil, err
    86  	}
    87  	return keeper1, keeper2, nil
    88  }
    89  
    90  func (h *harness) Close() {
    91  	h.close()
    92  }
    93  
    94  type dummyToken struct{}
    95  
    96  func (*dummyToken) GetToken(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
    97  	return azcore.AccessToken{}, nil
    98  }
    99  
   100  func newHarness(ctx context.Context, t *testing.T) (drivertest.Harness, error) {
   101  	httpClient, done := setup.NewAzureKeyVaultTestClient(ctx, t)
   102  	clientMaker := func(keyVaultURI string) (*azkeys.Client, error) {
   103  		var creds azcore.TokenCredential
   104  		var err error
   105  		if *setup.Record {
   106  			initEnv()
   107  			creds, err = azidentity.NewEnvironmentCredential(nil)
   108  		} else {
   109  			creds = &dummyToken{}
   110  		}
   111  		if err != nil {
   112  			return nil, err
   113  		}
   114  		return azkeys.NewClient(keyVaultURI, creds, &azkeys.ClientOptions{
   115  			ClientOptions: policy.ClientOptions{
   116  				Transport: httpClient,
   117  				Telemetry: policy.TelemetryOptions{
   118  					ApplicationID: useragent.AzureUserAgentPrefix("secrets"),
   119  				},
   120  			},
   121  		}), nil
   122  	}
   123  	return &harness{
   124  		clientMaker: clientMaker,
   125  		close:       done,
   126  	}, nil
   127  }
   128  
   129  func initEnv() {
   130  	// For Client Credentials authorization, set AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET
   131  	// For Client Certificate and Azure Managed Service Identity, see doc below for help
   132  	// https://github.com/Azure/azure-sdk-for-go
   133  	if os.Getenv("AZURE_TENANT_ID") == "" ||
   134  		os.Getenv("AZURE_CLIENT_ID") == "" ||
   135  		os.Getenv("AZURE_CLIENT_SECRET") == "" {
   136  		log.Fatal("Missing environment for recording tests, set AZURE_TENANT_ID, AZURE_CLIENT_ID and AZURE_CLIENT_SECRET")
   137  	}
   138  	os.Setenv("AZURE_ENVIRONMENT", "AzurePublicCloud")
   139  	os.Setenv("AZURE_AD_RESOURCE", "https://vault.azure.net")
   140  }
   141  
   142  func TestConformance(t *testing.T) {
   143  	drivertest.RunConformanceTests(t, newHarness, []drivertest.AsTest{verifyAs{}})
   144  }
   145  
   146  type verifyAs struct{}
   147  
   148  func (v verifyAs) Name() string {
   149  	return "verify As function"
   150  }
   151  
   152  func (v verifyAs) ErrorCheck(k *secrets.Keeper, err error) error {
   153  	var e *azcore.ResponseError
   154  	if !k.ErrorAs(err, &e) {
   155  		return errors.New("Keeper.ErrorAs failed")
   156  	}
   157  	return nil
   158  }
   159  
   160  // Key Vault-specific tests.
   161  
   162  func dummyClientMaker(s string) (*azkeys.Client, error) {
   163  	return &azkeys.Client{}, nil
   164  }
   165  
   166  func TestOpenKeeper(t *testing.T) {
   167  	tests := []struct {
   168  		URL     string
   169  		WantErr bool
   170  	}{
   171  		// OK.
   172  		{"azurekeyvaultdummy://mykeyvault.vault.azure.net/keys/mykey/myversion", false},
   173  		// No version -> OK.
   174  		{"azurekeyvaultdummy://mykeyvault.vault.azure.net/keys/mykey", false},
   175  		// Setting algorithm query param -> OK.
   176  		{"azurekeyvaultdummy://mykeyvault.vault.azure.net/keys/mykey/myversion?algorithm=RSA-OAEP", false},
   177  		// Invalid query parameter.
   178  		{"azurekeyvaultdummy://mykeyvault.vault.azure.net/keys/mykey/myversion?param=value", true},
   179  		// Missing key vault name.
   180  		{"azurekeyvaultdummy:///vault.azure.net/keys/mykey/myversion", true},
   181  		// Missing "keys".
   182  		{"azurekeyvaultdummy://mykeyvault.vault.azure.net/mykey/myversion", true},
   183  	}
   184  
   185  	secrets.DefaultURLMux().RegisterKeeper(Scheme+"dummy", &URLOpener{ClientMaker: dummyClientMaker})
   186  	ctx := context.Background()
   187  	for _, test := range tests {
   188  		keeper, err := secrets.OpenKeeper(ctx, test.URL)
   189  		if (err != nil) != test.WantErr {
   190  			t.Errorf("%s: got error %v, want error %v", test.URL, err, test.WantErr)
   191  		}
   192  		if err == nil {
   193  			if err = keeper.Close(); err != nil {
   194  				t.Errorf("%s: got error during close: %v", test.URL, err)
   195  			}
   196  		}
   197  	}
   198  }
   199  
   200  func TestKeyIDRE(t *testing.T) {
   201  	testCases := []struct {
   202  		// input
   203  		keyID string
   204  
   205  		// output
   206  		keyVaultURI string
   207  		keyName     string
   208  		keyVersion  string
   209  	}{
   210  		{
   211  			keyID:       keyID1,
   212  			keyVaultURI: "https://go-cdk.vault.azure.net/",
   213  			keyName:     "test1",
   214  		},
   215  		{
   216  			keyID:       keyID2,
   217  			keyVaultURI: "https://go-cdk.vault.azure.net/",
   218  			keyName:     "test2",
   219  		},
   220  		{
   221  			keyID:       "https://mykeyvault.vault.azure.net/keys/mykey/myversion",
   222  			keyVaultURI: "https://mykeyvault.vault.azure.net/",
   223  			keyName:     "mykey",
   224  			keyVersion:  "myversion",
   225  		},
   226  		{
   227  			keyID:       "https://mykeyvault.vault.usgovcloudapi.net/keys/mykey/myversion",
   228  			keyVaultURI: "https://mykeyvault.vault.usgovcloudapi.net/",
   229  			keyName:     "mykey",
   230  			keyVersion:  "myversion",
   231  		},
   232  		{
   233  			keyID:       "https://mykeyvault.vault.region01.external.com/keys/mykey/myversion",
   234  			keyVaultURI: "https://mykeyvault.vault.region01.external.com/",
   235  			keyName:     "mykey",
   236  			keyVersion:  "myversion",
   237  		},
   238  	}
   239  	for _, testCase := range testCases {
   240  		t.Run(testCase.keyID, func(t *testing.T) {
   241  			k, err := openKeeper(dummyClientMaker, testCase.keyID, nil)
   242  			if err != nil {
   243  				t.Fatal(err)
   244  			}
   245  			defer k.Close()
   246  
   247  			if k.keyVaultURI != testCase.keyVaultURI {
   248  				t.Errorf("got key vault URI %s, want key vault URI %s", k.keyVaultURI, testCase.keyVaultURI)
   249  			}
   250  
   251  			if k.keyName != testCase.keyName {
   252  				t.Errorf("got key name %s, want key name %s", k.keyName, testCase.keyName)
   253  			}
   254  
   255  			if k.keyVersion != testCase.keyVersion {
   256  				t.Errorf("got key version %s, want key version %s", k.keyVersion, testCase.keyVersion)
   257  			}
   258  		})
   259  	}
   260  }