From 9ed4d33b3ab948fe3bbcea2d64526150b2968aaf Mon Sep 17 00:00:00 2001 From: Patrick Jones Date: Tue, 13 Oct 2020 12:02:46 -0700 Subject: [PATCH 01/18] Draft showing WIP testing approach. --- google/internal/oauth/stsExchange.go | 90 +++++++++++++++++ google/internal/oauth/stsExchange_test.go | 113 ++++++++++++++++++++++ 2 files changed, 203 insertions(+) create mode 100644 google/internal/oauth/stsExchange.go create mode 100644 google/internal/oauth/stsExchange_test.go diff --git a/google/internal/oauth/stsExchange.go b/google/internal/oauth/stsExchange.go new file mode 100644 index 0000000..24684e0 --- /dev/null +++ b/google/internal/oauth/stsExchange.go @@ -0,0 +1,90 @@ +package externalaccount + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "strconv" + "strings" +) + +func ExchangeToken(endpoint string, request *STSTokenExchangeRequest, authentication ClientAuthentication, headers http.Header, options map[string]interface{}) (*STSTokenExchangeResponse, error) { + + client := &http.Client{} + + data := url.Values{} + data.Set("audience", request.Audience) + data.Set("grant_type", "urn:ietf:params:oauth:grant-type:token-exchange") + data.Set("requested_token_type", "urn:ietf:params:oauth:token-type:access_token") + data.Set("subject_token_type", request.SubjectTokenType) + data.Set("subject_token", request.SubjectToken) + data.Set("scope", strings.Join(request.Scope, " ")) + + //req, err := http.NewRequest("POST", endpoint, strings.NewReader(data.Encode())) + //if err != nil { + // fmt.Errorf("oauth2/google: failed to properly build http request") + //} + //for key, _ := range headers { + // for _, val := range headers.Values(key) { + // req.Header.Add(key, val) + // } + //} + //if authentication.ClientID != "" && authentication.ClientSecret != "" { + // plainHeader := authentication.ClientID + ":" + authentication.ClientSecret + // req.Header.Add("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(plainHeader))) + //} + //req.Header.Add("Content-Length", strconv.Itoa(len(data.Encode()))) + authentication.InjectAuthentication(&data, &headers) + req, err := http.NewRequest("POST", endpoint, strings.NewReader(data.Encode())) + if err != nil { + fmt.Errorf("oauth2/google: failed to properly build http request") + } + for key, _ := range headers { + for _, val := range headers.Values(key) { + req.Header.Add(key, val) + } + } + req.Header.Add("Content-Length", strconv.Itoa(len(data.Encode()))) + + + resp, err := client.Do(req) + if err != nil { + fmt.Errorf("oauth2/google: invalid response from Secure Token Server: #{err}") + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + fmt.Errorf("oauth2/google: invalid body in Secute Token Server response: #{err}") + } + var stsResp STSTokenExchangeResponse + err = json.Unmarshal(body, &stsResp) + if err != nil { + fmt.Println(err) + } + return &stsResp, nil +} + +type STSTokenExchangeRequest struct { + ActingParty struct { + ActorToken string + ActorTokenType string + } + GrantType string + Resource string + Audience string + Scope []string + RequestedTokenType string + SubjectToken string + SubjectTokenType string +} + +type STSTokenExchangeResponse struct { + AccessToken string `json:"access_token"` + IssuedTokenType string `json:"issued_token_type"` + TokenType string `json:"token_type"` + ExpiresIn int `json:"expires_in"` + Scope string `json:"scope"` + RefreshToken string `json:"refresh_token"` +} diff --git a/google/internal/oauth/stsExchange_test.go b/google/internal/oauth/stsExchange_test.go new file mode 100644 index 0000000..fd0ca1f --- /dev/null +++ b/google/internal/oauth/stsExchange_test.go @@ -0,0 +1,113 @@ +package externalaccount + +import ( + "net/http" + "net/http/httptest" + "testing" +) + +stsExchange_test.go + +package externalaccount + +import ( +"github.com/google/go-cmp/cmp" +"golang.org/x/oauth2" +"net/http" +"net/http/httptest" +"testing" +) + +var auth = ClientAuthentication{ + AuthStyle: oauth2.AuthStyleInHeader, + ClientID: clientID, + ClientSecret: clientSecret, +} + +var tokenRequest = STSTokenExchangeRequest{ + ActingParty: struct { + ActorToken string + ActorTokenType string + }{}, + GrantType: "urn:ietf:params:oauth:grant-type:token-exchange", + Resource: "", + Audience: "32555940559.apps.googleusercontent.com", //TODO: Make sure audience is correct in this test (might be mismatched) + Scope: []string{"https://www.googleapis.com/auth/devstorage.full_control"}, + RequestedTokenType: "urn:ietf:params:oauth:token-type:access_token", + SubjectToken: "eyJhbGciOiJSUzI1NiIsImtpZCI6IjJjNmZhNmY1OTUwYTdjZTQ2NWZjZjI0N2FhMGIwOTQ4MjhhYzk1MmMiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiIzMjU1NTk0MDU1OS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImF1ZCI6IjMyNTU1OTQwNTU5LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwic3ViIjoiMTEzMzE4NTQxMDA5MDU3Mzc4MzI4IiwiaGQiOiJnb29nbGUuY29tIiwiZW1haWwiOiJpdGh1cmllbEBnb29nbGUuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImF0X2hhc2giOiJyWUtBTjZwX21rS0U4U2ItN3ZGalBBIiwiaWF0IjoxNjAxNTk0NDY1LCJleHAiOjE2MDE1OTgwNjV9.mWOLjD6ghfgrFNcm_1h-wrpLlKFc2WSS13lu2L5t4549uYhX5DEbI7MmeUEwXSffrns1ljcdbJm4nXymXK3AH6ftRV17O3BnOsWngxKj5eKhzOMF308YNXjBKTDiu_crzjCpf_2ng03IIGbFsTvAUx4wvWhnFO-z4xl2tb13OMCxpkw52dO1ZcFhw0d_1iUj_q0UL9E15ADL4SOr-BVtXerWPhNVBplTw8gzL4HHmo2GGUA_ilQpJzD528BKLygemqy1taXZwOGJEAUYkcKm8DhA0NJWneUyqHN6qbs0wm_d_nZsiFx9CIDblt1dUkgfuPIsno-xrkkkwubcv1WlgA", + SubjectTokenType: "urn:ietf:params:oauth:token-type:jwt", +} + +var serverReq = http.Request{ + Method: "POSTURL:/", + URL: nil, + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + Header: map[string][]string{ + "Accept-Encoding": []string{"gzip"}, + "Authorization": []string{"Basic cmJyZ25vZ25yaG9uZ28zYmk0Z2I5Z2hnOWc6bm90c29zZWNyZXQ="}, + "Content-Length": []string{"1192"}, + "Content-Type": []string{"application/x-www-form-urlencoded"}, + "User-Agent": []string{"Go-http-client/1.1"}, + }, + Body: nil, //TODO: Oh god how do I get this... + ContentLength: 1192, + Close: false, + Host: "127.0.0.1:41147", //TODO: Does this conflict due to separate addresses? + Form: nil, //TODO: Should Form, PostForm, TransferEncoding, etc be initialized with Make? + PostForm: nil, + MultipartForm: nil, + Trailer: nil, + RemoteAddr: "127.0.0.1:52760", + RequestURI: "/", + TLS: nil, + Cancel: nil, + Response: nil, + //TODO: I hope to God that I don't need to set ctx for the comparison... +} + +var serverResp = http.Response{ + Status: "200 OK", + StatusCode: 200, + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + Header: map[string][]string{ + "Connection":[]string{"keep-alive"}, + "Content-Length":[]string{"362"}, + "Content-Type":[]string{"application/json; charset=utf-8"}, + "Date":[]string{"Wed, 07 Oct 2020 21:54:27 GMT"}, + "X-Powered-By":[]string{"Express"}, + }, + Body: nil, + ContentLength: 0, + TransferEncoding: nil, + Close: false, + Uncompressed: false, + Trailer: nil, + Request: nil, + TLS: nil, +} + +func TestExchangeToken(t *testing.T) { + + + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if diff := cmp.Diff(*r, serverReq); diff != "" { + t.Errorf("mismatched messages received by mock server (-want +got): \n%s", diff) + } + + return + })) + + headers := make(map[string][]string) + headers["Content-Type"] = []string{"application/x-www-form-urlencoded"} + + //TODO: Call TokenExchange, make sure I get the right results + resp, err := ExchangeToken(ts.URL, &tokenRequest, auth, headers, nil) + if err != nil { + t.Errorf("ExchangeToken failed with error: %s", err) + } +} \ No newline at end of file From 8ea360249f0e99dd4b686aaf32a3fd18cd1a5ee5 Mon Sep 17 00:00:00 2001 From: Patrick Jones Date: Tue, 13 Oct 2020 12:05:31 -0700 Subject: [PATCH 02/18] Tweaked some internal notes --- google/internal/oauth/stsExchange_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/google/internal/oauth/stsExchange_test.go b/google/internal/oauth/stsExchange_test.go index fd0ca1f..31b1b3f 100644 --- a/google/internal/oauth/stsExchange_test.go +++ b/google/internal/oauth/stsExchange_test.go @@ -51,7 +51,7 @@ var serverReq = http.Request{ "Content-Type": []string{"application/x-www-form-urlencoded"}, "User-Agent": []string{"Go-http-client/1.1"}, }, - Body: nil, //TODO: Oh god how do I get this... + Body: nil, //TODO: Construct this struct ContentLength: 1192, Close: false, Host: "127.0.0.1:41147", //TODO: Does this conflict due to separate addresses? @@ -64,7 +64,6 @@ var serverReq = http.Request{ TLS: nil, Cancel: nil, Response: nil, - //TODO: I hope to God that I don't need to set ctx for the comparison... } var serverResp = http.Response{ From 58b935b4ead6cecf021ae6847f8e843055e5836b Mon Sep 17 00:00:00 2001 From: Patrick Jones Date: Mon, 19 Oct 2020 13:43:51 -0700 Subject: [PATCH 03/18] temp --- google/internal/oauth/stsExchange.go | 14 -------------- google/internal/oauth/stsExchange_test.go | 11 +---------- 2 files changed, 1 insertion(+), 24 deletions(-) diff --git a/google/internal/oauth/stsExchange.go b/google/internal/oauth/stsExchange.go index 24684e0..6255dc9 100644 --- a/google/internal/oauth/stsExchange.go +++ b/google/internal/oauth/stsExchange.go @@ -22,20 +22,6 @@ func ExchangeToken(endpoint string, request *STSTokenExchangeRequest, authentica data.Set("subject_token", request.SubjectToken) data.Set("scope", strings.Join(request.Scope, " ")) - //req, err := http.NewRequest("POST", endpoint, strings.NewReader(data.Encode())) - //if err != nil { - // fmt.Errorf("oauth2/google: failed to properly build http request") - //} - //for key, _ := range headers { - // for _, val := range headers.Values(key) { - // req.Header.Add(key, val) - // } - //} - //if authentication.ClientID != "" && authentication.ClientSecret != "" { - // plainHeader := authentication.ClientID + ":" + authentication.ClientSecret - // req.Header.Add("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(plainHeader))) - //} - //req.Header.Add("Content-Length", strconv.Itoa(len(data.Encode()))) authentication.InjectAuthentication(&data, &headers) req, err := http.NewRequest("POST", endpoint, strings.NewReader(data.Encode())) if err != nil { diff --git a/google/internal/oauth/stsExchange_test.go b/google/internal/oauth/stsExchange_test.go index 31b1b3f..99f8959 100644 --- a/google/internal/oauth/stsExchange_test.go +++ b/google/internal/oauth/stsExchange_test.go @@ -1,15 +1,5 @@ package externalaccount -import ( - "net/http" - "net/http/httptest" - "testing" -) - -stsExchange_test.go - -package externalaccount - import ( "github.com/google/go-cmp/cmp" "golang.org/x/oauth2" @@ -97,6 +87,7 @@ func TestExchangeToken(t *testing.T) { if diff := cmp.Diff(*r, serverReq); diff != "" { t.Errorf("mismatched messages received by mock server (-want +got): \n%s", diff) } + if r.URL.String() != return })) From b838bbaf4da88470c26591d72ab39849ca443b23 Mon Sep 17 00:00:00 2001 From: Patrick Jones Date: Mon, 19 Oct 2020 14:35:44 -0700 Subject: [PATCH 04/18] Add testing for ExchangeToken() --- google/internal/oauth/stsExchange_test.go | 103 +++++++++------------- 1 file changed, 40 insertions(+), 63 deletions(-) diff --git a/google/internal/oauth/stsExchange_test.go b/google/internal/oauth/stsExchange_test.go index 99f8959..a8cd4a3 100644 --- a/google/internal/oauth/stsExchange_test.go +++ b/google/internal/oauth/stsExchange_test.go @@ -1,11 +1,12 @@ package externalaccount import ( -"github.com/google/go-cmp/cmp" -"golang.org/x/oauth2" -"net/http" -"net/http/httptest" -"testing" + "github.com/google/go-cmp" + "golang.org/x/oauth2" + "io/ioutil" + "net/http" + "net/http/httptest" + "testing" ) var auth = ClientAuthentication{ @@ -24,80 +25,56 @@ var tokenRequest = STSTokenExchangeRequest{ Audience: "32555940559.apps.googleusercontent.com", //TODO: Make sure audience is correct in this test (might be mismatched) Scope: []string{"https://www.googleapis.com/auth/devstorage.full_control"}, RequestedTokenType: "urn:ietf:params:oauth:token-type:access_token", - SubjectToken: "eyJhbGciOiJSUzI1NiIsImtpZCI6IjJjNmZhNmY1OTUwYTdjZTQ2NWZjZjI0N2FhMGIwOTQ4MjhhYzk1MmMiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiIzMjU1NTk0MDU1OS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImF1ZCI6IjMyNTU1OTQwNTU5LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwic3ViIjoiMTEzMzE4NTQxMDA5MDU3Mzc4MzI4IiwiaGQiOiJnb29nbGUuY29tIiwiZW1haWwiOiJpdGh1cmllbEBnb29nbGUuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImF0X2hhc2giOiJyWUtBTjZwX21rS0U4U2ItN3ZGalBBIiwiaWF0IjoxNjAxNTk0NDY1LCJleHAiOjE2MDE1OTgwNjV9.mWOLjD6ghfgrFNcm_1h-wrpLlKFc2WSS13lu2L5t4549uYhX5DEbI7MmeUEwXSffrns1ljcdbJm4nXymXK3AH6ftRV17O3BnOsWngxKj5eKhzOMF308YNXjBKTDiu_crzjCpf_2ng03IIGbFsTvAUx4wvWhnFO-z4xl2tb13OMCxpkw52dO1ZcFhw0d_1iUj_q0UL9E15ADL4SOr-BVtXerWPhNVBplTw8gzL4HHmo2GGUA_ilQpJzD528BKLygemqy1taXZwOGJEAUYkcKm8DhA0NJWneUyqHN6qbs0wm_d_nZsiFx9CIDblt1dUkgfuPIsno-xrkkkwubcv1WlgA", + SubjectToken: "eyJhbGciOiJSUzI1NiIsImtpIjJjNmZhNmY1OTUwYTdjZTQ2NWZjZjI0N2FhMGIwOTQ4MjhhYzk1MmMiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiIzMjU1NTk0MDU1OS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImF1IjMyNTU1OTQwNTU5LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwic3ViIjoiMTEzMzE4NTQxMDA5MDU3Mzc4MzI4IiwiaGQiOiJnb29nbGUuY29tIiwiZW1haWwiOiJpdGh1cmllbEBnb29nbGUuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImF0X2hhc2giOiJyWUtBTjZwX21rS0U4U2ItN3ZGalBBIiwiaWF0IjoxNjAxNTk0NDY1LCJleHAiOjE2MDE1OTgwNjV9.mWOLjD6ghfgrFNcm_1h-wrpLlKFc2WSS13lu2L5t4549uYhX5DEbI7MmeUEwXSffrns1ljcdbJm4nXymXK3AH6ftRV17O3BnOsWngxKj5eKhzOMF308YNXjBKTDiu_crzjCpf_2ng03IIGbFsTvAUx4wvWhnFO-z4xl2tb13OMCxpkw52dO1ZcFhw0d_1iUj_q0UL9E15ADL4SOr-BVtXerWPhNVBplTw8gzL4HHmo2GGUA_ilQpJzD528BKLygemqy1taXZwOGJEAUYkcKm8DhA0NJWneUyqHN6qbs0wm_d_nZsiFx9CIDblt1dUkgfuPIsno-xrkkkwubcv1WlgA", SubjectTokenType: "urn:ietf:params:oauth:token-type:jwt", } -var serverReq = http.Request{ - Method: "POSTURL:/", - URL: nil, - Proto: "HTTP/1.1", - ProtoMajor: 1, - ProtoMinor: 1, - Header: map[string][]string{ - "Accept-Encoding": []string{"gzip"}, - "Authorization": []string{"Basic cmJyZ25vZ25yaG9uZ28zYmk0Z2I5Z2hnOWc6bm90c29zZWNyZXQ="}, - "Content-Length": []string{"1192"}, - "Content-Type": []string{"application/x-www-form-urlencoded"}, - "User-Agent": []string{"Go-http-client/1.1"}, - }, - Body: nil, //TODO: Construct this struct - ContentLength: 1192, - Close: false, - Host: "127.0.0.1:41147", //TODO: Does this conflict due to separate addresses? - Form: nil, //TODO: Should Form, PostForm, TransferEncoding, etc be initialized with Make? - PostForm: nil, - MultipartForm: nil, - Trailer: nil, - RemoteAddr: "127.0.0.1:52760", - RequestURI: "/", - TLS: nil, - Cancel: nil, - Response: nil, -} - -var serverResp = http.Response{ - Status: "200 OK", - StatusCode: 200, - Proto: "HTTP/1.1", - ProtoMajor: 1, - ProtoMinor: 1, - Header: map[string][]string{ - "Connection":[]string{"keep-alive"}, - "Content-Length":[]string{"362"}, - "Content-Type":[]string{"application/json; charset=utf-8"}, - "Date":[]string{"Wed, 07 Oct 2020 21:54:27 GMT"}, - "X-Powered-By":[]string{"Express"}, - }, - Body: nil, - ContentLength: 0, - TransferEncoding: nil, - Close: false, - Uncompressed: false, - Trailer: nil, - Request: nil, - TLS: nil, +var requestbody = "audience=32555940559.apps.googleusercontent.com&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange&requested_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aaccess_token&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdevstorage.full_control&subject_token=eyJhbGciOiJSUzI1NiIsImtpIjJjNmZhNmY1OTUwYTdjZTQ2NWZjZjI0N2FhMGIwOTQ4MjhhYzk1MmMiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiIzMjU1NTk0MDU1OS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImF1IjMyNTU1OTQwNTU5LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwic3ViIjoiMTEzMzE4NTQxMDA5MDU3Mzc4MzI4IiwiaGQiOiJnb29nbGUuY29tIiwiZW1haWwiOiJpdGh1cmllbEBnb29nbGUuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImF0X2hhc2giOiJyWUtBTjZwX21rS0U4U2ItN3ZGalBBIiwiaWF0IjoxNjAxNTk0NDY1LCJleHAiOjE2MDE1OTgwNjV9.mWOLjD6ghfgrFNcm_1h-wrpLlKFc2WSS13lu2L5t4549uYhX5DEbI7MmeUEwXSffrns1ljcdbJm4nXymXK3AH6ftRV17O3BnOsWngxKj5eKhzOMF308YNXjBKTDiu_crzjCpf_2ng03IIGbFsTvAUx4wvWhnFO-z4xl2tb13OMCxpkw52dO1ZcFhw0d_1iUj_q0UL9E15ADL4SOr-BVtXerWPhNVBplTw8gzL4HHmo2GGUA_ilQpJzD528BKLygemqy1taXZwOGJEAUYkcKm8DhA0NJWneUyqHN6qbs0wm_d_nZsiFx9CIDblt1dUkgfuPIsno-xrkkkwubcv1WlgA&subject_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Ajwt" +var responseBody = `{"access_token":"ya29.a0AfH6SMCqKiKaHEpqs9sl6cocVlqG2dvP2g7eURqGNCfUyKZ6lYSHz531aioS3_0w_xdNDfj6A-Hzk6-0-M6olf3O-zfcGFd678lVuvplpclaK4XQ4ete6_9xSUFU08RefwE53rf2OW8FOQvjpQB-PmPhHZTzK99YYHNtjas","issued_token_type":"urn:ietf:params:oauth:token-type:access_token","token_type":"Bearer","expires_in":3600,"scope":"https://www.googleapis.com/auth/cloud-platform"}` +var expectedToken = STSTokenExchangeResponse{ + AccessToken: "ya29.a0AfH6SMCqKiKaHEpqs9sl6cocVlqG2dvP2g7eURqGNCfUyKZ6lYSHz531aioS3_0w_xdNDfj6A-Hzk6-0-M6olf3O-zfcGFd678lVuvplpclaK4XQ4ete6_9xSUFU08RefwE53rf2OW8FOQvjpQB-PmPhHZTzK99YYHNtjas", + IssuedTokenType: "urn:ietf:params:oauth:token-type:access_token", + TokenType: "Bearer", + ExpiresIn: 3600, + Scope: "https://www.googleapis.com/auth/cloud-platform", + RefreshToken: "", } func TestExchangeToken(t *testing.T) { - - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if diff := cmp.Diff(*r, serverReq); diff != "" { - t.Errorf("mismatched messages received by mock server (-want +got): \n%s", diff) + if r.URL.String() != "/" { + t.Errorf("Unexpected request URL, %v is found.", r.URL) } - if r.URL.String() != - - return + headerAuth := r.Header.Get("Authorization") + if headerAuth != "Basic cmJyZ25vZ25yaG9uZ28zYmk0Z2I5Z2hnOWc6bm90c29zZWNyZXQ=" { + t.Errorf("Unexpected autohrization header, %v is found.", headerAuth) + } + headerContentType := r.Header.Get("Content-Type") + if headerContentType != "application/x-www-form-urlencoded]" { + t.Errorf("Unexpected Content-Type header, %v is found.", headerContentType) + } + body, err := ioutil.ReadAll(r.Body) + if err != nil { + t.Errorf("Failed reading request body: %s.", err) + } + if string(body) != requestbody { + t.Errorf("Unexpected exchange payload, %v is found.", string(body)) + } + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(responseBody)) })) headers := make(map[string][]string) headers["Content-Type"] = []string{"application/x-www-form-urlencoded"} - //TODO: Call TokenExchange, make sure I get the right results resp, err := ExchangeToken(ts.URL, &tokenRequest, auth, headers, nil) if err != nil { t.Errorf("ExchangeToken failed with error: %s", err) } + + if diff := cmp.Diff(resp, expectedToken); diff != "" { + t.Errorf("mismatched messages received by mock server (-want +got): \n%v", diff) + } + } \ No newline at end of file From 7f24b845a5300aef62db38e395ceffb12f6fc871 Mon Sep 17 00:00:00 2001 From: Patrick Jones Date: Mon, 19 Oct 2020 14:39:14 -0700 Subject: [PATCH 05/18] Go fmt --- google/internal/oauth/stsExchange.go | 1 - google/internal/oauth/stsExchange_test.go | 18 +++++++++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/google/internal/oauth/stsExchange.go b/google/internal/oauth/stsExchange.go index 6255dc9..e50975f 100644 --- a/google/internal/oauth/stsExchange.go +++ b/google/internal/oauth/stsExchange.go @@ -34,7 +34,6 @@ func ExchangeToken(endpoint string, request *STSTokenExchangeRequest, authentica } req.Header.Add("Content-Length", strconv.Itoa(len(data.Encode()))) - resp, err := client.Do(req) if err != nil { fmt.Errorf("oauth2/google: invalid response from Secure Token Server: #{err}") diff --git a/google/internal/oauth/stsExchange_test.go b/google/internal/oauth/stsExchange_test.go index a8cd4a3..c5f0935 100644 --- a/google/internal/oauth/stsExchange_test.go +++ b/google/internal/oauth/stsExchange_test.go @@ -10,8 +10,8 @@ import ( ) var auth = ClientAuthentication{ - AuthStyle: oauth2.AuthStyleInHeader, - ClientID: clientID, + AuthStyle: oauth2.AuthStyleInHeader, + ClientID: clientID, ClientSecret: clientSecret, } @@ -22,7 +22,7 @@ var tokenRequest = STSTokenExchangeRequest{ }{}, GrantType: "urn:ietf:params:oauth:grant-type:token-exchange", Resource: "", - Audience: "32555940559.apps.googleusercontent.com", //TODO: Make sure audience is correct in this test (might be mismatched) + Audience: "32555940559.apps.googleusercontent.com", //TODO: Make sure audience is correct in this test (might be mismatched) Scope: []string{"https://www.googleapis.com/auth/devstorage.full_control"}, RequestedTokenType: "urn:ietf:params:oauth:token-type:access_token", SubjectToken: "eyJhbGciOiJSUzI1NiIsImtpIjJjNmZhNmY1OTUwYTdjZTQ2NWZjZjI0N2FhMGIwOTQ4MjhhYzk1MmMiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiIzMjU1NTk0MDU1OS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImF1IjMyNTU1OTQwNTU5LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwic3ViIjoiMTEzMzE4NTQxMDA5MDU3Mzc4MzI4IiwiaGQiOiJnb29nbGUuY29tIiwiZW1haWwiOiJpdGh1cmllbEBnb29nbGUuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImF0X2hhc2giOiJyWUtBTjZwX21rS0U4U2ItN3ZGalBBIiwiaWF0IjoxNjAxNTk0NDY1LCJleHAiOjE2MDE1OTgwNjV9.mWOLjD6ghfgrFNcm_1h-wrpLlKFc2WSS13lu2L5t4549uYhX5DEbI7MmeUEwXSffrns1ljcdbJm4nXymXK3AH6ftRV17O3BnOsWngxKj5eKhzOMF308YNXjBKTDiu_crzjCpf_2ng03IIGbFsTvAUx4wvWhnFO-z4xl2tb13OMCxpkw52dO1ZcFhw0d_1iUj_q0UL9E15ADL4SOr-BVtXerWPhNVBplTw8gzL4HHmo2GGUA_ilQpJzD528BKLygemqy1taXZwOGJEAUYkcKm8DhA0NJWneUyqHN6qbs0wm_d_nZsiFx9CIDblt1dUkgfuPIsno-xrkkkwubcv1WlgA", @@ -32,12 +32,12 @@ var tokenRequest = STSTokenExchangeRequest{ var requestbody = "audience=32555940559.apps.googleusercontent.com&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange&requested_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aaccess_token&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdevstorage.full_control&subject_token=eyJhbGciOiJSUzI1NiIsImtpIjJjNmZhNmY1OTUwYTdjZTQ2NWZjZjI0N2FhMGIwOTQ4MjhhYzk1MmMiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiIzMjU1NTk0MDU1OS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImF1IjMyNTU1OTQwNTU5LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwic3ViIjoiMTEzMzE4NTQxMDA5MDU3Mzc4MzI4IiwiaGQiOiJnb29nbGUuY29tIiwiZW1haWwiOiJpdGh1cmllbEBnb29nbGUuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImF0X2hhc2giOiJyWUtBTjZwX21rS0U4U2ItN3ZGalBBIiwiaWF0IjoxNjAxNTk0NDY1LCJleHAiOjE2MDE1OTgwNjV9.mWOLjD6ghfgrFNcm_1h-wrpLlKFc2WSS13lu2L5t4549uYhX5DEbI7MmeUEwXSffrns1ljcdbJm4nXymXK3AH6ftRV17O3BnOsWngxKj5eKhzOMF308YNXjBKTDiu_crzjCpf_2ng03IIGbFsTvAUx4wvWhnFO-z4xl2tb13OMCxpkw52dO1ZcFhw0d_1iUj_q0UL9E15ADL4SOr-BVtXerWPhNVBplTw8gzL4HHmo2GGUA_ilQpJzD528BKLygemqy1taXZwOGJEAUYkcKm8DhA0NJWneUyqHN6qbs0wm_d_nZsiFx9CIDblt1dUkgfuPIsno-xrkkkwubcv1WlgA&subject_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Ajwt" var responseBody = `{"access_token":"ya29.a0AfH6SMCqKiKaHEpqs9sl6cocVlqG2dvP2g7eURqGNCfUyKZ6lYSHz531aioS3_0w_xdNDfj6A-Hzk6-0-M6olf3O-zfcGFd678lVuvplpclaK4XQ4ete6_9xSUFU08RefwE53rf2OW8FOQvjpQB-PmPhHZTzK99YYHNtjas","issued_token_type":"urn:ietf:params:oauth:token-type:access_token","token_type":"Bearer","expires_in":3600,"scope":"https://www.googleapis.com/auth/cloud-platform"}` var expectedToken = STSTokenExchangeResponse{ - AccessToken: "ya29.a0AfH6SMCqKiKaHEpqs9sl6cocVlqG2dvP2g7eURqGNCfUyKZ6lYSHz531aioS3_0w_xdNDfj6A-Hzk6-0-M6olf3O-zfcGFd678lVuvplpclaK4XQ4ete6_9xSUFU08RefwE53rf2OW8FOQvjpQB-PmPhHZTzK99YYHNtjas", + AccessToken: "ya29.a0AfH6SMCqKiKaHEpqs9sl6cocVlqG2dvP2g7eURqGNCfUyKZ6lYSHz531aioS3_0w_xdNDfj6A-Hzk6-0-M6olf3O-zfcGFd678lVuvplpclaK4XQ4ete6_9xSUFU08RefwE53rf2OW8FOQvjpQB-PmPhHZTzK99YYHNtjas", IssuedTokenType: "urn:ietf:params:oauth:token-type:access_token", - TokenType: "Bearer", - ExpiresIn: 3600, - Scope: "https://www.googleapis.com/auth/cloud-platform", - RefreshToken: "", + TokenType: "Bearer", + ExpiresIn: 3600, + Scope: "https://www.googleapis.com/auth/cloud-platform", + RefreshToken: "", } func TestExchangeToken(t *testing.T) { @@ -77,4 +77,4 @@ func TestExchangeToken(t *testing.T) { t.Errorf("mismatched messages received by mock server (-want +got): \n%v", diff) } -} \ No newline at end of file +} From ddbe2ba342c8cc24fe412497e3c751fb6a433783 Mon Sep 17 00:00:00 2001 From: Patrick Jones Date: Mon, 19 Oct 2020 14:49:46 -0700 Subject: [PATCH 06/18] Fixed file names. --- google/internal/oauth/{stsExchange.go => sts_exchange.go} | 0 .../internal/oauth/{stsExchange_test.go => sts_exchange_test.go} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename google/internal/oauth/{stsExchange.go => sts_exchange.go} (100%) rename google/internal/oauth/{stsExchange_test.go => sts_exchange_test.go} (100%) diff --git a/google/internal/oauth/stsExchange.go b/google/internal/oauth/sts_exchange.go similarity index 100% rename from google/internal/oauth/stsExchange.go rename to google/internal/oauth/sts_exchange.go diff --git a/google/internal/oauth/stsExchange_test.go b/google/internal/oauth/sts_exchange_test.go similarity index 100% rename from google/internal/oauth/stsExchange_test.go rename to google/internal/oauth/sts_exchange_test.go From c19137f314defadf808ed3531bd03d592e710a5c Mon Sep 17 00:00:00 2001 From: Patrick Jones Date: Mon, 19 Oct 2020 14:53:45 -0700 Subject: [PATCH 07/18] Fixed directory structure. --- google/internal/{oauth => externalaccount}/sts_exchange.go | 0 google/internal/{oauth => externalaccount}/sts_exchange_test.go | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename google/internal/{oauth => externalaccount}/sts_exchange.go (100%) rename google/internal/{oauth => externalaccount}/sts_exchange_test.go (100%) diff --git a/google/internal/oauth/sts_exchange.go b/google/internal/externalaccount/sts_exchange.go similarity index 100% rename from google/internal/oauth/sts_exchange.go rename to google/internal/externalaccount/sts_exchange.go diff --git a/google/internal/oauth/sts_exchange_test.go b/google/internal/externalaccount/sts_exchange_test.go similarity index 100% rename from google/internal/oauth/sts_exchange_test.go rename to google/internal/externalaccount/sts_exchange_test.go From 13049afc57df7402ad4526a79f7ec249a939ced3 Mon Sep 17 00:00:00 2001 From: Patrick Jones Date: Mon, 19 Oct 2020 15:17:49 -0700 Subject: [PATCH 08/18] Fixed import issues and test validity. --- google/internal/externalaccount/sts_exchange.go | 2 +- .../internal/externalaccount/sts_exchange_test.go | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/google/internal/externalaccount/sts_exchange.go b/google/internal/externalaccount/sts_exchange.go index e50975f..2e20682 100644 --- a/google/internal/externalaccount/sts_exchange.go +++ b/google/internal/externalaccount/sts_exchange.go @@ -22,7 +22,7 @@ func ExchangeToken(endpoint string, request *STSTokenExchangeRequest, authentica data.Set("subject_token", request.SubjectToken) data.Set("scope", strings.Join(request.Scope, " ")) - authentication.InjectAuthentication(&data, &headers) + authentication.InjectAuthentication(data, headers) req, err := http.NewRequest("POST", endpoint, strings.NewReader(data.Encode())) if err != nil { fmt.Errorf("oauth2/google: failed to properly build http request") diff --git a/google/internal/externalaccount/sts_exchange_test.go b/google/internal/externalaccount/sts_exchange_test.go index c5f0935..82ee59a 100644 --- a/google/internal/externalaccount/sts_exchange_test.go +++ b/google/internal/externalaccount/sts_exchange_test.go @@ -1,7 +1,7 @@ package externalaccount import ( - "github.com/google/go-cmp" + "github.com/google/go-cmp/cmp" "golang.org/x/oauth2" "io/ioutil" "net/http" @@ -25,14 +25,14 @@ var tokenRequest = STSTokenExchangeRequest{ Audience: "32555940559.apps.googleusercontent.com", //TODO: Make sure audience is correct in this test (might be mismatched) Scope: []string{"https://www.googleapis.com/auth/devstorage.full_control"}, RequestedTokenType: "urn:ietf:params:oauth:token-type:access_token", - SubjectToken: "eyJhbGciOiJSUzI1NiIsImtpIjJjNmZhNmY1OTUwYTdjZTQ2NWZjZjI0N2FhMGIwOTQ4MjhhYzk1MmMiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiIzMjU1NTk0MDU1OS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImF1IjMyNTU1OTQwNTU5LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwic3ViIjoiMTEzMzE4NTQxMDA5MDU3Mzc4MzI4IiwiaGQiOiJnb29nbGUuY29tIiwiZW1haWwiOiJpdGh1cmllbEBnb29nbGUuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImF0X2hhc2giOiJyWUtBTjZwX21rS0U4U2ItN3ZGalBBIiwiaWF0IjoxNjAxNTk0NDY1LCJleHAiOjE2MDE1OTgwNjV9.mWOLjD6ghfgrFNcm_1h-wrpLlKFc2WSS13lu2L5t4549uYhX5DEbI7MmeUEwXSffrns1ljcdbJm4nXymXK3AH6ftRV17O3BnOsWngxKj5eKhzOMF308YNXjBKTDiu_crzjCpf_2ng03IIGbFsTvAUx4wvWhnFO-z4xl2tb13OMCxpkw52dO1ZcFhw0d_1iUj_q0UL9E15ADL4SOr-BVtXerWPhNVBplTw8gzL4HHmo2GGUA_ilQpJzD528BKLygemqy1taXZwOGJEAUYkcKm8DhA0NJWneUyqHN6qbs0wm_d_nZsiFx9CIDblt1dUkgfuPIsno-xrkkkwubcv1WlgA", + SubjectToken: "Sample.Subject.Token", SubjectTokenType: "urn:ietf:params:oauth:token-type:jwt", } -var requestbody = "audience=32555940559.apps.googleusercontent.com&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange&requested_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aaccess_token&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdevstorage.full_control&subject_token=eyJhbGciOiJSUzI1NiIsImtpIjJjNmZhNmY1OTUwYTdjZTQ2NWZjZjI0N2FhMGIwOTQ4MjhhYzk1MmMiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiIzMjU1NTk0MDU1OS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImF1IjMyNTU1OTQwNTU5LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwic3ViIjoiMTEzMzE4NTQxMDA5MDU3Mzc4MzI4IiwiaGQiOiJnb29nbGUuY29tIiwiZW1haWwiOiJpdGh1cmllbEBnb29nbGUuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImF0X2hhc2giOiJyWUtBTjZwX21rS0U4U2ItN3ZGalBBIiwiaWF0IjoxNjAxNTk0NDY1LCJleHAiOjE2MDE1OTgwNjV9.mWOLjD6ghfgrFNcm_1h-wrpLlKFc2WSS13lu2L5t4549uYhX5DEbI7MmeUEwXSffrns1ljcdbJm4nXymXK3AH6ftRV17O3BnOsWngxKj5eKhzOMF308YNXjBKTDiu_crzjCpf_2ng03IIGbFsTvAUx4wvWhnFO-z4xl2tb13OMCxpkw52dO1ZcFhw0d_1iUj_q0UL9E15ADL4SOr-BVtXerWPhNVBplTw8gzL4HHmo2GGUA_ilQpJzD528BKLygemqy1taXZwOGJEAUYkcKm8DhA0NJWneUyqHN6qbs0wm_d_nZsiFx9CIDblt1dUkgfuPIsno-xrkkkwubcv1WlgA&subject_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Ajwt" -var responseBody = `{"access_token":"ya29.a0AfH6SMCqKiKaHEpqs9sl6cocVlqG2dvP2g7eURqGNCfUyKZ6lYSHz531aioS3_0w_xdNDfj6A-Hzk6-0-M6olf3O-zfcGFd678lVuvplpclaK4XQ4ete6_9xSUFU08RefwE53rf2OW8FOQvjpQB-PmPhHZTzK99YYHNtjas","issued_token_type":"urn:ietf:params:oauth:token-type:access_token","token_type":"Bearer","expires_in":3600,"scope":"https://www.googleapis.com/auth/cloud-platform"}` +var requestbody = "audience=32555940559.apps.googleusercontent.com&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange&requested_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aaccess_token&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdevstorage.full_control&subject_token=Sample.Subject.Token&subject_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Ajwt" +var responseBody = `{"access_token":"Sample.Access.Token","issued_token_type":"urn:ietf:params:oauth:token-type:access_token","token_type":"Bearer","expires_in":3600,"scope":"https://www.googleapis.com/auth/cloud-platform"}` var expectedToken = STSTokenExchangeResponse{ - AccessToken: "ya29.a0AfH6SMCqKiKaHEpqs9sl6cocVlqG2dvP2g7eURqGNCfUyKZ6lYSHz531aioS3_0w_xdNDfj6A-Hzk6-0-M6olf3O-zfcGFd678lVuvplpclaK4XQ4ete6_9xSUFU08RefwE53rf2OW8FOQvjpQB-PmPhHZTzK99YYHNtjas", + AccessToken: "Sample.Access.Token", IssuedTokenType: "urn:ietf:params:oauth:token-type:access_token", TokenType: "Bearer", ExpiresIn: 3600, @@ -51,7 +51,7 @@ func TestExchangeToken(t *testing.T) { t.Errorf("Unexpected autohrization header, %v is found.", headerAuth) } headerContentType := r.Header.Get("Content-Type") - if headerContentType != "application/x-www-form-urlencoded]" { + if headerContentType != "application/x-www-form-urlencoded" { t.Errorf("Unexpected Content-Type header, %v is found.", headerContentType) } body, err := ioutil.ReadAll(r.Body) @@ -73,7 +73,7 @@ func TestExchangeToken(t *testing.T) { t.Errorf("ExchangeToken failed with error: %s", err) } - if diff := cmp.Diff(resp, expectedToken); diff != "" { + if diff := cmp.Diff(expectedToken, *resp); diff != "" { t.Errorf("mismatched messages received by mock server (-want +got): \n%v", diff) } From ed1c7b88580aaffc499d0bae0c6f0281109eb0ce Mon Sep 17 00:00:00 2001 From: Patrick Jones Date: Thu, 29 Oct 2020 12:28:45 -0700 Subject: [PATCH 09/18] google: add ExchangeToken unit tests --- google/internal/externalaccount/sts_exchange_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/google/internal/externalaccount/sts_exchange_test.go b/google/internal/externalaccount/sts_exchange_test.go index 82ee59a..451b86a 100644 --- a/google/internal/externalaccount/sts_exchange_test.go +++ b/google/internal/externalaccount/sts_exchange_test.go @@ -78,3 +78,11 @@ func TestExchangeToken(t *testing.T) { } } + +func TestExchangeToken_Err(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.Write([]byte("what's wrong with this response?")) + })) + +} \ No newline at end of file From 717b310560a07d5c00e61dd0877a34f3debb7981 Mon Sep 17 00:00:00 2001 From: Patrick Jones Date: Tue, 3 Nov 2020 13:44:17 -0800 Subject: [PATCH 10/18] google: fix error formatting, add ctx argument Change-Id: If664a760005cc209c7398f2efce5ff324ebc4746 --- .../internal/externalaccount/sts_exchange.go | 32 +++++++++++++------ .../externalaccount/sts_exchange_test.go | 15 +++++++-- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/google/internal/externalaccount/sts_exchange.go b/google/internal/externalaccount/sts_exchange.go index 2e20682..ccff3ad 100644 --- a/google/internal/externalaccount/sts_exchange.go +++ b/google/internal/externalaccount/sts_exchange.go @@ -1,8 +1,15 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package externalaccount import ( + "context" "encoding/json" "fmt" + "golang.org/x/oauth2" + "io" "io/ioutil" "net/http" "net/url" @@ -10,9 +17,9 @@ import ( "strings" ) -func ExchangeToken(endpoint string, request *STSTokenExchangeRequest, authentication ClientAuthentication, headers http.Header, options map[string]interface{}) (*STSTokenExchangeResponse, error) { +func ExchangeToken(ctx context.Context, endpoint string, request *STSTokenExchangeRequest, authentication ClientAuthentication, headers http.Header, options map[string]interface{}) (*STSTokenExchangeResponse, error) { - client := &http.Client{} + client := oauth2.NewClient(ctx, nil) data := url.Values{} data.Set("audience", request.Audience) @@ -21,32 +28,39 @@ func ExchangeToken(endpoint string, request *STSTokenExchangeRequest, authentica data.Set("subject_token_type", request.SubjectTokenType) data.Set("subject_token", request.SubjectToken) data.Set("scope", strings.Join(request.Scope, " ")) + opts, err := json.Marshal(options) + if err == nil { + data.Set("options", string(opts)) + } else { + fmt.Errorf("oauth2/google: failed to marshal additional options: %v", err) + } authentication.InjectAuthentication(data, headers) - req, err := http.NewRequest("POST", endpoint, strings.NewReader(data.Encode())) + encodedData := data.Encode() + req, err := http.NewRequest("POST", endpoint, strings.NewReader(encodedData)) if err != nil { - fmt.Errorf("oauth2/google: failed to properly build http request") + fmt.Errorf("oauth2/google: failed to properly build http request: %v", err) } for key, _ := range headers { for _, val := range headers.Values(key) { req.Header.Add(key, val) } } - req.Header.Add("Content-Length", strconv.Itoa(len(data.Encode()))) + req.Header.Add("Content-Length", strconv.Itoa(len(encodedData))) resp, err := client.Do(req) if err != nil { - fmt.Errorf("oauth2/google: invalid response from Secure Token Server: #{err}") + fmt.Errorf("oauth2/google: invalid response from Secure Token Server: %v", err) } defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) + body, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1<<20)) if err != nil { - fmt.Errorf("oauth2/google: invalid body in Secute Token Server response: #{err}") + fmt.Errorf("oauth2/google: invalid body in Secure Token Server response: %v", err) } var stsResp STSTokenExchangeResponse err = json.Unmarshal(body, &stsResp) if err != nil { - fmt.Println(err) + fmt.Errorf("oauth2/google: failed to unmarshal response body from STS server: %v", err) } return &stsResp, nil } diff --git a/google/internal/externalaccount/sts_exchange_test.go b/google/internal/externalaccount/sts_exchange_test.go index 451b86a..ca409dc 100644 --- a/google/internal/externalaccount/sts_exchange_test.go +++ b/google/internal/externalaccount/sts_exchange_test.go @@ -1,6 +1,11 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package externalaccount import ( + "context" "github.com/google/go-cmp/cmp" "golang.org/x/oauth2" "io/ioutil" @@ -68,7 +73,7 @@ func TestExchangeToken(t *testing.T) { headers := make(map[string][]string) headers["Content-Type"] = []string{"application/x-www-form-urlencoded"} - resp, err := ExchangeToken(ts.URL, &tokenRequest, auth, headers, nil) + resp, err := ExchangeToken(context.Background(), ts.URL, &tokenRequest, auth, headers, nil) if err != nil { t.Errorf("ExchangeToken failed with error: %s", err) } @@ -84,5 +89,11 @@ func TestExchangeToken_Err(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.Write([]byte("what's wrong with this response?")) })) - + + headers := make(map[string][]string) + headers["Content-Type"] = []string{"application/x-www-form-urlencoded"} + _, err := ExchangeToken(context.Background(), ts.URL, &tokenRequest, auth, headers, nil) + if err == nil { + t.Errorf("Expected handled error; instead got nil.") + } } \ No newline at end of file From 4bcf502a22c863917c4285ba47742bc796dc30bf Mon Sep 17 00:00:00 2001 From: Patrick Jones Date: Tue, 3 Nov 2020 15:37:42 -0800 Subject: [PATCH 11/18] google: fix more nits and return early in one error case. Change-Id: Idafbe36f4add6998e57878fec84a1080ac962511 --- google/internal/externalaccount/sts_exchange.go | 12 ++++++------ google/internal/externalaccount/sts_exchange_test.go | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/google/internal/externalaccount/sts_exchange.go b/google/internal/externalaccount/sts_exchange.go index ccff3ad..099e4b3 100644 --- a/google/internal/externalaccount/sts_exchange.go +++ b/google/internal/externalaccount/sts_exchange.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Go Authors. All rights reserved. +// Copyright 2020 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -29,15 +29,15 @@ func ExchangeToken(ctx context.Context, endpoint string, request *STSTokenExchan data.Set("subject_token", request.SubjectToken) data.Set("scope", strings.Join(request.Scope, " ")) opts, err := json.Marshal(options) - if err == nil { - data.Set("options", string(opts)) - } else { + if err != nil { fmt.Errorf("oauth2/google: failed to marshal additional options: %v", err) + return nil, err } + data.Set("options", string(opts)) authentication.InjectAuthentication(data, headers) encodedData := data.Encode() - req, err := http.NewRequest("POST", endpoint, strings.NewReader(encodedData)) + req, err := http.NewRequestWithContext(ctx, "POST", endpoint, strings.NewReader(encodedData)) if err != nil { fmt.Errorf("oauth2/google: failed to properly build http request: %v", err) } @@ -60,7 +60,7 @@ func ExchangeToken(ctx context.Context, endpoint string, request *STSTokenExchan var stsResp STSTokenExchangeResponse err = json.Unmarshal(body, &stsResp) if err != nil { - fmt.Errorf("oauth2/google: failed to unmarshal response body from STS server: %v", err) + fmt.Errorf("oauth2/google: failed to unmarshal response body from Secure Token Server: %v", err) } return &stsResp, nil } diff --git a/google/internal/externalaccount/sts_exchange_test.go b/google/internal/externalaccount/sts_exchange_test.go index ca409dc..703a96e 100644 --- a/google/internal/externalaccount/sts_exchange_test.go +++ b/google/internal/externalaccount/sts_exchange_test.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Go Authors. All rights reserved. +// Copyright 2020 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -75,7 +75,7 @@ func TestExchangeToken(t *testing.T) { resp, err := ExchangeToken(context.Background(), ts.URL, &tokenRequest, auth, headers, nil) if err != nil { - t.Errorf("ExchangeToken failed with error: %s", err) + t.Fatalf("ExchangeToken failed with error: %s", err) } if diff := cmp.Diff(expectedToken, *resp); diff != "" { From 74f89b5575991825bbd14186fc23171be71ffa25 Mon Sep 17 00:00:00 2001 From: Patrick Jones Date: Tue, 10 Nov 2020 11:59:17 -0800 Subject: [PATCH 12/18] google: update formatting and code structure Change-Id: I9c45c7848de4ab2e3a685f761c85b1c9405b6eb1 --- .../internal/externalaccount/sts_exchange.go | 27 +++++++++++-------- .../externalaccount/sts_exchange_test.go | 27 ++++++++++--------- 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/google/internal/externalaccount/sts_exchange.go b/google/internal/externalaccount/sts_exchange.go index 099e4b3..11548f0 100644 --- a/google/internal/externalaccount/sts_exchange.go +++ b/google/internal/externalaccount/sts_exchange.go @@ -10,7 +10,6 @@ import ( "fmt" "golang.org/x/oauth2" "io" - "io/ioutil" "net/http" "net/url" "strconv" @@ -33,35 +32,41 @@ func ExchangeToken(ctx context.Context, endpoint string, request *STSTokenExchan fmt.Errorf("oauth2/google: failed to marshal additional options: %v", err) return nil, err } - data.Set("options", string(opts)) + data.Set("options", string(opts)) authentication.InjectAuthentication(data, headers) encodedData := data.Encode() + req, err := http.NewRequestWithContext(ctx, "POST", endpoint, strings.NewReader(encodedData)) if err != nil { fmt.Errorf("oauth2/google: failed to properly build http request: %v", err) + return nil, err } - for key, _ := range headers { - for _, val := range headers.Values(key) { + for key, list := range headers { + for _, val := range list { req.Header.Add(key, val) } } req.Header.Add("Content-Length", strconv.Itoa(len(encodedData))) resp, err := client.Do(req) + if err != nil { fmt.Errorf("oauth2/google: invalid response from Secure Token Server: %v", err) + return nil, err } defer resp.Body.Close() - body, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1<<20)) - if err != nil { - fmt.Errorf("oauth2/google: invalid body in Secure Token Server response: %v", err) - } + + bodyJson := json.NewDecoder(io.LimitReader(resp.Body, 1<<20)) var stsResp STSTokenExchangeResponse - err = json.Unmarshal(body, &stsResp) - if err != nil { - fmt.Errorf("oauth2/google: failed to unmarshal response body from Secure Token Server: %v", err) + for bodyJson.More() { + err = bodyJson.Decode(&stsResp) + if err != nil { + fmt.Errorf("oauth2/google: failed to unmarshal response body from Secure Token Server: %v", err) + return nil, err + } } + return &stsResp, nil } diff --git a/google/internal/externalaccount/sts_exchange_test.go b/google/internal/externalaccount/sts_exchange_test.go index 703a96e..496bba6 100644 --- a/google/internal/externalaccount/sts_exchange_test.go +++ b/google/internal/externalaccount/sts_exchange_test.go @@ -34,7 +34,7 @@ var tokenRequest = STSTokenExchangeRequest{ SubjectTokenType: "urn:ietf:params:oauth:token-type:jwt", } -var requestbody = "audience=32555940559.apps.googleusercontent.com&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange&requested_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aaccess_token&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdevstorage.full_control&subject_token=Sample.Subject.Token&subject_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Ajwt" +var requestbody = "audience=32555940559.apps.googleusercontent.com&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange&options=null&requested_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aaccess_token&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdevstorage.full_control&subject_token=Sample.Subject.Token&subject_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Ajwt" var responseBody = `{"access_token":"Sample.Access.Token","issued_token_type":"urn:ietf:params:oauth:token-type:access_token","token_type":"Bearer","expires_in":3600,"scope":"https://www.googleapis.com/auth/cloud-platform"}` var expectedToken = STSTokenExchangeResponse{ AccessToken: "Sample.Access.Token", @@ -48,34 +48,37 @@ var expectedToken = STSTokenExchangeResponse{ func TestExchangeToken(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + t.Errorf("Unexpected request method, %v is found", r.Method) + } if r.URL.String() != "/" { - t.Errorf("Unexpected request URL, %v is found.", r.URL) + t.Errorf("Unexpected request URL, %v is found", r.URL) } headerAuth := r.Header.Get("Authorization") if headerAuth != "Basic cmJyZ25vZ25yaG9uZ28zYmk0Z2I5Z2hnOWc6bm90c29zZWNyZXQ=" { - t.Errorf("Unexpected autohrization header, %v is found.", headerAuth) + t.Errorf("Unexpected autohrization header, %v is found", headerAuth) } headerContentType := r.Header.Get("Content-Type") if headerContentType != "application/x-www-form-urlencoded" { - t.Errorf("Unexpected Content-Type header, %v is found.", headerContentType) + t.Errorf("Unexpected Content-Type header, %v is found", headerContentType) } body, err := ioutil.ReadAll(r.Body) if err != nil { - t.Errorf("Failed reading request body: %s.", err) + t.Errorf("Failed reading request body: %v.", err) } if string(body) != requestbody { - t.Errorf("Unexpected exchange payload, %v is found.", string(body)) + t.Errorf("Unexpected exchange payload, %v is found", string(body)) } w.Header().Set("Content-Type", "application/json") w.Write([]byte(responseBody)) })) - headers := make(map[string][]string) - headers["Content-Type"] = []string{"application/x-www-form-urlencoded"} + headers := http.Header{} + headers.Add("Content-Type", "application/x-www-form-urlencoded") resp, err := ExchangeToken(context.Background(), ts.URL, &tokenRequest, auth, headers, nil) if err != nil { - t.Fatalf("ExchangeToken failed with error: %s", err) + t.Fatalf("ExchangeToken failed with error: %v", err) } if diff := cmp.Diff(expectedToken, *resp); diff != "" { @@ -90,10 +93,10 @@ func TestExchangeToken_Err(t *testing.T) { w.Write([]byte("what's wrong with this response?")) })) - headers := make(map[string][]string) - headers["Content-Type"] = []string{"application/x-www-form-urlencoded"} + headers := http.Header{} + headers.Add("Content-Type", "application/x-www-form-urlencoded") _, err := ExchangeToken(context.Background(), ts.URL, &tokenRequest, auth, headers, nil) if err == nil { t.Errorf("Expected handled error; instead got nil.") } -} \ No newline at end of file +} From 87e4e25dedc83f578f1d16acb14ec7ea19acc800 Mon Sep 17 00:00:00 2001 From: Patrick Jones Date: Tue, 10 Nov 2020 13:12:37 -0800 Subject: [PATCH 13/18] google: update error return format. Change-Id: Id2cd28484e3724eee98757c2f40cbb237dee2c75 --- google/internal/externalaccount/sts_exchange.go | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/google/internal/externalaccount/sts_exchange.go b/google/internal/externalaccount/sts_exchange.go index 11548f0..c2f0626 100644 --- a/google/internal/externalaccount/sts_exchange.go +++ b/google/internal/externalaccount/sts_exchange.go @@ -29,8 +29,7 @@ func ExchangeToken(ctx context.Context, endpoint string, request *STSTokenExchan data.Set("scope", strings.Join(request.Scope, " ")) opts, err := json.Marshal(options) if err != nil { - fmt.Errorf("oauth2/google: failed to marshal additional options: %v", err) - return nil, err + return nil, fmt.Errorf("oauth2/google: failed to marshal additional options: %v", err) } data.Set("options", string(opts)) @@ -39,8 +38,8 @@ func ExchangeToken(ctx context.Context, endpoint string, request *STSTokenExchan req, err := http.NewRequestWithContext(ctx, "POST", endpoint, strings.NewReader(encodedData)) if err != nil { - fmt.Errorf("oauth2/google: failed to properly build http request: %v", err) - return nil, err + return nil, fmt.Errorf("oauth2/google: failed to properly build http request: %v", err) + } for key, list := range headers { for _, val := range list { @@ -52,8 +51,7 @@ func ExchangeToken(ctx context.Context, endpoint string, request *STSTokenExchan resp, err := client.Do(req) if err != nil { - fmt.Errorf("oauth2/google: invalid response from Secure Token Server: %v", err) - return nil, err + return nil, fmt.Errorf("oauth2/google: invalid response from Secure Token Server: %v", err) } defer resp.Body.Close() @@ -62,8 +60,8 @@ func ExchangeToken(ctx context.Context, endpoint string, request *STSTokenExchan for bodyJson.More() { err = bodyJson.Decode(&stsResp) if err != nil { - fmt.Errorf("oauth2/google: failed to unmarshal response body from Secure Token Server: %v", err) - return nil, err + return nil, fmt.Errorf("oauth2/google: failed to unmarshal response body from Secure Token Server: %v", err) + } } From 929f793807be2b042e4330fb3f953a1f7a32c5c6 Mon Sep 17 00:00:00 2001 From: Patrick Jones Date: Thu, 12 Nov 2020 11:43:19 -0800 Subject: [PATCH 14/18] google: change json decoding layout Change-Id: Idc322fc66cb93e5d61f0c41d5847a2567a5e1019 --- google/internal/externalaccount/sts_exchange.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/google/internal/externalaccount/sts_exchange.go b/google/internal/externalaccount/sts_exchange.go index c2f0626..1bcd661 100644 --- a/google/internal/externalaccount/sts_exchange.go +++ b/google/internal/externalaccount/sts_exchange.go @@ -31,8 +31,8 @@ func ExchangeToken(ctx context.Context, endpoint string, request *STSTokenExchan if err != nil { return nil, fmt.Errorf("oauth2/google: failed to marshal additional options: %v", err) } - data.Set("options", string(opts)) + authentication.InjectAuthentication(data, headers) encodedData := data.Encode() @@ -57,14 +57,13 @@ func ExchangeToken(ctx context.Context, endpoint string, request *STSTokenExchan bodyJson := json.NewDecoder(io.LimitReader(resp.Body, 1<<20)) var stsResp STSTokenExchangeResponse - for bodyJson.More() { - err = bodyJson.Decode(&stsResp) - if err != nil { - return nil, fmt.Errorf("oauth2/google: failed to unmarshal response body from Secure Token Server: %v", err) + err = bodyJson.Decode(&stsResp) + if err != nil { + return nil, fmt.Errorf("oauth2/google: failed to unmarshal response body from Secure Token Server: %v", err) - } } + return &stsResp, nil } From 0dc8cd78200b28e589c2ab198c271db10c2ae01a Mon Sep 17 00:00:00 2001 From: Patrick Jones Date: Thu, 12 Nov 2020 14:21:10 -0800 Subject: [PATCH 15/18] google: remove remaining cmp.Diff call Change-Id: I189302ad4f49a206dd298b767a827b0263df8c43 --- google/internal/externalaccount/sts_exchange.go | 1 - google/internal/externalaccount/sts_exchange_test.go | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/google/internal/externalaccount/sts_exchange.go b/google/internal/externalaccount/sts_exchange.go index 1bcd661..7571a0b 100644 --- a/google/internal/externalaccount/sts_exchange.go +++ b/google/internal/externalaccount/sts_exchange.go @@ -63,7 +63,6 @@ func ExchangeToken(ctx context.Context, endpoint string, request *STSTokenExchan } - return &stsResp, nil } diff --git a/google/internal/externalaccount/sts_exchange_test.go b/google/internal/externalaccount/sts_exchange_test.go index 496bba6..1cd05e7 100644 --- a/google/internal/externalaccount/sts_exchange_test.go +++ b/google/internal/externalaccount/sts_exchange_test.go @@ -81,8 +81,8 @@ func TestExchangeToken(t *testing.T) { t.Fatalf("ExchangeToken failed with error: %v", err) } - if diff := cmp.Diff(expectedToken, *resp); diff != "" { - t.Errorf("mismatched messages received by mock server (-want +got): \n%v", diff) + if expectedToken != *resp { + t.Errorf("mismatched messages received by mock server. \nWant: \n%v\n\nGot:\n%v", expectedToken, *resp) } } From 098ad88d0215ea548036945a5853fa92abf7f62d Mon Sep 17 00:00:00 2001 From: Patrick Jones Date: Mon, 16 Nov 2020 16:25:35 -0800 Subject: [PATCH 16/18] google: add test to ensure optional field works as intended. Change-Id: I00cb6cf668073883c96ac0144847fa2693414044 --- .../externalaccount/sts_exchange_test.go | 82 ++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/google/internal/externalaccount/sts_exchange_test.go b/google/internal/externalaccount/sts_exchange_test.go index 1cd05e7..1846b02 100644 --- a/google/internal/externalaccount/sts_exchange_test.go +++ b/google/internal/externalaccount/sts_exchange_test.go @@ -6,11 +6,12 @@ package externalaccount import ( "context" - "github.com/google/go-cmp/cmp" + "encoding/json" "golang.org/x/oauth2" "io/ioutil" "net/http" "net/http/httptest" + "net/url" "testing" ) @@ -85,6 +86,8 @@ func TestExchangeToken(t *testing.T) { t.Errorf("mismatched messages received by mock server. \nWant: \n%v\n\nGot:\n%v", expectedToken, *resp) } + + } func TestExchangeToken_Err(t *testing.T) { @@ -100,3 +103,80 @@ func TestExchangeToken_Err(t *testing.T) { t.Errorf("Expected handled error; instead got nil.") } } +/* Lean test specifically for options, as the other features are tested earlier. */ +type testOpts struct { + First string `json:"first"` + Second string `json:"second"` +} +var optsValues = [][]string{{"foo", "bar"},{"cat", "pan"}} +func TestExchangeToken_Opts(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + body, err := ioutil.ReadAll(r.Body) + if err != nil { + t.Errorf("Failed reading request body: %v.", err) + } + data, err := url.ParseQuery(string(body)) + if err != nil { + t.Fatalf("Failed to parse request body: %v", err) + } + strOpts, ok := data["options"] + if !ok { + t.Errorf("Server didn't recieve an \"options\" field.") + } else if len(strOpts) < 1 { + t.Errorf("\"options\" field has length 0.") + } + var opts map[string]interface{} + err = json.Unmarshal([]byte(strOpts[0]), &opts) + if len(opts) < 2 { + t.Errorf("Too few options recieved.") + } + + val, ok := opts["one"] + if !ok { + t.Errorf("Couldn't find first option parameter.") + } else { + tOpts1, ok := val.(map[string]interface{}) + if !ok { + t.Errorf("Failed to assert the first option parameter as type testOpts.") + } else { + if tOpts1["first"].(string) != optsValues[0][0] { + t.Errorf("First value in first options field is incorrect; want %v but got %v", tOpts1["first"].(string), optsValues[0][0]) + } + if tOpts1["second"].(string) != optsValues[0][1] { + t.Errorf("Second value in first options field is incorrect; want %v but got %v", tOpts1["second"].(string), optsValues[0][1]) + } + } + } + + val2, ok := opts["two"] + if !ok { + t.Errorf("Couldn't find second option parameter.") + } else { + tOpts2, ok := val2.(map[string]interface{}) + if !ok { + t.Errorf("Failed to assert the second option parameter as type testOpts.") + } else { + if tOpts2["first"].(string) != optsValues[1][0] { + t.Errorf("First value in second options field is incorrect; want %v but got %v", tOpts2["first"].(string), optsValues[1][0]) + } + if tOpts2["second"].(string) != optsValues[1][1] { + t.Errorf("Second value in second options field is incorrect; want %v but got %v", tOpts2["second"].(string), optsValues[1][1]) + } + } + } + + // Send a proper reply so that no other errors crop up. + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(responseBody)) + + })) + headers := http.Header{} + headers.Add("Content-Type", "application/x-www-form-urlencoded") + + firstOption := testOpts{optsValues[0][0], optsValues[0][1]} + secondOption := testOpts{optsValues[1][0], optsValues[1][1]} + inputOpts := make(map[string]interface{}) + inputOpts["one"] = firstOption + inputOpts["two"] = secondOption + ExchangeToken(context.Background(), ts.URL, &tokenRequest, auth, headers, inputOpts) +} \ No newline at end of file From 9432311ec22a592927a17f0ff31894efa5dfb7df Mon Sep 17 00:00:00 2001 From: Patrick Jones Date: Tue, 17 Nov 2020 16:40:37 -0800 Subject: [PATCH 17/18] google: clean up tests and add godoc comments Change-Id: I54d2702a149d1eccd64f8ef2ed4b4617429c9358 --- .../internal/externalaccount/sts_exchange.go | 7 ++++ .../externalaccount/sts_exchange_test.go | 38 +++++++++---------- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/google/internal/externalaccount/sts_exchange.go b/google/internal/externalaccount/sts_exchange.go index 7571a0b..fb90be3 100644 --- a/google/internal/externalaccount/sts_exchange.go +++ b/google/internal/externalaccount/sts_exchange.go @@ -16,6 +16,10 @@ import ( "strings" ) +// ExchangeToken preforms a oauth2 token exchange with the provided endpoint. +// The first 4 fields are all mandatory. headers can be used to pass additional +// headers beyond the bare minimum required by the token exchange. options can +// be used to pass additional JSON-structured options to the remote server. func ExchangeToken(ctx context.Context, endpoint string, request *STSTokenExchangeRequest, authentication ClientAuthentication, headers http.Header, options map[string]interface{}) (*STSTokenExchangeResponse, error) { client := oauth2.NewClient(ctx, nil) @@ -63,9 +67,11 @@ func ExchangeToken(ctx context.Context, endpoint string, request *STSTokenExchan } + return &stsResp, nil } +// STSTokenExchangeRequest contains fields necessary to make an oauth2 token exchange. type STSTokenExchangeRequest struct { ActingParty struct { ActorToken string @@ -80,6 +86,7 @@ type STSTokenExchangeRequest struct { SubjectTokenType string } +// STSTokenExchangeResponse is used to decode the remote server's response during an oauth2 token exchange. type STSTokenExchangeResponse struct { AccessToken string `json:"access_token"` IssuedTokenType string `json:"issued_token_type"` diff --git a/google/internal/externalaccount/sts_exchange_test.go b/google/internal/externalaccount/sts_exchange_test.go index 1846b02..e8bc0d0 100644 --- a/google/internal/externalaccount/sts_exchange_test.go +++ b/google/internal/externalaccount/sts_exchange_test.go @@ -47,7 +47,6 @@ var expectedToken = STSTokenExchangeResponse{ } func TestExchangeToken(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { t.Errorf("Unexpected request method, %v is found", r.Method) @@ -55,24 +54,23 @@ func TestExchangeToken(t *testing.T) { if r.URL.String() != "/" { t.Errorf("Unexpected request URL, %v is found", r.URL) } - headerAuth := r.Header.Get("Authorization") - if headerAuth != "Basic cmJyZ25vZ25yaG9uZ28zYmk0Z2I5Z2hnOWc6bm90c29zZWNyZXQ=" { - t.Errorf("Unexpected autohrization header, %v is found", headerAuth) + if got, want := r.Header.Get("Authorization"), "Basic cmJyZ25vZ25yaG9uZ28zYmk0Z2I5Z2hnOWc6bm90c29zZWNyZXQ="; got != want { + t.Errorf("Unexpected authorization header, got %v, want %v", got, want) } - headerContentType := r.Header.Get("Content-Type") - if headerContentType != "application/x-www-form-urlencoded" { - t.Errorf("Unexpected Content-Type header, %v is found", headerContentType) + if got, want := r.Header.Get("Content-Type"), "application/x-www-form-urlencoded"; got != want{ + t.Errorf("Unexpected Content-Type header, got %v, want %v", got, want) } body, err := ioutil.ReadAll(r.Body) if err != nil { t.Errorf("Failed reading request body: %v.", err) } - if string(body) != requestbody { - t.Errorf("Unexpected exchange payload, %v is found", string(body)) + if got, want := string(body), requestbody; got != want { + t.Errorf("Unexpected exchange payload, got %v but want %v", got, want) } w.Header().Set("Content-Type", "application/json") w.Write([]byte(responseBody)) })) + defer ts.Close() headers := http.Header{} headers.Add("Content-Type", "application/x-www-form-urlencoded") @@ -95,6 +93,7 @@ func TestExchangeToken_Err(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.Write([]byte("what's wrong with this response?")) })) + defer ts.Close() headers := http.Header{} headers.Add("Content-Type", "application/x-www-form-urlencoded") @@ -113,7 +112,7 @@ func TestExchangeToken_Opts(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { body, err := ioutil.ReadAll(r.Body) if err != nil { - t.Errorf("Failed reading request body: %v.", err) + t.Fatalf("Failed reading request body: %v.", err) } data, err := url.ParseQuery(string(body)) if err != nil { @@ -128,7 +127,7 @@ func TestExchangeToken_Opts(t *testing.T) { var opts map[string]interface{} err = json.Unmarshal([]byte(strOpts[0]), &opts) if len(opts) < 2 { - t.Errorf("Too few options recieved.") + t.Errorf("Too few options received.") } val, ok := opts["one"] @@ -139,11 +138,11 @@ func TestExchangeToken_Opts(t *testing.T) { if !ok { t.Errorf("Failed to assert the first option parameter as type testOpts.") } else { - if tOpts1["first"].(string) != optsValues[0][0] { - t.Errorf("First value in first options field is incorrect; want %v but got %v", tOpts1["first"].(string), optsValues[0][0]) + if got, want := tOpts1["first"].(string), optsValues[0][0]; got != want { + t.Errorf("First value in first options field is incorrect; got %v but want %v", got, want) } - if tOpts1["second"].(string) != optsValues[0][1] { - t.Errorf("Second value in first options field is incorrect; want %v but got %v", tOpts1["second"].(string), optsValues[0][1]) + if got, want := tOpts1["second"].(string), optsValues[0][1]; got != want { + t.Errorf("Second value in first options field is incorrect; got %v but want %v", got, want) } } } @@ -156,11 +155,11 @@ func TestExchangeToken_Opts(t *testing.T) { if !ok { t.Errorf("Failed to assert the second option parameter as type testOpts.") } else { - if tOpts2["first"].(string) != optsValues[1][0] { - t.Errorf("First value in second options field is incorrect; want %v but got %v", tOpts2["first"].(string), optsValues[1][0]) + if got, want := tOpts2["first"].(string), optsValues[1][0]; got != want { + t.Errorf("First value in second options field is incorrect; got %v but want %v", got, want) } - if tOpts2["second"].(string) != optsValues[1][1] { - t.Errorf("Second value in second options field is incorrect; want %v but got %v", tOpts2["second"].(string), optsValues[1][1]) + if got, want := tOpts2["second"].(string), optsValues[1][1]; got != want { + t.Errorf("Second value in second options field is incorrect; got %v but want %v", got, want) } } } @@ -170,6 +169,7 @@ func TestExchangeToken_Opts(t *testing.T) { w.Write([]byte(responseBody)) })) + defer ts.Close() headers := http.Header{} headers.Add("Content-Type", "application/x-www-form-urlencoded") From c53b04884949cbb522264f2d92aeb478b9255c1e Mon Sep 17 00:00:00 2001 From: Patrick Jones Date: Wed, 18 Nov 2020 11:03:03 -0800 Subject: [PATCH 18/18] google: fix spelling and go fmt Change-Id: Ifc55814018bf66d2e77752d770a51c716376cabc --- google/internal/externalaccount/sts_exchange.go | 3 +-- .../internal/externalaccount/sts_exchange_test.go | 13 +++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/google/internal/externalaccount/sts_exchange.go b/google/internal/externalaccount/sts_exchange.go index fb90be3..cc2c99a 100644 --- a/google/internal/externalaccount/sts_exchange.go +++ b/google/internal/externalaccount/sts_exchange.go @@ -16,7 +16,7 @@ import ( "strings" ) -// ExchangeToken preforms a oauth2 token exchange with the provided endpoint. +// ExchangeToken performs an oauth2 token exchange with the provided endpoint. // The first 4 fields are all mandatory. headers can be used to pass additional // headers beyond the bare minimum required by the token exchange. options can // be used to pass additional JSON-structured options to the remote server. @@ -67,7 +67,6 @@ func ExchangeToken(ctx context.Context, endpoint string, request *STSTokenExchan } - return &stsResp, nil } diff --git a/google/internal/externalaccount/sts_exchange_test.go b/google/internal/externalaccount/sts_exchange_test.go index e8bc0d0..bd4034a 100644 --- a/google/internal/externalaccount/sts_exchange_test.go +++ b/google/internal/externalaccount/sts_exchange_test.go @@ -57,7 +57,7 @@ func TestExchangeToken(t *testing.T) { if got, want := r.Header.Get("Authorization"), "Basic cmJyZ25vZ25yaG9uZ28zYmk0Z2I5Z2hnOWc6bm90c29zZWNyZXQ="; got != want { t.Errorf("Unexpected authorization header, got %v, want %v", got, want) } - if got, want := r.Header.Get("Content-Type"), "application/x-www-form-urlencoded"; got != want{ + if got, want := r.Header.Get("Content-Type"), "application/x-www-form-urlencoded"; got != want { t.Errorf("Unexpected Content-Type header, got %v, want %v", got, want) } body, err := ioutil.ReadAll(r.Body) @@ -84,8 +84,6 @@ func TestExchangeToken(t *testing.T) { t.Errorf("mismatched messages received by mock server. \nWant: \n%v\n\nGot:\n%v", expectedToken, *resp) } - - } func TestExchangeToken_Err(t *testing.T) { @@ -102,12 +100,15 @@ func TestExchangeToken_Err(t *testing.T) { t.Errorf("Expected handled error; instead got nil.") } } + /* Lean test specifically for options, as the other features are tested earlier. */ type testOpts struct { - First string `json:"first"` + First string `json:"first"` Second string `json:"second"` } -var optsValues = [][]string{{"foo", "bar"},{"cat", "pan"}} + +var optsValues = [][]string{{"foo", "bar"}, {"cat", "pan"}} + func TestExchangeToken_Opts(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { body, err := ioutil.ReadAll(r.Body) @@ -179,4 +180,4 @@ func TestExchangeToken_Opts(t *testing.T) { inputOpts["one"] = firstOption inputOpts["two"] = secondOption ExchangeToken(context.Background(), ts.URL, &tokenRequest, auth, headers, inputOpts) -} \ No newline at end of file +}