@@ -2,6 +2,9 @@ package gcp
2
2
3
3
import (
4
4
"context"
5
+ "encoding/base64"
6
+ "errors"
7
+ "fmt"
5
8
"testing"
6
9
7
10
"golang.org/x/oauth2"
@@ -55,4 +58,161 @@ func TestGetCurrentAccountEmail(t *testing.T) {
55
58
t .Errorf ("expected email to be %s, got %s" , expected , email )
56
59
}
57
60
})
61
+
62
+ t .Run ("External account with service account impersonation" , func (t * testing.T ) {
63
+ FindGoogleDefaultCredentials = func (ctx context.Context , scopes ... string ) (* google.Credentials , error ) {
64
+ return & google.Credentials {
65
+ JSON : []byte (`{
66
+ "type": "external_account",
67
+ "service_account_impersonation_url": "https://iamcredentials.googleapis.com/v1/projects/1234567890/serviceAccounts/[email protected] :generateAccessToken"
68
+ }` ),
69
+ }, nil
70
+ }
71
+ var gcp Gcp
72
+ email , err := gcp .GetCurrentAccountEmail (context .Background ())
73
+ if err != nil {
74
+ t .Errorf ("unexpected error: %v" , err )
75
+ }
76
+
77
+ if email != expected {
78
+ t .Errorf ("expected email to be %s, got %s" , expected , email )
79
+ }
80
+ })
81
+
82
+ t .Run ("Error getting credentials" , func (t * testing.T ) {
83
+ FindGoogleDefaultCredentials = func (ctx context.Context , scopes ... string ) (* google.Credentials , error ) {
84
+ return nil , errors .New ("no credentials found" )
85
+ }
86
+ var gcp Gcp
87
+ _ , err := gcp .GetCurrentAccountEmail (context .Background ())
88
+ if err == nil {
89
+ t .Error ("expected error but got none" )
90
+ }
91
+ })
92
+
93
+ t .Run ("Token error" , func (t * testing.T ) {
94
+ FindGoogleDefaultCredentials = func (ctx context.Context , scopes ... string ) (* google.Credentials , error ) {
95
+ return & google.Credentials {
96
+ JSON : []byte (`{}` ),
97
+ TokenSource : & MockTokenSourceWithError {},
98
+ }, nil
99
+ }
100
+ var gcp Gcp
101
+ _ , err := gcp .GetCurrentAccountEmail (context .Background ())
102
+ if err == nil {
103
+ t .Error ("expected error but got none" )
104
+ }
105
+ })
106
+
107
+ t .Run ("Invalid JSON in credentials" , func (t * testing.T ) {
108
+ token := & oauth2.Token {AccessToken : "test-token" }
109
+ FindGoogleDefaultCredentials = func (ctx context.Context , scopes ... string ) (* google.Credentials , error ) {
110
+ return & google.Credentials {
111
+ JSON : []byte (`invalid json` ),
112
+ TokenSource : & MockTokenSource {token : token },
113
+ }, nil
114
+ }
115
+ var gcp Gcp
116
+ _ , err := gcp .GetCurrentAccountEmail (context .Background ())
117
+ if err == nil {
118
+ t .Error ("expected error but got none" )
119
+ }
120
+ })
121
+ }
122
+
123
+ type MockTokenSourceWithError struct {}
124
+
125
+ func (m * MockTokenSourceWithError ) Token () (* oauth2.Token , error ) {
126
+ return nil , errors .New ("token error" )
127
+ }
128
+
129
+ func TestExtractEmailFromIDToken (t * testing.T ) {
130
+ t .Run ("Valid ID token" , func (t * testing.T ) {
131
+ header := `{"typ":"JWT","alg":"HS256"}`
132
+ payload := `{"email":"[email protected] "}`
133
+ idToken := encodeJWT (header , payload )
134
+ email , err := extractEmailFromIDToken (idToken )
135
+ if err != nil {
136
+ t .Errorf ("unexpected error: %v" , err )
137
+ }
138
+
139
+ if email != expected {
140
+ t .Errorf ("expected email to be %s, got %s" , expected , email )
141
+ }
142
+ })
143
+
144
+ t .Run ("Invalid token format" , func (t * testing.T ) {
145
+ idToken := "invalid.token"
146
+ _ , err := extractEmailFromIDToken (idToken )
147
+ if err == nil {
148
+ t .Error ("expected error but got none" )
149
+ }
150
+ })
151
+
152
+ t .Run ("Invalid base64 payload" , func (t * testing.T ) {
153
+ idToken := "header.invalid_base64.signature"
154
+ _ , err := extractEmailFromIDToken (idToken )
155
+ if err == nil {
156
+ t .Error ("expected error but got none" )
157
+ }
158
+ })
159
+
160
+ t .Run ("Invalid JSON payload" , func (t * testing.T ) {
161
+ idToken := encodeJWT ("header" , "invalid json" )
162
+ _ , err := extractEmailFromIDToken (idToken )
163
+ if err == nil {
164
+ t .Error ("expected error but got none" )
165
+ }
166
+ })
167
+
168
+ t .Run ("Empty email in token" , func (t * testing.T ) {
169
+ // JWT with payload: {"email":""}
170
+ header := `{"typ":"JWT","alg":"HS256"}`
171
+ payload := `{"email":""}`
172
+ idToken := encodeJWT (header , payload )
173
+ email , err := extractEmailFromIDToken (idToken )
174
+ if err != nil {
175
+ t .Errorf ("unexpected error: %v" , err )
176
+ }
177
+ if email != "" {
178
+ t .Errorf ("expected empty email, got %s" , email )
179
+ }
180
+ })
181
+ }
182
+
183
+ func TestParseServiceAccountFromURL (t * testing.T ) {
184
+ t .Run ("Valid service account URL" , func (t * testing.T ) {
185
+ url := "https://iamcredentials.googleapis.com/v1/projects/123456789/serviceAccounts/[email protected] :generateAccessToken"
186
+ email , err := parseServiceAccountFromURL (url )
187
+ if err != nil {
188
+ t .Errorf ("unexpected error: %v" , err )
189
+ }
190
+
191
+ if email != expected {
192
+ t .Errorf ("expected email to be %s, got %s" , expected , email )
193
+ }
194
+ })
195
+
196
+ t .Run ("Invalid URL format" , func (t * testing.T ) {
197
+ url := "https://invalidpath"
198
+ _ , err := parseServiceAccountFromURL (url )
199
+ if err == nil {
200
+ t .Error ("expected error but got none" )
201
+ }
202
+ })
203
+
204
+ t .Run ("URL without service account" , func (t * testing.T ) {
205
+ url := "https://iamcredentials.googleapis.com/v1/projects/123456789/notaserviceaccount"
206
+ _ , err := parseServiceAccountFromURL (url )
207
+ if err == nil {
208
+ t .Error ("expected error but got none" )
209
+ }
210
+ })
211
+ }
212
+
213
+ func encodeJWT (header , payload string ) string {
214
+ encode := func (s string ) string {
215
+ return base64 .RawURLEncoding .EncodeToString ([]byte (s ))
216
+ }
217
+ return fmt .Sprintf ("%s.%s.signature" , encode (header ), encode (payload ))
58
218
}
0 commit comments