Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(auth/oauth2adapt): adds a new module to translate types #8595

Merged
merged 7 commits into from
Sep 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .release-please-manifest-individual.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"auth": "0.0.0",
"auth/oauth2adapt": "0.0.0",
"bigquery": "1.55.0",
"bigtable": "1.19.0",
"datastore": "1.14.0",
Expand Down
19 changes: 19 additions & 0 deletions auth/oauth2adapt/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module cloud.google.com/go/auth/oauth2adapt

go 1.19

require (
cloud.google.com/go/auth v0.0.0
github.com/google/go-cmp v0.5.9
golang.org/x/oauth2 v0.11.0
)

require (
github.com/golang/protobuf v1.5.3 // indirect
golang.org/x/net v0.14.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.31.0 // indirect
)

// TODO(codyoss): remove this once we have a real release.
replace cloud.google.com/go/auth => ../
24 changes: 24 additions & 0 deletions auth/oauth2adapt/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU=
golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
123 changes: 123 additions & 0 deletions auth/oauth2adapt/oauth2adapt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package oauth2adapt helps converts types used in [cloud.google.com/go/auth]
// and [golang.org/x/oauth2].
package oauth2adapt

import (
"context"
"encoding/json"
"errors"

"cloud.google.com/go/auth"
"golang.org/x/oauth2"
)

// TokenProviderFromTokenSource converts any [golang.org/x/oauth2.TokenSource]
// into a [cloud.google.com/go/auth.TokenProvider].
func TokenProviderFromTokenSource(ts oauth2.TokenSource) auth.TokenProvider {
return &tokenProviderAdapter{ts: ts}
}

type tokenProviderAdapter struct {
ts oauth2.TokenSource
}

// Token fulfills the [cloud.google.com/go/auth.TokenProvider] interface. It
// is a light wrapper around the underlying TokenSource.
func (tp *tokenProviderAdapter) Token(context.Context) (*auth.Token, error) {
tok, err := tp.ts.Token()
if err != nil {
var err2 *oauth2.RetrieveError
if ok := errors.As(err, &err2); ok {
return nil, AuthErrorFromRetrieveError(err2)
}
return nil, err
}
return &auth.Token{
Value: tok.AccessToken,
Expiry: tok.Expiry,
}, nil
}

// TokenSourceFromTokenProvider converts any
// [cloud.google.com/go/auth.TokenProvider] into a
// [golang.org/x/oauth2.TokenSource].
func TokenSourceFromTokenProvider(tp auth.TokenProvider) oauth2.TokenSource {
return &tokenSourceAdapter{tp: tp}
}

type tokenSourceAdapter struct {
tp auth.TokenProvider
}

// Token fulfills the [golang.org/x/oauth2.TokenSource] interface. It
// is a light wrapper around the underlying TokenProvider.
func (ts *tokenSourceAdapter) Token() (*oauth2.Token, error) {
tok, err := ts.tp.Token(context.Background())
if err != nil {
var err2 *auth.Error
if ok := errors.As(err, &err2); ok {
return nil, AddRetrieveErrorToAuthError(err2)
}
return nil, err
}
return &oauth2.Token{
AccessToken: tok.Value,
Expiry: tok.Expiry,
}, nil
}

type oauth2Error struct {
ErrorCode string `json:"error"`
ErrorDescription string `json:"error_description"`
ErrorURI string `json:"error_uri"`
}

// AddRetrieveErrorToAuthError returns the same error provided and adds a
// [golang.org/x/oauth2.RetrieveError] to the error chain by setting the `Err` field on the
// [cloud.google.com/go/auth.Error].
func AddRetrieveErrorToAuthError(err *auth.Error) *auth.Error {
codyoss marked this conversation as resolved.
Show resolved Hide resolved
if err == nil {
return nil
}
e := &oauth2.RetrieveError{
Response: err.Response,
Body: err.Body,
}
err.Err = e
if len(err.Body) > 0 {
var oErr oauth2Error
// ignore the error as it only fills in extra details
json.Unmarshal(err.Body, &oErr)
e.ErrorCode = oErr.ErrorCode
e.ErrorDescription = oErr.ErrorDescription
e.ErrorURI = oErr.ErrorURI
}
return err
}

// AuthErrorFromRetrieveError returns an [cloud.google.com/go/auth.Error] that
// wraps the provided [golang.org/x/oauth2.RetrieveError].
func AuthErrorFromRetrieveError(err *oauth2.RetrieveError) *auth.Error {
if err == nil {
return nil
}
return &auth.Error{
Response: err.Response,
Body: err.Body,
Err: err,
}
}
169 changes: 169 additions & 0 deletions auth/oauth2adapt/oauth2adapt_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package oauth2adapt

import (
"context"
"errors"
"net/http"
"testing"

"cloud.google.com/go/auth"
"github.com/google/go-cmp/cmp"
"golang.org/x/oauth2"
)

func TestTokenProviderFromTokenSource(t *testing.T) {
tests := []struct {
name string
token string
err error
}{
{
name: "working token",
token: "fakeToken",
err: nil,
},
{
name: "coverts err",
err: &oauth2.RetrieveError{
Body: []byte("some bytes"),
ErrorCode: "412",
Response: &http.Response{
StatusCode: http.StatusTeapot,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tp := TokenProviderFromTokenSource(tokenSource{
token: tt.token,
err: tt.err,
})
tok, err := tp.Token(context.Background())
if tt.err != nil {
aErr := &auth.Error{}
if !errors.As(err, &aErr) {
t.Fatalf("error not of correct type: %T", err)
}
err := tt.err.(*oauth2.RetrieveError)
if !cmp.Equal(aErr.Body, err.Body) {
t.Errorf("got %s, want %s", aErr.Body, err.Body)
}
if !cmp.Equal(aErr.Err, err) {
t.Errorf("got %s, want %s", aErr.Err, err)
}
if !cmp.Equal(aErr.Response, err.Response) {
t.Errorf("got %s, want %s", aErr.Err, err)
}
return
}
if tok.Value != tt.token {
t.Errorf("got %q, want %q", tok.Value, tt.token)
}
})
}
}

func TestTokenSourceFromTokenProvider(t *testing.T) {
tests := []struct {
name string
token string
err error
}{
{
name: "working token",
token: "fakeToken",
err: nil,
},
{
name: "coverts err",
err: &auth.Error{
Body: []byte("some bytes"),
Response: &http.Response{
StatusCode: http.StatusTeapot,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ts := TokenSourceFromTokenProvider(tokenProvider{
token: tt.token,
err: tt.err,
})
tok, err := ts.Token()
if tt.err != nil {
// Should be able to be an auth.Error
aErr := &auth.Error{}
if !errors.As(err, &aErr) {
t.Fatalf("error not of correct type: %T", err)
}
err := tt.err.(*auth.Error)
if !cmp.Equal(aErr.Body, err.Body) {
t.Errorf("got %s, want %s", aErr.Body, err.Body)
}
if !cmp.Equal(aErr.Response, err.Response) {
t.Errorf("got %s, want %s", aErr.Err, err)
}

// Should be able to be an oauth2.RetrieveError
rErr := &oauth2.RetrieveError{}
if !errors.As(err, &rErr) {
t.Fatalf("error not of correct type: %T", err)
}
if !cmp.Equal(rErr.Body, err.Body) {
t.Errorf("got %s, want %s", aErr.Body, err.Body)
}
if !cmp.Equal(rErr.Response, err.Response) {
t.Errorf("got %s, want %s", aErr.Err, err)
}
return
}
if tok.AccessToken != tt.token {
t.Errorf("got %q, want %q", tok.AccessToken, tt.token)
}
})
}
}

type tokenSource struct {
token string
err error
}

func (ts tokenSource) Token() (*oauth2.Token, error) {
if ts.err != nil {
return nil, ts.err
}
return &oauth2.Token{
AccessToken: ts.token,
}, nil
}

type tokenProvider struct {
token string
err error
}

func (tp tokenProvider) Token(context.Context) (*auth.Token, error) {
if tp.err != nil {
return nil, tp.err
}
return &auth.Token{
Value: tp.token,
}, nil
}
1 change: 1 addition & 0 deletions go.work
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use (
./asset
./assuredworkloads
./auth
./auth/oauth2adapt
./automl
./baremetalsolution
./batch
Expand Down
4 changes: 2 additions & 2 deletions internal/generated/snippets/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ require (
cloud.google.com/go/workflows v1.11.1
cloud.google.com/go/workstations v0.0.0-00010101000000-000000000000
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
google.golang.org/api v0.138.0
google.golang.org/api v0.139.0
google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d
)

Expand All @@ -152,7 +152,7 @@ require (
cloud.google.com/go/dataproc v1.12.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/s2a-go v0.1.5 // indirect
github.com/google/s2a-go v0.1.7 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.5 // indirect
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
go.opencensus.io v0.24.0 // indirect
Expand Down
Loading