6dccf494f8
cleanup permit policies and correct documentation * docs * policy cleanup * refactor * Merge remote-tracking branch 'origin/feature/permit-policy-cleanup' into feature/permit-policy-cleanup * docs and edits
357 lines
9.4 KiB
Go
357 lines
9.4 KiB
Go
//go:build aws
|
|
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"flag"
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/aws/aws-sdk-go-v2/aws"
|
|
"github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider"
|
|
"github.com/joho/godotenv"
|
|
)
|
|
|
|
// Test configuration
|
|
const (
|
|
testCSVPath = "./temp-users.csv"
|
|
testProjectID = "ebde1e1e9623491cab6f8112e67bd61c"
|
|
testEnvID = "9d6801123cfd4a0ea2ef1df2f430e9d3"
|
|
)
|
|
|
|
// Test data
|
|
var testUsers = []map[string]string{
|
|
{
|
|
"email": "foo1@gmail.com",
|
|
"first_name": "john",
|
|
"last_name": "smith",
|
|
"roles": "editor,viewer",
|
|
},
|
|
{
|
|
"email": "foo2@gmail.com",
|
|
"first_name": "jane",
|
|
"last_name": "smith",
|
|
"roles": "editor,viewer,admin",
|
|
},
|
|
}
|
|
|
|
func TestMain(m *testing.M) {
|
|
// Load environment variables
|
|
if err := godotenv.Overload(); err != nil {
|
|
fmt.Println("warning - problem loading .env file - ignoring.")
|
|
}
|
|
|
|
// Run tests
|
|
code := m.Run()
|
|
|
|
// Cleanup
|
|
if err := os.Remove(testCSVPath); err != nil && !os.IsNotExist(err) {
|
|
fmt.Printf("Warning: failed to cleanup test CSV file: %v\n", err)
|
|
}
|
|
|
|
os.Exit(code)
|
|
}
|
|
|
|
func TestUserLifecycle(t *testing.T) {
|
|
// Skip test if required environment variables are not set
|
|
if !checkRequiredEnvVars(t) {
|
|
return
|
|
}
|
|
|
|
// Create test CSV file
|
|
if err := createTestCSV(); err != nil {
|
|
t.Fatalf("Failed to create test CSV: %v", err)
|
|
}
|
|
|
|
// Test user creation
|
|
t.Run("CreateUsers", func(t *testing.T) {
|
|
testCreateUsers(t)
|
|
})
|
|
|
|
// Test user verification
|
|
t.Run("VerifyUsersExist", func(t *testing.T) {
|
|
testVerifyUsersExist(t)
|
|
})
|
|
|
|
// Test user deletion
|
|
t.Run("DeleteUsers", func(t *testing.T) {
|
|
testDeleteUsers(t)
|
|
})
|
|
|
|
// Test user deletion verification
|
|
t.Run("VerifyUsersDeleted", func(t *testing.T) {
|
|
testVerifyUsersDeleted(t)
|
|
})
|
|
}
|
|
|
|
func checkRequiredEnvVars(t *testing.T) bool {
|
|
required := []string{"PERMIT_KEY", "COGNITO_USER_POOL_ID", "AWS_REGION"}
|
|
missing := []string{}
|
|
|
|
for _, env := range required {
|
|
if os.Getenv(env) == "" {
|
|
missing = append(missing, env)
|
|
}
|
|
}
|
|
|
|
if len(missing) > 0 {
|
|
t.Skipf("Skipping integration test - missing required environment variables: %v", missing)
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func createTestCSV() error {
|
|
csvContent := "email,first_name,last_name,role1,role2,role3\n"
|
|
csvContent += "foo1@gmail.com,john,smith,editor,viewer,\n"
|
|
csvContent += "foo2@gmail.com,jane,smith,editor,viewer,admin\n"
|
|
|
|
return os.WriteFile(testCSVPath, []byte(csvContent), 0600)
|
|
}
|
|
|
|
func testCreateUsers(t *testing.T) {
|
|
// Save original os.Args
|
|
originalArgs := os.Args
|
|
|
|
// Set up args for user creation
|
|
os.Args = []string{
|
|
"cognito-permit-sync",
|
|
"-project", testProjectID,
|
|
"-env", testEnvID,
|
|
"-csv", testCSVPath,
|
|
}
|
|
|
|
// Restore os.Args after test
|
|
defer func() {
|
|
os.Args = originalArgs
|
|
}()
|
|
|
|
// Reset flags for testing
|
|
resetFlags()
|
|
|
|
// Run the application (this will create its own audit logger internally)
|
|
err := run()
|
|
if err != nil {
|
|
t.Fatalf("Failed to create users: %v", err)
|
|
}
|
|
|
|
t.Logf("Successfully created users")
|
|
}
|
|
|
|
func testVerifyUsersExist(t *testing.T) {
|
|
ctx := t.Context()
|
|
|
|
// Initialize clients
|
|
cognitoClient, err := initializeCognitoClient()
|
|
if err != nil {
|
|
t.Fatalf("Failed to initialize Cognito client: %v", err)
|
|
}
|
|
|
|
httpClient := &http.Client{Timeout: 30 * time.Second}
|
|
config := AppConfig{
|
|
PermitAPIKey: os.Getenv("PERMIT_KEY"),
|
|
PermitProjectID: testProjectID,
|
|
PermitEnvID: testEnvID,
|
|
PermitBaseURL: "https://api.permit.io",
|
|
CognitoUserPoolID: os.Getenv("COGNITO_USER_POOL_ID"),
|
|
CognitoRegion: os.Getenv("AWS_REGION"),
|
|
}
|
|
|
|
for _, user := range testUsers {
|
|
t.Run(fmt.Sprintf("VerifyUser_%s", user["email"]), func(t *testing.T) {
|
|
// Verify user exists in Cognito
|
|
cognitoSubjectID, err := getCognitoUserSubject(ctx, cognitoClient, config.CognitoUserPoolID, user["email"])
|
|
if err != nil {
|
|
t.Fatalf("Failed to find user %s in Cognito: %v", user["email"], err)
|
|
}
|
|
if cognitoSubjectID == "" {
|
|
t.Fatalf("User %s not found in Cognito", user["email"])
|
|
}
|
|
t.Logf("User %s found in Cognito with subject ID: %s", user["email"], cognitoSubjectID)
|
|
|
|
// Verify user exists in Permit.io
|
|
permitUserKey, err := findPermitUserByEmail(httpClient, config, user["email"])
|
|
if err != nil {
|
|
t.Fatalf("Failed to find user %s in Permit.io: %v", user["email"], err)
|
|
}
|
|
if permitUserKey == "" {
|
|
t.Fatalf("User %s not found in Permit.io", user["email"])
|
|
}
|
|
t.Logf("User %s found in Permit.io with key: %s", user["email"], permitUserKey)
|
|
|
|
// Verify subject IDs match
|
|
if cognitoSubjectID != permitUserKey {
|
|
t.Fatalf("Subject ID mismatch for user %s: Cognito=%s, Permit.io=%s",
|
|
user["email"], cognitoSubjectID, permitUserKey)
|
|
}
|
|
t.Logf("Subject IDs match for user %s: %s", user["email"], cognitoSubjectID)
|
|
|
|
// Verify roles are assigned in Permit.io
|
|
if user["roles"] != "" {
|
|
expectedRoles := strings.Split(user["roles"], ",")
|
|
assignedRoles, err := getPermitUserRoles(httpClient, config, permitUserKey)
|
|
if err != nil {
|
|
t.Fatalf("Failed to get roles for user %s: %v", user["email"], err)
|
|
}
|
|
|
|
for _, expectedRole := range expectedRoles {
|
|
expectedRole = strings.TrimSpace(expectedRole)
|
|
if expectedRole == "" {
|
|
continue
|
|
}
|
|
found := false
|
|
for _, assignedRole := range assignedRoles {
|
|
if assignedRole == expectedRole {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
t.Fatalf("Expected role %s not found for user %s. Assigned roles: %v",
|
|
expectedRole, user["email"], assignedRoles)
|
|
}
|
|
}
|
|
t.Logf("All expected roles verified for user %s: %v", user["email"], expectedRoles)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func testDeleteUsers(t *testing.T) {
|
|
// Save original os.Args
|
|
originalArgs := os.Args
|
|
|
|
// Set up args for user deletion
|
|
os.Args = []string{
|
|
"cognito-permit-sync",
|
|
"-project", testProjectID,
|
|
"-env", testEnvID,
|
|
"-csv", testCSVPath,
|
|
"--delete-users",
|
|
}
|
|
|
|
// Restore os.Args after test
|
|
defer func() {
|
|
os.Args = originalArgs
|
|
}()
|
|
|
|
// Reset flags for testing
|
|
resetFlags()
|
|
|
|
// Run the application in delete mode
|
|
err := run()
|
|
if err != nil {
|
|
t.Fatalf("Failed to delete users: %v", err)
|
|
}
|
|
|
|
t.Logf("Successfully deleted users")
|
|
}
|
|
|
|
func testVerifyUsersDeleted(t *testing.T) {
|
|
ctx := t.Context()
|
|
|
|
// Initialize clients
|
|
cognitoClient, err := initializeCognitoClient()
|
|
if err != nil {
|
|
t.Fatalf("Failed to initialize Cognito client: %v", err)
|
|
}
|
|
|
|
httpClient := &http.Client{Timeout: 30 * time.Second}
|
|
config := AppConfig{
|
|
PermitAPIKey: os.Getenv("PERMIT_KEY"),
|
|
PermitProjectID: testProjectID,
|
|
PermitEnvID: testEnvID,
|
|
PermitBaseURL: "https://api.permit.io",
|
|
CognitoUserPoolID: os.Getenv("COGNITO_USER_POOL_ID"),
|
|
CognitoRegion: os.Getenv("AWS_REGION"),
|
|
}
|
|
|
|
for _, user := range testUsers {
|
|
t.Run(fmt.Sprintf("VerifyUserDeleted_%s", user["email"]), func(t *testing.T) {
|
|
// Verify user is deleted from both systems with retry logic
|
|
const maxRetries = 12 // 1 minute with 5-second intervals
|
|
const retryInterval = 5 * time.Second
|
|
|
|
var cognitoDeleted, permitDeleted bool
|
|
|
|
for attempt := 0; attempt < maxRetries; attempt++ {
|
|
if attempt > 0 {
|
|
t.Logf("Retry attempt %d/%d for user %s", attempt+1, maxRetries, user["email"])
|
|
time.Sleep(retryInterval)
|
|
}
|
|
|
|
// Check Cognito deletion
|
|
if !cognitoDeleted {
|
|
cognitoSubjectID, err := getCognitoUserSubject(ctx, cognitoClient, config.CognitoUserPoolID, user["email"])
|
|
cognitoDeleted = (err != nil || cognitoSubjectID == "")
|
|
}
|
|
|
|
// Check Permit.io deletion
|
|
if !permitDeleted {
|
|
permitUserKey, err := findPermitUserByEmail(httpClient, config, user["email"])
|
|
permitDeleted = (err != nil || permitUserKey == "")
|
|
}
|
|
|
|
// If both are deleted, we're done
|
|
if cognitoDeleted && permitDeleted {
|
|
break
|
|
}
|
|
}
|
|
|
|
// Final verification
|
|
if !cognitoDeleted {
|
|
cognitoSubjectID, _ := getCognitoUserSubject(ctx, cognitoClient, config.CognitoUserPoolID, user["email"])
|
|
t.Fatalf("User %s still exists in Cognito after %d attempts with subject ID: %s",
|
|
user["email"], maxRetries, cognitoSubjectID)
|
|
}
|
|
t.Logf("User %s successfully deleted from Cognito", user["email"])
|
|
|
|
if !permitDeleted {
|
|
permitUserKey, _ := findPermitUserByEmail(httpClient, config, user["email"])
|
|
t.Fatalf("User %s still exists in Permit.io after %d attempts with key: %s",
|
|
user["email"], maxRetries, permitUserKey)
|
|
}
|
|
t.Logf("User %s successfully deleted from Permit.io", user["email"])
|
|
})
|
|
}
|
|
}
|
|
|
|
// Helper function to get Cognito user subject ID
|
|
func getCognitoUserSubject(ctx context.Context, client *cognitoidentityprovider.Client, userPoolID, username string) (string, error) {
|
|
input := &cognitoidentityprovider.AdminGetUserInput{
|
|
UserPoolId: aws.String(userPoolID),
|
|
Username: aws.String(username),
|
|
}
|
|
|
|
result, err := client.AdminGetUser(ctx, input)
|
|
if err != nil {
|
|
// Check if it's a UserNotFoundException
|
|
if strings.Contains(err.Error(), "UserNotFoundException") ||
|
|
strings.Contains(err.Error(), "User does not exist") {
|
|
return "", nil // User not found, return empty string
|
|
}
|
|
return "", err
|
|
}
|
|
|
|
// Extract subject ID from user attributes
|
|
for _, attr := range result.UserAttributes {
|
|
if aws.ToString(attr.Name) == "sub" {
|
|
return aws.ToString(attr.Value), nil
|
|
}
|
|
}
|
|
|
|
return "", fmt.Errorf("subject ID not found for user %s", username)
|
|
}
|
|
|
|
// Helper function to reset flags for testing
|
|
func resetFlags() {
|
|
// Reset the default command line flag set
|
|
flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
|
|
}
|