From f9beade732cfd933d0b679ca099c9044b9c8573a Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Tue, 18 Jan 2022 11:03:38 -0600 Subject: [PATCH 01/11] draft changes for AZURE_CLIENT_SEND_CERTIFICATE_CHAIN --- sdk/azidentity/environment_credential.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sdk/azidentity/environment_credential.go b/sdk/azidentity/environment_credential.go index b2759aee8d08..14e702b6b021 100644 --- a/sdk/azidentity/environment_credential.go +++ b/sdk/azidentity/environment_credential.go @@ -80,7 +80,9 @@ func NewEnvironmentCredential(options *EnvironmentCredentialOptions) (*Environme if err != nil { return nil, fmt.Errorf(`failed to load certificate from "%s": %v`, certPath, err) } - o := &ClientCertificateCredentialOptions{AuthorityHost: options.AuthorityHost, ClientOptions: options.ClientOptions} + sendChainEnv := os.Getenv("AZURE_CLIENT_SEND_CERTIFICATE_CHAIN") + sendCertChain := sendChainEnv == "true" || sendChainEnv == "1" + o := &ClientCertificateCredentialOptions{AuthorityHost: options.AuthorityHost, ClientOptions: options.ClientOptions, SendCertificateChain: sendCertChain} cred, err := NewClientCertificateCredential(tenantID, clientID, certs, key, o) if err != nil { return nil, err From 1401cccc2101687de30e9bb268da586f41293ba8 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Wed, 19 Jan 2022 17:20:23 -0600 Subject: [PATCH 02/11] Add mock test for sendx5c header --- sdk/azidentity/azidentity_test.go | 86 +++++++++++++++++++ .../client_certificate_credential_test.go | 45 ++++++++++ sdk/azidentity/environment_credential.go | 6 +- sdk/azidentity/go.mod | 3 + sdk/azidentity/go.sum | 2 + sdk/internal/mock/mock.go | 25 +++++- 6 files changed, 165 insertions(+), 2 deletions(-) diff --git a/sdk/azidentity/azidentity_test.go b/sdk/azidentity/azidentity_test.go index 5e5f501284ba..596d815ae13b 100644 --- a/sdk/azidentity/azidentity_test.go +++ b/sdk/azidentity/azidentity_test.go @@ -27,6 +27,92 @@ const ( customHostString = "https://custommock.com/" ) +func getTenantDiscoveryResponse(url string) string { + return `{` + + `"token_endpoint": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/oauth2/v2.0/token",` + + `"token_endpoint_auth_methods_supported": [` + + `"client_secret_post",` + + `"private_key_jwt",` + + `"client_secret_basic"` + + `],` + + `"jwks_uri": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/discovery/v2.0/keys",` + + `"response_modes_supported": [` + + `"query",` + + `"fragment",` + + `"form_post"` + + `],` + + `"subject_types_supported": [` + + `"pairwise"` + + `],` + + `"id_token_signing_alg_values_supported": [` + + `"RS256"` + + `],` + + `"response_types_supported": [` + + `"code",` + + `"id_token",` + + `"code id_token",` + + `"id_token token"` + + `],` + + `"scopes_supported": [` + + `"openid",` + + `"profile",` + + `"email",` + + `"offline_access"` + + `],` + + `"issuer": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/v2.0",` + + `"request_uri_parameter_supported": false,` + + `"userinfo_endpoint": "https://graph.microsoft.com/oidc/userinfo",` + + `"authorization_endpoint": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/oauth2/v2.0/authorize",` + + `"device_authorization_endpoint": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/oauth2/v2.0/devicecode",` + + `"http_logout_supported": true,` + + `"frontchannel_logout_supported": true,` + + `"end_session_endpoint": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/oauth2/v2.0/logout",` + + `"claims_supported": [` + + `"sub",` + + `"iss",` + + `"cloud_instance_name",` + + `"cloud_instance_host_name",` + + `"cloud_graph_host_name",` + + `"msgraph_host",` + + `"aud",` + + `"exp",` + + `"iat",` + + `"auth_time",` + + `"acr",` + + `"nonce",` + + `"preferred_username",` + + `"name",` + + `"tid",` + + `"ver",` + + `"at_hash",` + + `"c_hash",` + + `"email"` + + `],` + + `"kerberos_endpoint": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/kerberos",` + + `"tenant_region_scope": "NA",` + + `"cloud_instance_name": "microsoftonline.com",` + + `"cloud_graph_host_name": "graph.windows.net",` + + `"msgraph_host": "graph.microsoft.com",` + + `"rbac_url": "https://pas.windows.net"` + + `}` + + `},` + + `{` + + `"RequestUri": "https://login.microsoftonline.com/fake-tenant/oauth2/v2.0/token",` + + `"RequestMethod": "POST",` + + `"RequestHeaders": {` + + `":authority": "localhost:5001",` + + `":method": "POST",` + + `":path": "/fake-tenant/oauth2/v2.0/token",` + + `":scheme": "https",` + + `"Accept-Encoding": "gzip",` + + `"Content-Length": "2",` + + `"Content-Type": "application/x-www-form-urlencoded; charset=utf-8",` + + `"User-Agent": "azsdk-go-azidentity/v0.12.1 azsdk-go-azcore/v0.21.0 (go1.16.7; linux)"` + + `},` + + `"RequestBody": {},` + + `"StatusCode": 400,` +} + // Set environment variables for the duration of a test. Restore their prior values // after the test completes. Obviated by 1.17's T.Setenv func setEnvironmentVariables(t *testing.T, vars map[string]string) { diff --git a/sdk/azidentity/client_certificate_credential_test.go b/sdk/azidentity/client_certificate_credential_test.go index a7d2c4be5e6c..a52fcf30eade 100644 --- a/sdk/azidentity/client_certificate_credential_test.go +++ b/sdk/azidentity/client_certificate_credential_test.go @@ -8,12 +8,17 @@ import ( "crypto" "crypto/x509" "errors" + "io/ioutil" "log" + "net/http" "os" + "strings" "testing" + "github.com/Azure/azure-sdk-for-go/sdk/azcore" "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" "github.com/Azure/azure-sdk-for-go/sdk/internal/mock" + "github.com/golang-jwt/jwt/v4" ) type certTest struct { @@ -82,6 +87,46 @@ func TestClientCertificateCredential_GetTokenSuccess_withCertificateChain(t *tes } } +func TestClientCertificateCredential_GetTokenSuccess_withCertificateChain_mock(t *testing.T) { + + validateReq := func(req *http.Request) bool { + body, err := ioutil.ReadAll(req.Body) + if err == nil { + bodystr := string(body) + kvps := strings.Split(bodystr, "&") + assertion := strings.Split(kvps[0], "=") + token, _ := jwt.Parse(assertion[1], nil)// func(token *jwt.Token) (interface{}, error) { return []byte(""), nil}) + if _, ok := token.Header["x5c"]; !ok { + t.Fatal("JWT did not contain the x5c header") + } + } + return true + } + + test := allCertTests[0] + + // srv, close := mock.NewServerWithURL(":443") + srv, close := mock.NewServer(mock.WithTransformAllRequestsToTestServerUrl()) + defer close() + srv.AppendResponse() + srv.AppendResponse(mock.WithBody([]byte(getTenantDiscoveryResponse(srv.URL())))) + srv.AppendResponse(mock.WithPredicate(validateReq), mock.WithBody([]byte(accessTokenRespSuccess))) + srv.AppendResponse() + + options := ClientCertificateCredentialOptions{ClientOptions: azcore.ClientOptions{Transport: srv}, SendCertificateChain: true} + cred, err := NewClientCertificateCredential(fakeTenantID, fakeClientID, test.certs, test.key, &options) + if err != nil { + t.Fatal(err) + } + tk, err := cred.GetToken(context.Background(), policy.TokenRequestOptions{Scopes: []string{liveTestScope}}) + if err != nil { + t.Fatal(err) + } + if tk.Token != tokenValue { + t.Fatalf("unexpected token: %s", tk.Token) + } +} + func TestClientCertificateCredential_GetTokenCheckPrivateKeyBlocks(t *testing.T) { test := allCertTests[0] cred, err := NewClientCertificateCredential(fakeTenantID, fakeClientID, test.certs, test.key, nil) diff --git a/sdk/azidentity/environment_credential.go b/sdk/azidentity/environment_credential.go index 14e702b6b021..c49efc23d185 100644 --- a/sdk/azidentity/environment_credential.go +++ b/sdk/azidentity/environment_credential.go @@ -14,6 +14,10 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/internal/log" ) +const ( + envVarSendCertChain = "AZURE_CLIENT_SEND_CERTIFICATE_CHAIN" +) + // EnvironmentCredentialOptions contains optional parameters for EnvironmentCredential type EnvironmentCredentialOptions struct { azcore.ClientOptions @@ -70,7 +74,7 @@ func NewEnvironmentCredential(options *EnvironmentCredentialOptions) (*Environme } return &EnvironmentCredential{cred: cred}, nil } - if certPath := os.Getenv("AZURE_CLIENT_CERTIFICATE_PATH"); certPath != "" { + if certPath := os.Getenv(envVarSendCertChain); certPath != "" { log.Write(EventAuthentication, "Azure Identity => NewEnvironmentCredential() invoking ClientCertificateCredential") certData, err := os.ReadFile(certPath) if err != nil { diff --git a/sdk/azidentity/go.mod b/sdk/azidentity/go.mod index 9829e90382f5..22459be638d6 100644 --- a/sdk/azidentity/go.mod +++ b/sdk/azidentity/go.mod @@ -7,9 +7,12 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3 github.com/AzureAD/microsoft-authentication-library-for-go v0.4.0 github.com/davecgh/go-spew v1.1.1 // indirect + github.com/golang-jwt/jwt/v4 v4.2.0 // indirect golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f // indirect golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 // indirect golang.org/x/text v0.3.7 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) + +replace github.com/Azure/azure-sdk-for-go/sdk/internal => ../internal diff --git a/sdk/azidentity/go.sum b/sdk/azidentity/go.sum index 9a943f908cb2..ba0ded9d7430 100644 --- a/sdk/azidentity/go.sum +++ b/sdk/azidentity/go.sum @@ -11,6 +11,8 @@ github.com/dnaeon/go-vcr v1.1.0 h1:ReYa/UBrRyQdant9B4fNHGoCNKw6qh6P0fsdGmZpR7c= github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c= github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU= +github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= diff --git a/sdk/internal/mock/mock.go b/sdk/internal/mock/mock.go index fe30948197ee..adffd7f65c47 100644 --- a/sdk/internal/mock/mock.go +++ b/sdk/internal/mock/mock.go @@ -12,6 +12,7 @@ import ( "io" "net/http" "net/http/httptest" + "net/url" "sync" "time" ) @@ -38,6 +39,9 @@ type Server struct { // count tracks the number of requests that have been made. count int + + // determines whether all requests will be routed to the httptest Server by changing the Host of each request + routeAllRequestsToMockServer bool } func newServer() *Server { @@ -123,7 +127,20 @@ func (s *Server) Do(req *http.Request) (*http.Response, error) { resp := s.getResponse() return nil, resp.err } - resp, err := s.srv.Client().Do(req) + var err error + var resp *http.Response + if s.routeAllRequestsToMockServer /*&& strings.Contains(req.URL.Host, "login")*/ { + originalURL := req.URL + mockUrl, _ := url.Parse(req.URL.String()) + srvUrl, _ := url.Parse(s.srv.URL) + mockUrl.Host = srvUrl.Host + mockUrl.Scheme = srvUrl.Scheme + req.URL = mockUrl + resp, err = s.srv.Client().Do(req) + req.URL = originalURL + } else { + resp, err = s.srv.Client().Do(req) + } if err != nil { return resp, err } @@ -225,6 +242,12 @@ func (fn fnSrvOpt) apply(s *Server) { fn(s) } +func WithTransformAllRequestsToTestServerUrl() ServerOption { + return fnSrvOpt(func(s *Server) { + s.routeAllRequestsToMockServer = true + }) +} + // WithTLSConfig sets the given TLS config on server. func WithTLSConfig(cfg *tls.Config) ServerOption { return fnSrvOpt(func(s *Server) { From 2eb51966063ce60c31e0aa237c882fb217b75e0d Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Wed, 19 Jan 2022 17:38:28 -0600 Subject: [PATCH 03/11] add EnvironmentCredential test --- .../client_certificate_credential_test.go | 2 - sdk/azidentity/environment_credential.go | 4 +- sdk/azidentity/environment_credential_test.go | 50 +++++++++++++++++++ 3 files changed, 52 insertions(+), 4 deletions(-) diff --git a/sdk/azidentity/client_certificate_credential_test.go b/sdk/azidentity/client_certificate_credential_test.go index a52fcf30eade..e63e61d1a46b 100644 --- a/sdk/azidentity/client_certificate_credential_test.go +++ b/sdk/azidentity/client_certificate_credential_test.go @@ -104,8 +104,6 @@ func TestClientCertificateCredential_GetTokenSuccess_withCertificateChain_mock(t } test := allCertTests[0] - - // srv, close := mock.NewServerWithURL(":443") srv, close := mock.NewServer(mock.WithTransformAllRequestsToTestServerUrl()) defer close() srv.AppendResponse() diff --git a/sdk/azidentity/environment_credential.go b/sdk/azidentity/environment_credential.go index c49efc23d185..248b7a13d852 100644 --- a/sdk/azidentity/environment_credential.go +++ b/sdk/azidentity/environment_credential.go @@ -74,7 +74,7 @@ func NewEnvironmentCredential(options *EnvironmentCredentialOptions) (*Environme } return &EnvironmentCredential{cred: cred}, nil } - if certPath := os.Getenv(envVarSendCertChain); certPath != "" { + if certPath := os.Getenv("AZURE_CLIENT_CERTIFICATE_PATH"); certPath != "" { log.Write(EventAuthentication, "Azure Identity => NewEnvironmentCredential() invoking ClientCertificateCredential") certData, err := os.ReadFile(certPath) if err != nil { @@ -84,7 +84,7 @@ func NewEnvironmentCredential(options *EnvironmentCredentialOptions) (*Environme if err != nil { return nil, fmt.Errorf(`failed to load certificate from "%s": %v`, certPath, err) } - sendChainEnv := os.Getenv("AZURE_CLIENT_SEND_CERTIFICATE_CHAIN") + sendChainEnv := os.Getenv(envVarSendCertChain) sendCertChain := sendChainEnv == "true" || sendChainEnv == "1" o := &ClientCertificateCredentialOptions{AuthorityHost: options.AuthorityHost, ClientOptions: options.ClientOptions, SendCertificateChain: sendCertChain} cred, err := NewClientCertificateCredential(tenantID, clientID, certs, key, o) diff --git a/sdk/azidentity/environment_credential_test.go b/sdk/azidentity/environment_credential_test.go index 16aef6f31592..f4c7c2ede739 100644 --- a/sdk/azidentity/environment_credential_test.go +++ b/sdk/azidentity/environment_credential_test.go @@ -6,10 +6,16 @@ package azidentity import ( "context" "errors" + "io/ioutil" + "net/http" "os" + "strings" "testing" + "github.com/Azure/azure-sdk-for-go/sdk/azcore" "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" + "github.com/Azure/azure-sdk-for-go/sdk/internal/mock" + "github.com/golang-jwt/jwt/v4" ) func initEnvironmentVarsForTest() error { @@ -173,6 +179,50 @@ func TestEnvironmentCredential_UsernamePasswordSet(t *testing.T) { } } +func TestEnvironmentCredential_UsernamePasswordSet_withCertificateChain_mock(t *testing.T) { + resetEnvironmentVarsForTest() + + validateReq := func(req *http.Request) bool { + body, err := ioutil.ReadAll(req.Body) + if err == nil { + bodystr := string(body) + kvps := strings.Split(bodystr, "&") + assertion := strings.Split(kvps[0], "=") + token, _ := jwt.Parse(assertion[1], nil) // func(token *jwt.Token) (interface{}, error) { return []byte(""), nil}) + if _, ok := token.Header["x5c"]; !ok { + t.Fatal("JWT did not contain the x5c header") + } + } + return true + } + + srv, close := mock.NewServer(mock.WithTransformAllRequestsToTestServerUrl()) + defer close() + srv.AppendResponse() + srv.AppendResponse(mock.WithBody([]byte(getTenantDiscoveryResponse(srv.URL())))) + srv.AppendResponse(mock.WithPredicate(validateReq), mock.WithBody([]byte(accessTokenRespSuccess))) + srv.AppendResponse() + + vars := map[string]string{ + "AZURE_CLIENT_ID": liveSP.clientID, + "AZURE_CLIENT_CERTIFICATE_PATH": liveSP.pfxPath, + "AZURE_TENANT_ID": liveSP.tenantID, + envVarSendCertChain: "true", + } + setEnvironmentVariables(t, vars) + cred, err := NewEnvironmentCredential(&EnvironmentCredentialOptions{ClientOptions: azcore.ClientOptions{Transport: srv}}) + if err != nil { + t.Fatal(err) + } + tk, err := cred.GetToken(context.Background(), policy.TokenRequestOptions{Scopes: []string{liveTestScope}}) + if err != nil { + t.Fatal(err) + } + if tk.Token != tokenValue { + t.Fatalf("unexpected token: %s", tk.Token) + } +} + func TestEnvironmentCredential_ClientSecretLive(t *testing.T) { vars := map[string]string{ "AZURE_CLIENT_ID": liveSP.clientID, From c9c969092d3ab2fc35e0887d11d9905e69f0bead Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Thu, 20 Jan 2022 10:33:51 -0600 Subject: [PATCH 04/11] fb --- sdk/internal/mock/mock.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sdk/internal/mock/mock.go b/sdk/internal/mock/mock.go index adffd7f65c47..cd8f02531b78 100644 --- a/sdk/internal/mock/mock.go +++ b/sdk/internal/mock/mock.go @@ -129,13 +129,13 @@ func (s *Server) Do(req *http.Request) (*http.Response, error) { } var err error var resp *http.Response - if s.routeAllRequestsToMockServer /*&& strings.Contains(req.URL.Host, "login")*/ { + if s.routeAllRequestsToMockServer { originalURL := req.URL - mockUrl, _ := url.Parse(req.URL.String()) + mockUrl := *req.URL srvUrl, _ := url.Parse(s.srv.URL) mockUrl.Host = srvUrl.Host mockUrl.Scheme = srvUrl.Scheme - req.URL = mockUrl + req.URL = &mockUrl resp, err = s.srv.Client().Do(req) req.URL = originalURL } else { From 04dd79c50873d904dd1bc0202ef8438db262e514 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Thu, 20 Jan 2022 11:07:02 -0600 Subject: [PATCH 05/11] format --- sdk/azidentity/azidentity_test.go | 164 +++++++++--------- .../client_certificate_credential_test.go | 2 +- 2 files changed, 83 insertions(+), 83 deletions(-) diff --git a/sdk/azidentity/azidentity_test.go b/sdk/azidentity/azidentity_test.go index 596d815ae13b..557e81756b79 100644 --- a/sdk/azidentity/azidentity_test.go +++ b/sdk/azidentity/azidentity_test.go @@ -29,88 +29,88 @@ const ( func getTenantDiscoveryResponse(url string) string { return `{` + - `"token_endpoint": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/oauth2/v2.0/token",` + - `"token_endpoint_auth_methods_supported": [` + - `"client_secret_post",` + - `"private_key_jwt",` + - `"client_secret_basic"` + - `],` + - `"jwks_uri": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/discovery/v2.0/keys",` + - `"response_modes_supported": [` + - `"query",` + - `"fragment",` + - `"form_post"` + - `],` + - `"subject_types_supported": [` + - `"pairwise"` + - `],` + - `"id_token_signing_alg_values_supported": [` + - `"RS256"` + - `],` + - `"response_types_supported": [` + - `"code",` + - `"id_token",` + - `"code id_token",` + - `"id_token token"` + - `],` + - `"scopes_supported": [` + - `"openid",` + - `"profile",` + - `"email",` + - `"offline_access"` + - `],` + - `"issuer": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/v2.0",` + - `"request_uri_parameter_supported": false,` + - `"userinfo_endpoint": "https://graph.microsoft.com/oidc/userinfo",` + - `"authorization_endpoint": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/oauth2/v2.0/authorize",` + - `"device_authorization_endpoint": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/oauth2/v2.0/devicecode",` + - `"http_logout_supported": true,` + - `"frontchannel_logout_supported": true,` + - `"end_session_endpoint": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/oauth2/v2.0/logout",` + - `"claims_supported": [` + - `"sub",` + - `"iss",` + - `"cloud_instance_name",` + - `"cloud_instance_host_name",` + - `"cloud_graph_host_name",` + - `"msgraph_host",` + - `"aud",` + - `"exp",` + - `"iat",` + - `"auth_time",` + - `"acr",` + - `"nonce",` + - `"preferred_username",` + - `"name",` + - `"tid",` + - `"ver",` + - `"at_hash",` + - `"c_hash",` + - `"email"` + - `],` + - `"kerberos_endpoint": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/kerberos",` + - `"tenant_region_scope": "NA",` + - `"cloud_instance_name": "microsoftonline.com",` + - `"cloud_graph_host_name": "graph.windows.net",` + - `"msgraph_host": "graph.microsoft.com",` + - `"rbac_url": "https://pas.windows.net"` + - `}` + - `},` + - `{` + - `"RequestUri": "https://login.microsoftonline.com/fake-tenant/oauth2/v2.0/token",` + - `"RequestMethod": "POST",` + - `"RequestHeaders": {` + - `":authority": "localhost:5001",` + - `":method": "POST",` + - `":path": "/fake-tenant/oauth2/v2.0/token",` + - `":scheme": "https",` + - `"Accept-Encoding": "gzip",` + - `"Content-Length": "2",` + - `"Content-Type": "application/x-www-form-urlencoded; charset=utf-8",` + - `"User-Agent": "azsdk-go-azidentity/v0.12.1 azsdk-go-azcore/v0.21.0 (go1.16.7; linux)"` + - `},` + - `"RequestBody": {},` + - `"StatusCode": 400,` + `"token_endpoint": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/oauth2/v2.0/token",` + + `"token_endpoint_auth_methods_supported": [` + + `"client_secret_post",` + + `"private_key_jwt",` + + `"client_secret_basic"` + + `],` + + `"jwks_uri": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/discovery/v2.0/keys",` + + `"response_modes_supported": [` + + `"query",` + + `"fragment",` + + `"form_post"` + + `],` + + `"subject_types_supported": [` + + `"pairwise"` + + `],` + + `"id_token_signing_alg_values_supported": [` + + `"RS256"` + + `],` + + `"response_types_supported": [` + + `"code",` + + `"id_token",` + + `"code id_token",` + + `"id_token token"` + + `],` + + `"scopes_supported": [` + + `"openid",` + + `"profile",` + + `"email",` + + `"offline_access"` + + `],` + + `"issuer": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/v2.0",` + + `"request_uri_parameter_supported": false,` + + `"userinfo_endpoint": "https://graph.microsoft.com/oidc/userinfo",` + + `"authorization_endpoint": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/oauth2/v2.0/authorize",` + + `"device_authorization_endpoint": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/oauth2/v2.0/devicecode",` + + `"http_logout_supported": true,` + + `"frontchannel_logout_supported": true,` + + `"end_session_endpoint": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/oauth2/v2.0/logout",` + + `"claims_supported": [` + + `"sub",` + + `"iss",` + + `"cloud_instance_name",` + + `"cloud_instance_host_name",` + + `"cloud_graph_host_name",` + + `"msgraph_host",` + + `"aud",` + + `"exp",` + + `"iat",` + + `"auth_time",` + + `"acr",` + + `"nonce",` + + `"preferred_username",` + + `"name",` + + `"tid",` + + `"ver",` + + `"at_hash",` + + `"c_hash",` + + `"email"` + + `],` + + `"kerberos_endpoint": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/kerberos",` + + `"tenant_region_scope": "NA",` + + `"cloud_instance_name": "microsoftonline.com",` + + `"cloud_graph_host_name": "graph.windows.net",` + + `"msgraph_host": "graph.microsoft.com",` + + `"rbac_url": "https://pas.windows.net"` + + `}` + + `},` + + `{` + + `"RequestUri": "https://login.microsoftonline.com/fake-tenant/oauth2/v2.0/token",` + + `"RequestMethod": "POST",` + + `"RequestHeaders": {` + + `":authority": "localhost:5001",` + + `":method": "POST",` + + `":path": "/fake-tenant/oauth2/v2.0/token",` + + `":scheme": "https",` + + `"Accept-Encoding": "gzip",` + + `"Content-Length": "2",` + + `"Content-Type": "application/x-www-form-urlencoded; charset=utf-8",` + + `"User-Agent": "azsdk-go-azidentity/v0.12.1 azsdk-go-azcore/v0.21.0 (go1.16.7; linux)"` + + `},` + + `"RequestBody": {},` + + `"StatusCode": 400,` } // Set environment variables for the duration of a test. Restore their prior values diff --git a/sdk/azidentity/client_certificate_credential_test.go b/sdk/azidentity/client_certificate_credential_test.go index e63e61d1a46b..df095ced4d20 100644 --- a/sdk/azidentity/client_certificate_credential_test.go +++ b/sdk/azidentity/client_certificate_credential_test.go @@ -95,7 +95,7 @@ func TestClientCertificateCredential_GetTokenSuccess_withCertificateChain_mock(t bodystr := string(body) kvps := strings.Split(bodystr, "&") assertion := strings.Split(kvps[0], "=") - token, _ := jwt.Parse(assertion[1], nil)// func(token *jwt.Token) (interface{}, error) { return []byte(""), nil}) + token, _ := jwt.Parse(assertion[1], nil) if _, ok := token.Header["x5c"]; !ok { t.Fatal("JWT did not contain the x5c header") } From 4b9eedc558538d7f7abb2b7db191e5e06aba2386 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Fri, 21 Jan 2022 10:06:51 -0600 Subject: [PATCH 06/11] fb --- sdk/azidentity/azidentity_test.go | 191 ++++++++++-------- .../client_certificate_credential_test.go | 23 +-- sdk/azidentity/environment_credential.go | 12 +- sdk/azidentity/environment_credential_test.go | 25 +-- 4 files changed, 118 insertions(+), 133 deletions(-) diff --git a/sdk/azidentity/azidentity_test.go b/sdk/azidentity/azidentity_test.go index 557e81756b79..8a16be65f5a1 100644 --- a/sdk/azidentity/azidentity_test.go +++ b/sdk/azidentity/azidentity_test.go @@ -6,11 +6,16 @@ package azidentity import ( "context" "errors" + "io/ioutil" + "net/http" "os" + "strings" "testing" + "github.com/Azure/azure-sdk-for-go/sdk/internal/mock" "github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential" "github.com/AzureAD/microsoft-authentication-library-for-go/apps/public" + "github.com/golang-jwt/jwt/v4" ) // constants used throughout this package @@ -27,90 +32,108 @@ const ( customHostString = "https://custommock.com/" ) -func getTenantDiscoveryResponse(url string) string { - return `{` + - `"token_endpoint": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/oauth2/v2.0/token",` + - `"token_endpoint_auth_methods_supported": [` + - `"client_secret_post",` + - `"private_key_jwt",` + - `"client_secret_basic"` + - `],` + - `"jwks_uri": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/discovery/v2.0/keys",` + - `"response_modes_supported": [` + - `"query",` + - `"fragment",` + - `"form_post"` + - `],` + - `"subject_types_supported": [` + - `"pairwise"` + - `],` + - `"id_token_signing_alg_values_supported": [` + - `"RS256"` + - `],` + - `"response_types_supported": [` + - `"code",` + - `"id_token",` + - `"code id_token",` + - `"id_token token"` + - `],` + - `"scopes_supported": [` + - `"openid",` + - `"profile",` + - `"email",` + - `"offline_access"` + - `],` + - `"issuer": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/v2.0",` + - `"request_uri_parameter_supported": false,` + - `"userinfo_endpoint": "https://graph.microsoft.com/oidc/userinfo",` + - `"authorization_endpoint": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/oauth2/v2.0/authorize",` + - `"device_authorization_endpoint": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/oauth2/v2.0/devicecode",` + - `"http_logout_supported": true,` + - `"frontchannel_logout_supported": true,` + - `"end_session_endpoint": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/oauth2/v2.0/logout",` + - `"claims_supported": [` + - `"sub",` + - `"iss",` + - `"cloud_instance_name",` + - `"cloud_instance_host_name",` + - `"cloud_graph_host_name",` + - `"msgraph_host",` + - `"aud",` + - `"exp",` + - `"iat",` + - `"auth_time",` + - `"acr",` + - `"nonce",` + - `"preferred_username",` + - `"name",` + - `"tid",` + - `"ver",` + - `"at_hash",` + - `"c_hash",` + - `"email"` + - `],` + - `"kerberos_endpoint": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/kerberos",` + - `"tenant_region_scope": "NA",` + - `"cloud_instance_name": "microsoftonline.com",` + - `"cloud_graph_host_name": "graph.windows.net",` + - `"msgraph_host": "graph.microsoft.com",` + - `"rbac_url": "https://pas.windows.net"` + - `}` + - `},` + - `{` + - `"RequestUri": "https://login.microsoftonline.com/fake-tenant/oauth2/v2.0/token",` + - `"RequestMethod": "POST",` + - `"RequestHeaders": {` + - `":authority": "localhost:5001",` + - `":method": "POST",` + - `":path": "/fake-tenant/oauth2/v2.0/token",` + - `":scheme": "https",` + - `"Accept-Encoding": "gzip",` + - `"Content-Length": "2",` + - `"Content-Type": "application/x-www-form-urlencoded; charset=utf-8",` + - `"User-Agent": "azsdk-go-azidentity/v0.12.1 azsdk-go-azcore/v0.21.0 (go1.16.7; linux)"` + - `},` + - `"RequestBody": {},` + - `"StatusCode": 400,` +func getTenantDiscoveryResponse() string { + return `{ + "token_endpoint": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/oauth2/v2.0/token", + "token_endpoint_auth_methods_supported": [ + "client_secret_post", + "private_key_jwt", + "client_secret_basic" + ], + "jwks_uri": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/discovery/v2.0/keys", + "response_modes_supported": [ + "query", + "fragment", + "form_post" + ], + "subject_types_supported": [ + "pairwise" + ], + "id_token_signing_alg_values_supported": [ + "RS256" + ], + "response_types_supported": [ + "code", + "id_token", + "code id_token", + "id_token token" + ], + "scopes_supported": [ + "openid", + "profile", + "email", + "offline_access" + ], + "issuer": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/v2.0", + "request_uri_parameter_supported": false, + "userinfo_endpoint": "https://graph.microsoft.com/oidc/userinfo", + "authorization_endpoint": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/oauth2/v2.0/authorize", + "device_authorization_endpoint": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/oauth2/v2.0/devicecode", + "http_logout_supported": true, + "frontchannel_logout_supported": true, + "end_session_endpoint": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/oauth2/v2.0/logout", + "claims_supported": [ + "sub", + "iss", + "cloud_instance_name", + "cloud_instance_host_name", + "cloud_graph_host_name", + "msgraph_host", + "aud", + "exp", + "iat", + "auth_time", + "acr", + "nonce", + "preferred_username", + "name", + "tid", + "ver", + "at_hash", + "c_hash", + "email" + ], + "kerberos_endpoint": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/kerberos", + "tenant_region_scope": "NA", + "cloud_instance_name": "microsoftonline.com", + "cloud_graph_host_name": "graph.windows.net", + "msgraph_host": "graph.microsoft.com", + "rbac_url": "https://pas.windows.net" + } + }, + { + "RequestUri": "https://login.microsoftonline.com/fake-tenant/oauth2/v2.0/token", + "RequestMethod": "POST", + "RequestHeaders": { + ":authority": "localhost:5001", + ":method": "POST", + ":path": "/fake-tenant/oauth2/v2.0/token", + ":scheme": "https", + "Accept-Encoding": "gzip", + "Content-Length": "2", + "Content-Type": "application/x-www-form-urlencoded; charset=utf-8", + "User-Agent": "azsdk-go-azidentity/v0.12.1 azsdk-go-azcore/v0.21.0 (go1.16.7; linux)" + }, + "RequestBody": {}, + "StatusCode": 400,` +} + +func validateJWTRequestContainsHeader(t *testing.T, headerName string) mock.ResponsePredicate { + return func(req *http.Request) bool { + body, err := ioutil.ReadAll(req.Body) + if err == nil { + bodystr := string(body) + kvps := strings.Split(bodystr, "&") + assertion := strings.Split(kvps[0], "=") + token, _ := jwt.Parse(assertion[1], nil) + if _, ok := token.Header[headerName]; !ok { + t.Fatalf("JWT did not contain the %s header", headerName) + } + } else { + t.Fatal("Expected a request with the JWT in the body.") + } + return true + } } // Set environment variables for the duration of a test. Restore their prior values diff --git a/sdk/azidentity/client_certificate_credential_test.go b/sdk/azidentity/client_certificate_credential_test.go index df095ced4d20..5f7cba87f3af 100644 --- a/sdk/azidentity/client_certificate_credential_test.go +++ b/sdk/azidentity/client_certificate_credential_test.go @@ -8,17 +8,13 @@ import ( "crypto" "crypto/x509" "errors" - "io/ioutil" "log" - "net/http" "os" - "strings" "testing" "github.com/Azure/azure-sdk-for-go/sdk/azcore" "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" "github.com/Azure/azure-sdk-for-go/sdk/internal/mock" - "github.com/golang-jwt/jwt/v4" ) type certTest struct { @@ -88,27 +84,12 @@ func TestClientCertificateCredential_GetTokenSuccess_withCertificateChain(t *tes } func TestClientCertificateCredential_GetTokenSuccess_withCertificateChain_mock(t *testing.T) { - - validateReq := func(req *http.Request) bool { - body, err := ioutil.ReadAll(req.Body) - if err == nil { - bodystr := string(body) - kvps := strings.Split(bodystr, "&") - assertion := strings.Split(kvps[0], "=") - token, _ := jwt.Parse(assertion[1], nil) - if _, ok := token.Header["x5c"]; !ok { - t.Fatal("JWT did not contain the x5c header") - } - } - return true - } - test := allCertTests[0] srv, close := mock.NewServer(mock.WithTransformAllRequestsToTestServerUrl()) defer close() srv.AppendResponse() - srv.AppendResponse(mock.WithBody([]byte(getTenantDiscoveryResponse(srv.URL())))) - srv.AppendResponse(mock.WithPredicate(validateReq), mock.WithBody([]byte(accessTokenRespSuccess))) + srv.AppendResponse(mock.WithBody([]byte(getTenantDiscoveryResponse()))) + srv.AppendResponse(mock.WithPredicate(validateJWTRequestContainsHeader(t, "x5c")), mock.WithBody([]byte(accessTokenRespSuccess))) srv.AppendResponse() options := ClientCertificateCredentialOptions{ClientOptions: azcore.ClientOptions{Transport: srv}, SendCertificateChain: true} diff --git a/sdk/azidentity/environment_credential.go b/sdk/azidentity/environment_credential.go index 248b7a13d852..03332a851421 100644 --- a/sdk/azidentity/environment_credential.go +++ b/sdk/azidentity/environment_credential.go @@ -8,15 +8,14 @@ import ( "errors" "fmt" "os" + "strings" "github.com/Azure/azure-sdk-for-go/sdk/azcore" "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" "github.com/Azure/azure-sdk-for-go/sdk/internal/log" ) -const ( - envVarSendCertChain = "AZURE_CLIENT_SEND_CERTIFICATE_CHAIN" -) +const envVarSendCertChain = "AZURE_CLIENT_SEND_CERTIFICATE_CHAIN" // EnvironmentCredentialOptions contains optional parameters for EnvironmentCredential type EnvironmentCredentialOptions struct { @@ -84,9 +83,10 @@ func NewEnvironmentCredential(options *EnvironmentCredentialOptions) (*Environme if err != nil { return nil, fmt.Errorf(`failed to load certificate from "%s": %v`, certPath, err) } - sendChainEnv := os.Getenv(envVarSendCertChain) - sendCertChain := sendChainEnv == "true" || sendChainEnv == "1" - o := &ClientCertificateCredentialOptions{AuthorityHost: options.AuthorityHost, ClientOptions: options.ClientOptions, SendCertificateChain: sendCertChain} + o := &ClientCertificateCredentialOptions{AuthorityHost: options.AuthorityHost, ClientOptions: options.ClientOptions} + if v, ok := os.LookupEnv(envVarSendCertChain); ok { + o.SendCertificateChain = v == "1" || strings.ToLower(v) == "true" + } cred, err := NewClientCertificateCredential(tenantID, clientID, certs, key, o) if err != nil { return nil, err diff --git a/sdk/azidentity/environment_credential_test.go b/sdk/azidentity/environment_credential_test.go index f4c7c2ede739..76ff277dd109 100644 --- a/sdk/azidentity/environment_credential_test.go +++ b/sdk/azidentity/environment_credential_test.go @@ -6,16 +6,12 @@ package azidentity import ( "context" "errors" - "io/ioutil" - "net/http" "os" - "strings" "testing" "github.com/Azure/azure-sdk-for-go/sdk/azcore" "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" "github.com/Azure/azure-sdk-for-go/sdk/internal/mock" - "github.com/golang-jwt/jwt/v4" ) func initEnvironmentVarsForTest() error { @@ -179,28 +175,13 @@ func TestEnvironmentCredential_UsernamePasswordSet(t *testing.T) { } } -func TestEnvironmentCredential_UsernamePasswordSet_withCertificateChain_mock(t *testing.T) { +func TestEnvironmentCredential_SendCertificateChain(t *testing.T) { resetEnvironmentVarsForTest() - - validateReq := func(req *http.Request) bool { - body, err := ioutil.ReadAll(req.Body) - if err == nil { - bodystr := string(body) - kvps := strings.Split(bodystr, "&") - assertion := strings.Split(kvps[0], "=") - token, _ := jwt.Parse(assertion[1], nil) // func(token *jwt.Token) (interface{}, error) { return []byte(""), nil}) - if _, ok := token.Header["x5c"]; !ok { - t.Fatal("JWT did not contain the x5c header") - } - } - return true - } - srv, close := mock.NewServer(mock.WithTransformAllRequestsToTestServerUrl()) defer close() srv.AppendResponse() - srv.AppendResponse(mock.WithBody([]byte(getTenantDiscoveryResponse(srv.URL())))) - srv.AppendResponse(mock.WithPredicate(validateReq), mock.WithBody([]byte(accessTokenRespSuccess))) + srv.AppendResponse(mock.WithBody([]byte(getTenantDiscoveryResponse()))) + srv.AppendResponse(mock.WithPredicate(validateJWTRequestContainsHeader(t, "x5c")), mock.WithBody([]byte(accessTokenRespSuccess))) srv.AppendResponse() vars := map[string]string{ From c5c7e77ebb50df17bab15429a0ef534aa1b7cc7a Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Fri, 21 Jan 2022 15:27:18 -0600 Subject: [PATCH 07/11] fb --- sdk/azidentity/azidentity_test.go | 14 +++++++------- sdk/internal/mock/mock.go | 7 ++++++- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/sdk/azidentity/azidentity_test.go b/sdk/azidentity/azidentity_test.go index 8a16be65f5a1..feb7c03f2a3e 100644 --- a/sdk/azidentity/azidentity_test.go +++ b/sdk/azidentity/azidentity_test.go @@ -28,12 +28,9 @@ const ( // constants for this file const ( - envHostString = "https://mock.com/" - customHostString = "https://custommock.com/" -) - -func getTenantDiscoveryResponse() string { - return `{ + envHostString = "https://mock.com/" + customHostString = "https://custommock.com/" + getTenantDiscoveryResponse = `{ "token_endpoint": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/oauth2/v2.0/token", "token_endpoint_auth_methods_supported": [ "client_secret_post", @@ -116,7 +113,7 @@ func getTenantDiscoveryResponse() string { }, "RequestBody": {}, "StatusCode": 400,` -} +) func validateJWTRequestContainsHeader(t *testing.T, headerName string) mock.ResponsePredicate { return func(req *http.Request) bool { @@ -126,6 +123,9 @@ func validateJWTRequestContainsHeader(t *testing.T, headerName string) mock.Resp kvps := strings.Split(bodystr, "&") assertion := strings.Split(kvps[0], "=") token, _ := jwt.Parse(assertion[1], nil) + if token == nil { + t.Fatalf("Failed to parse the JWT token: %s.", assertion[1]) + } if _, ok := token.Header[headerName]; !ok { t.Fatalf("JWT did not contain the %s header", headerName) } diff --git a/sdk/internal/mock/mock.go b/sdk/internal/mock/mock.go index cd8f02531b78..c80ca1dfd7af 100644 --- a/sdk/internal/mock/mock.go +++ b/sdk/internal/mock/mock.go @@ -9,6 +9,7 @@ package mock import ( "crypto/tls" "errors" + "fmt" "io" "net/http" "net/http/httptest" @@ -130,9 +131,13 @@ func (s *Server) Do(req *http.Request) (*http.Response, error) { var err error var resp *http.Response if s.routeAllRequestsToMockServer { + var srvUrl *url.URL originalURL := req.URL mockUrl := *req.URL - srvUrl, _ := url.Parse(s.srv.URL) + srvUrl, err = url.Parse(s.srv.URL) + if err != nil { + return nil, fmt.Errorf("Unable to parse the test server URL: %v", err) + } mockUrl.Host = srvUrl.Host mockUrl.Scheme = srvUrl.Scheme req.URL = &mockUrl From bdcfb3df02e7d69fd555b4a118b27fd76e05e4ed Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Fri, 21 Jan 2022 16:06:16 -0600 Subject: [PATCH 08/11] fix --- sdk/azidentity/client_certificate_credential_test.go | 2 +- sdk/azidentity/environment_credential_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/azidentity/client_certificate_credential_test.go b/sdk/azidentity/client_certificate_credential_test.go index 5f7cba87f3af..0ba9c0654cce 100644 --- a/sdk/azidentity/client_certificate_credential_test.go +++ b/sdk/azidentity/client_certificate_credential_test.go @@ -88,7 +88,7 @@ func TestClientCertificateCredential_GetTokenSuccess_withCertificateChain_mock(t srv, close := mock.NewServer(mock.WithTransformAllRequestsToTestServerUrl()) defer close() srv.AppendResponse() - srv.AppendResponse(mock.WithBody([]byte(getTenantDiscoveryResponse()))) + srv.AppendResponse(mock.WithBody([]byte(getTenantDiscoveryResponse))) srv.AppendResponse(mock.WithPredicate(validateJWTRequestContainsHeader(t, "x5c")), mock.WithBody([]byte(accessTokenRespSuccess))) srv.AppendResponse() diff --git a/sdk/azidentity/environment_credential_test.go b/sdk/azidentity/environment_credential_test.go index 76ff277dd109..975f699960c8 100644 --- a/sdk/azidentity/environment_credential_test.go +++ b/sdk/azidentity/environment_credential_test.go @@ -180,7 +180,7 @@ func TestEnvironmentCredential_SendCertificateChain(t *testing.T) { srv, close := mock.NewServer(mock.WithTransformAllRequestsToTestServerUrl()) defer close() srv.AppendResponse() - srv.AppendResponse(mock.WithBody([]byte(getTenantDiscoveryResponse()))) + srv.AppendResponse(mock.WithBody([]byte(getTenantDiscoveryResponse))) srv.AppendResponse(mock.WithPredicate(validateJWTRequestContainsHeader(t, "x5c")), mock.WithBody([]byte(accessTokenRespSuccess))) srv.AppendResponse() From 662b2dc561c2b138cc37e5c7d5858ed51ee46c20 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Mon, 24 Jan 2022 08:44:03 -0600 Subject: [PATCH 09/11] Apply suggestions from code review Co-authored-by: Charles Lowell <10964656+chlowell@users.noreply.github.com> --- sdk/azidentity/azidentity_test.go | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/sdk/azidentity/azidentity_test.go b/sdk/azidentity/azidentity_test.go index feb7c03f2a3e..ad77cafdfa85 100644 --- a/sdk/azidentity/azidentity_test.go +++ b/sdk/azidentity/azidentity_test.go @@ -30,7 +30,7 @@ const ( const ( envHostString = "https://mock.com/" customHostString = "https://custommock.com/" - getTenantDiscoveryResponse = `{ + tenantDiscoveryResponse = `{ "token_endpoint": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/oauth2/v2.0/token", "token_endpoint_auth_methods_supported": [ "client_secret_post", @@ -97,22 +97,6 @@ const ( "msgraph_host": "graph.microsoft.com", "rbac_url": "https://pas.windows.net" } - }, - { - "RequestUri": "https://login.microsoftonline.com/fake-tenant/oauth2/v2.0/token", - "RequestMethod": "POST", - "RequestHeaders": { - ":authority": "localhost:5001", - ":method": "POST", - ":path": "/fake-tenant/oauth2/v2.0/token", - ":scheme": "https", - "Accept-Encoding": "gzip", - "Content-Length": "2", - "Content-Type": "application/x-www-form-urlencoded; charset=utf-8", - "User-Agent": "azsdk-go-azidentity/v0.12.1 azsdk-go-azcore/v0.21.0 (go1.16.7; linux)" - }, - "RequestBody": {}, - "StatusCode": 400,` ) func validateJWTRequestContainsHeader(t *testing.T, headerName string) mock.ResponsePredicate { From 5f77bf8d29df16afe362641489541a7d044b93f1 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Mon, 24 Jan 2022 08:50:01 -0600 Subject: [PATCH 10/11] fb --- sdk/azidentity/azidentity_test.go | 25 +++++++++---------- .../client_certificate_credential_test.go | 2 +- sdk/azidentity/environment_credential_test.go | 2 +- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/sdk/azidentity/azidentity_test.go b/sdk/azidentity/azidentity_test.go index ad77cafdfa85..be91371f6ce1 100644 --- a/sdk/azidentity/azidentity_test.go +++ b/sdk/azidentity/azidentity_test.go @@ -96,26 +96,25 @@ const ( "cloud_graph_host_name": "graph.windows.net", "msgraph_host": "graph.microsoft.com", "rbac_url": "https://pas.windows.net" - } + }` ) func validateJWTRequestContainsHeader(t *testing.T, headerName string) mock.ResponsePredicate { return func(req *http.Request) bool { body, err := ioutil.ReadAll(req.Body) - if err == nil { - bodystr := string(body) - kvps := strings.Split(bodystr, "&") - assertion := strings.Split(kvps[0], "=") - token, _ := jwt.Parse(assertion[1], nil) - if token == nil { - t.Fatalf("Failed to parse the JWT token: %s.", assertion[1]) - } - if _, ok := token.Header[headerName]; !ok { - t.Fatalf("JWT did not contain the %s header", headerName) - } - } else { + if err != nil { t.Fatal("Expected a request with the JWT in the body.") } + bodystr := string(body) + kvps := strings.Split(bodystr, "&") + assertion := strings.Split(kvps[0], "=") + token, _ := jwt.Parse(assertion[1], nil) + if token == nil { + t.Fatalf("Failed to parse the JWT token: %s.", assertion[1]) + } + if _, ok := token.Header[headerName]; !ok { + t.Fatalf("JWT did not contain the %s header", headerName) + } return true } } diff --git a/sdk/azidentity/client_certificate_credential_test.go b/sdk/azidentity/client_certificate_credential_test.go index 0ba9c0654cce..4d287815041a 100644 --- a/sdk/azidentity/client_certificate_credential_test.go +++ b/sdk/azidentity/client_certificate_credential_test.go @@ -88,7 +88,7 @@ func TestClientCertificateCredential_GetTokenSuccess_withCertificateChain_mock(t srv, close := mock.NewServer(mock.WithTransformAllRequestsToTestServerUrl()) defer close() srv.AppendResponse() - srv.AppendResponse(mock.WithBody([]byte(getTenantDiscoveryResponse))) + srv.AppendResponse(mock.WithBody([]byte(tenantDiscoveryResponse))) srv.AppendResponse(mock.WithPredicate(validateJWTRequestContainsHeader(t, "x5c")), mock.WithBody([]byte(accessTokenRespSuccess))) srv.AppendResponse() diff --git a/sdk/azidentity/environment_credential_test.go b/sdk/azidentity/environment_credential_test.go index 975f699960c8..2b385d3e708a 100644 --- a/sdk/azidentity/environment_credential_test.go +++ b/sdk/azidentity/environment_credential_test.go @@ -180,7 +180,7 @@ func TestEnvironmentCredential_SendCertificateChain(t *testing.T) { srv, close := mock.NewServer(mock.WithTransformAllRequestsToTestServerUrl()) defer close() srv.AppendResponse() - srv.AppendResponse(mock.WithBody([]byte(getTenantDiscoveryResponse))) + srv.AppendResponse(mock.WithBody([]byte(tenantDiscoveryResponse))) srv.AppendResponse(mock.WithPredicate(validateJWTRequestContainsHeader(t, "x5c")), mock.WithBody([]byte(accessTokenRespSuccess))) srv.AppendResponse() From 2bdd24854dedd0486e274f47af07329f5ccf24af Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Mon, 24 Jan 2022 11:34:14 -0600 Subject: [PATCH 11/11] format --- sdk/azidentity/azidentity_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/azidentity/azidentity_test.go b/sdk/azidentity/azidentity_test.go index be91371f6ce1..3f24c335a181 100644 --- a/sdk/azidentity/azidentity_test.go +++ b/sdk/azidentity/azidentity_test.go @@ -28,8 +28,8 @@ const ( // constants for this file const ( - envHostString = "https://mock.com/" - customHostString = "https://custommock.com/" + envHostString = "https://mock.com/" + customHostString = "https://custommock.com/" tenantDiscoveryResponse = `{ "token_endpoint": "https://login.microsoftonline.com/3c631bb7-a9f7-4343-a5ba-a6159135f1fc/oauth2/v2.0/token", "token_endpoint_auth_methods_supported": [