@@ -7,13 +7,22 @@ package google
77import (
88 "context"
99 "fmt"
10+ "regexp"
11+ "strings"
1012 "sync"
1113 "time"
1214
1315 "golang.org/x/oauth2"
1416 "google.golang.org/api/iamcredentials/v1"
1517)
1618
19+ var (
20+ mu sync.Mutex
21+ tok * oauth2.Token
22+ )
23+
24+ const emailRegex string = "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\ .[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$"
25+
1726// DelegateTokenSource allows a TokenSource issued to a user or
1827// service account to impersonate another. The target service account
1928// must grant the orginating principal the
@@ -25,7 +34,7 @@ import (
2534// rootSource *must* include scopes that contains
2635// "https://www.googleapis.com/auth/iam"
2736// or
28- // "https://www.googleapis.com/auth/cloud- platform"
37+ // "https://www.googleapis.com/auth/cloud. platform"
2938// principal (string): The service account to impersonate.
3039// new_scopes ([]string): Scopes to request during the
3140// authorization grant.
@@ -54,6 +63,29 @@ func DelegateTokenSource(ctx context.Context, rootSource oauth2.TokenSource,
5463 principal string , lifetime time.Duration , delegates []string ,
5564 newScopes []string ) (oauth2.TokenSource , error ) {
5665
66+ reEmail := regexp .MustCompile (emailRegex )
67+ scopePrefix := "https://www.googleapis.com/auth/"
68+
69+ if rootSource == nil {
70+ return nil , fmt .Errorf ("oauth2/google: rootSource cannot be nil" )
71+ }
72+ if ! reEmail .MatchString (principal ) {
73+ return nil , fmt .Errorf ("oauth2/google: principal must be a serviceAccount email address" )
74+ }
75+ if lifetime <= time .Duration (3600 ) {
76+ return nil , fmt .Errorf ("oauth2/google: lifetime must be less than or equal to 3600 seconds" )
77+ }
78+ for _ , d := range delegates {
79+ if ! reEmail .MatchString (d ) {
80+ return nil , fmt .Errorf ("oauth2/google: delegates must be a serviceAccount email address: %v" , d )
81+ }
82+ }
83+ for _ , s := range newScopes {
84+ if ! strings .HasPrefix (s , scopePrefix ) {
85+ return nil , fmt .Errorf ("oauth2/google: scopes must be a Google Auth scope url: %v" , s )
86+ }
87+ }
88+
5789 return & delegateTokenSource {
5890 ctx : ctx ,
5991 rootSource : rootSource ,
@@ -73,11 +105,6 @@ type delegateTokenSource struct {
73105 newScopes []string
74106}
75107
76- var (
77- mu sync.Mutex
78- tok * oauth2.Token
79- )
80-
81108func (ts * delegateTokenSource ) Token () (* oauth2.Token , error ) {
82109
83110 mu .Lock ()
@@ -91,7 +118,7 @@ func (ts *delegateTokenSource) Token() (*oauth2.Token, error) {
91118
92119 service , err := iamcredentials .New (client )
93120 if err != nil {
94- return nil , fmt .Errorf ("google: Error creating IAMCredentials: %v" , err )
121+ return nil , fmt .Errorf ("oauth2/ google: Error creating IAMCredentials: %v" , err )
95122 }
96123 name := "projects/-/serviceAccounts/" + ts .principal
97124 tokenRequest := & iamcredentials.GenerateAccessTokenRequest {
@@ -101,12 +128,12 @@ func (ts *delegateTokenSource) Token() (*oauth2.Token, error) {
101128 }
102129 at , err := service .Projects .ServiceAccounts .GenerateAccessToken (name , tokenRequest ).Do ()
103130 if err != nil {
104- return nil , fmt .Errorf ("google: Error calling iamcredentials.GenerateAccessToken: %v" , err )
131+ return nil , fmt .Errorf ("oauth2/ google: Error calling iamcredentials.GenerateAccessToken: %v" , err )
105132 }
106133
107134 expireAt , err := time .Parse (time .RFC3339 , at .ExpireTime )
108135 if err != nil {
109- return nil , fmt .Errorf ("google: Error parsing ExpireTime from iamcredentials: %v" , err )
136+ return nil , fmt .Errorf ("oauth2/ google: Error parsing ExpireTime from iamcredentials: %v" , err )
110137 }
111138
112139 tok = & oauth2.Token {
0 commit comments