Skip to content

Commit 78cb6c5

Browse files
committed
add tests using IAM Identity Center profiles
1 parent e50c2e0 commit 78cb6c5

File tree

1 file changed

+185
-0
lines changed

1 file changed

+185
-0
lines changed

pkg/integration_testing/assume_e2e_test.go

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package integration_testing
33
import (
44
"bytes"
55
"context"
6+
"crypto/sha1"
67
"encoding/json"
78
"fmt"
89
"net"
@@ -140,6 +141,174 @@ FileBackend = ""
140141
t.Errorf("Unexpected output format: %s", output)
141142
}
142143
})
144+
145+
t.Run("AssumeProfileWithSSO", func(t *testing.T) {
146+
// Create AWS config with SSO profile
147+
ssoConfig := fmt.Sprintf(`[profile test-sso]
148+
sso_account_id = 123456789012
149+
sso_role_name = TestRole
150+
sso_region = us-east-1
151+
sso_start_url = %s
152+
region = us-east-1
153+
`, mockServer.URL)
154+
155+
// Update AWS config file with SSO profile
156+
err := os.WriteFile(awsConfigPath, []byte(awsConfig+"\n"+ssoConfig), 0644)
157+
require.NoError(t, err)
158+
159+
// Create SSO cache directory and token
160+
ssoCacheDir := filepath.Join(awsDir, "sso", "cache")
161+
err = os.MkdirAll(ssoCacheDir, 0755)
162+
require.NoError(t, err)
163+
164+
// Create a cached SSO token
165+
tokenData := map[string]interface{}{
166+
"accessToken": "cached-test-token",
167+
"expiresAt": time.Now().Add(1 * time.Hour).Format(time.RFC3339),
168+
"region": "us-east-1",
169+
"startUrl": mockServer.URL,
170+
}
171+
tokenBytes, err := json.Marshal(tokenData)
172+
require.NoError(t, err)
173+
174+
// The cache filename is a SHA1 hash of the session name
175+
// For AWS SSO, the session name is derived from the start URL
176+
h := sha1.New()
177+
h.Write([]byte(mockServer.URL))
178+
cacheFile := filepath.Join(ssoCacheDir, fmt.Sprintf("%x.json", h.Sum(nil)))
179+
err = os.WriteFile(cacheFile, tokenBytes, 0600)
180+
require.NoError(t, err)
181+
182+
// Set up environment
183+
env := []string{
184+
fmt.Sprintf("HOME=%s", homeDir),
185+
fmt.Sprintf("AWS_CONFIG_FILE=%s", awsConfigPath),
186+
fmt.Sprintf("XDG_CONFIG_HOME=%s", xdgConfigHome),
187+
"GRANTED_QUIET=true", // Suppress output messages
188+
"FORCE_NO_ALIAS=true", // Skip alias configuration
189+
"FORCE_ASSUME_CLI=true", // Force assume mode
190+
"PATH=" + os.Getenv("PATH"), // Preserve PATH
191+
}
192+
193+
// Run assume command with SSO profile
194+
cmd := exec.Command(grantedBinary, "test-sso")
195+
cmd.Env = env
196+
197+
var stdout, stderr bytes.Buffer
198+
cmd.Stdout = &stdout
199+
cmd.Stderr = &stderr
200+
201+
err = cmd.Run()
202+
if err != nil {
203+
t.Fatalf("Assume command failed: %v\nStdout: %s\nStderr: %s", err, stdout.String(), stderr.String())
204+
}
205+
206+
// Parse output
207+
output := stdout.String()
208+
t.Logf("Assume output: %s", output)
209+
210+
// The assume command outputs credentials in a specific format
211+
assert.Contains(t, output, "GrantedAssume")
212+
213+
// Extract credentials from output
214+
parts := strings.Fields(output)
215+
if len(parts) >= 4 {
216+
accessKey := parts[1]
217+
secretKey := parts[2]
218+
sessionToken := parts[3]
219+
220+
// For SSO profiles, we expect temporary credentials from the mock server
221+
assert.Equal(t, "ASIAMOCKEXAMPLE", accessKey)
222+
assert.Equal(t, "mock-secret-key", secretKey)
223+
assert.Equal(t, "mock-session-token", sessionToken)
224+
} else {
225+
t.Errorf("Unexpected output format: %s", output)
226+
}
227+
})
228+
229+
t.Run("AssumeProfileWithGrantedSSO", func(t *testing.T) {
230+
// Create AWS config with granted_sso_ profile configuration
231+
grantedSSOConfig := fmt.Sprintf(`[profile test-granted-sso]
232+
granted_sso_account_id = 123456789012
233+
granted_sso_role_name = TestRole
234+
granted_sso_region = us-east-1
235+
granted_sso_start_url = %s
236+
credential_process = %s credential-process --profile test-granted-sso
237+
region = us-east-1
238+
`, mockServer.URL, grantedBinary)
239+
240+
// Update AWS config file with granted SSO profile
241+
err := os.WriteFile(awsConfigPath, []byte(awsConfig+"\n"+grantedSSOConfig), 0644)
242+
require.NoError(t, err)
243+
244+
// Create SSO cache directory and token for the granted credential process
245+
ssoCacheDir := filepath.Join(awsDir, "sso", "cache")
246+
err = os.MkdirAll(ssoCacheDir, 0755)
247+
require.NoError(t, err)
248+
249+
// Create a cached SSO token
250+
tokenData := map[string]interface{}{
251+
"accessToken": "cached-test-token",
252+
"expiresAt": time.Now().Add(1 * time.Hour).Format(time.RFC3339),
253+
"region": "us-east-1",
254+
"startUrl": mockServer.URL,
255+
}
256+
tokenBytes, err := json.Marshal(tokenData)
257+
require.NoError(t, err)
258+
259+
// The cache filename is a SHA1 hash of the start URL
260+
h := sha1.New()
261+
h.Write([]byte(mockServer.URL))
262+
cacheFile := filepath.Join(ssoCacheDir, fmt.Sprintf("%x.json", h.Sum(nil)))
263+
err = os.WriteFile(cacheFile, tokenBytes, 0600)
264+
require.NoError(t, err)
265+
266+
// Set up environment
267+
env := []string{
268+
fmt.Sprintf("HOME=%s", homeDir),
269+
fmt.Sprintf("AWS_CONFIG_FILE=%s", awsConfigPath),
270+
fmt.Sprintf("XDG_CONFIG_HOME=%s", xdgConfigHome),
271+
"GRANTED_QUIET=true", // Suppress output messages
272+
"FORCE_NO_ALIAS=true", // Skip alias configuration
273+
"FORCE_ASSUME_CLI=true", // Force assume mode
274+
"PATH=" + os.Getenv("PATH"), // Preserve PATH
275+
}
276+
277+
// Run assume command with granted SSO profile
278+
cmd := exec.Command(grantedBinary, "test-granted-sso")
279+
cmd.Env = env
280+
281+
var stdout, stderr bytes.Buffer
282+
cmd.Stdout = &stdout
283+
cmd.Stderr = &stderr
284+
285+
err = cmd.Run()
286+
if err != nil {
287+
t.Fatalf("Assume command failed: %v\nStdout: %s\nStderr: %s", err, stdout.String(), stderr.String())
288+
}
289+
290+
// Parse output
291+
output := stdout.String()
292+
t.Logf("Assume output: %s", output)
293+
294+
// The assume command outputs credentials in a specific format
295+
assert.Contains(t, output, "GrantedAssume")
296+
297+
// Extract credentials from output
298+
parts := strings.Fields(output)
299+
if len(parts) >= 4 {
300+
accessKey := parts[1]
301+
secretKey := parts[2]
302+
sessionToken := parts[3]
303+
304+
// For granted SSO profiles with credential process, we expect temporary credentials
305+
assert.Equal(t, "ASIAMOCKEXAMPLE", accessKey)
306+
assert.Equal(t, "mock-secret-key", secretKey)
307+
assert.Equal(t, "mock-session-token", sessionToken)
308+
} else {
309+
t.Errorf("Unexpected output format: %s", output)
310+
}
311+
})
143312
}
144313

145314
// AssumeE2EMockServer is a specialized mock server for assume command testing
@@ -171,6 +340,8 @@ func NewAssumeE2EMockServer() *AssumeE2EMockServer {
171340
server.handleGetRoleCredentials(w, r)
172341
case "AWSSSSOPortalService.ListAccounts":
173342
server.handleListAccounts(w, r)
343+
case "AWSSSSOPortalService.ListAccountRoles":
344+
server.handleListAccountRoles(w, r)
174345
case "SSOOIDCService.CreateToken":
175346
server.handleCreateToken(w, r)
176347
default:
@@ -250,3 +421,17 @@ func (s *AssumeE2EMockServer) handleCreateToken(w http.ResponseWriter, r *http.R
250421
w.Header().Set("Content-Type", "application/x-amz-json-1.1")
251422
json.NewEncoder(w).Encode(response)
252423
}
424+
425+
func (s *AssumeE2EMockServer) handleListAccountRoles(w http.ResponseWriter, r *http.Request) {
426+
response := map[string]interface{}{
427+
"roleList": []map[string]interface{}{
428+
{
429+
"roleName": "TestRole",
430+
"accountId": "123456789012",
431+
},
432+
},
433+
}
434+
435+
w.Header().Set("Content-Type", "application/x-amz-json-1.1")
436+
json.NewEncoder(w).Encode(response)
437+
}

0 commit comments

Comments
 (0)