Files
query-orchestration/cmd/cognito_test/user.creation.tool/cognito.go
T
Jay Brown ab7072ac90 Merged in feature/permit.api.keys (pull request #196)
permit keys

* permit keys

use the environment from the api key if requested
2025-12-11 22:18:42 +00:00

259 lines
7.4 KiB
Go

// cognito.go handles AWS Cognito User Pool operations for user identity management.
// This file provides functions to create, delete, enable, disable, and retrieve users from
// AWS Cognito User Pools, including credential validation, user attribute management,
// and integration with AWS SDK v2 for identity provider operations.
package main
import (
"context"
"fmt"
"net/http"
"os"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider"
"github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider/types"
"github.com/aws/aws-sdk-go-v2/service/sts"
)
// Cognito types
type CognitoConfig struct {
UserPoolID string
Region string
}
type UserInput struct {
Email string
FirstName string
LastName string
}
type CognitoUserResponse struct {
SubjectID string
Username string
Email string
FirstName string
LastName string
Status string
Enabled bool
}
// sanityCheckCredentials validates that both AWS and Permit.io credentials are properly configured.
// It checks AWS credentials by calling STS GetCallerIdentity and Permit.io credentials by
// calling the API key scope endpoint.
//
// Parameters:
// - appConfig: Application configuration containing Permit.io API key and base URL
func sanityCheckCredentials(appConfig AppConfig) {
awsProfile := os.Getenv("AWS_PROFILE")
var cfgOpts []func(*config.LoadOptions) error
if awsProfile != "" {
cfgOpts = append(cfgOpts, config.WithSharedConfigProfile(awsProfile))
}
// Pass options with ... to expand the slice
cfg, err := config.LoadDefaultConfig(context.Background(), cfgOpts...)
if err != nil {
fmt.Fprintf(os.Stderr, "AWS: Failed to load config: %v\n", err)
os.Exit(1)
}
stsSvc := sts.NewFromConfig(cfg)
_, err = stsSvc.GetCallerIdentity(context.Background(), &sts.GetCallerIdentityInput{})
if err != nil {
fmt.Fprintf(os.Stderr, "AWS: Credential check failed: %v\n", err)
os.Exit(1)
}
if appConfig.PermitAPIKey == "" {
fmt.Fprintf(os.Stderr, "Permit.io: PERMIT_KEY environment variable is not set\n")
os.Exit(1)
}
// Use the API key scope endpoint to validate the key - this works for all key types
req, _ := http.NewRequest("GET", appConfig.PermitBaseURL+"/v2/api-key/scope", nil)
req.Header.Add("Authorization", "Bearer "+appConfig.PermitAPIKey)
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Do(req)
if err != nil || resp.StatusCode < 200 || resp.StatusCode >= 300 {
statusCode := 0
if resp != nil {
statusCode = resp.StatusCode
}
fmt.Fprintf(os.Stderr, "Permit.io: Credential check failed: %v, status=%v\n", err, statusCode)
os.Exit(1)
}
resp.Body.Close()
}
// Initialize a Cognito client with the correct profile
func initializeCognitoClient() (*cognitoidentityprovider.Client, error) {
awsProfile := os.Getenv("AWS_PROFILE")
var cfgOpts []func(*config.LoadOptions) error
if awsProfile != "" {
cfgOpts = append(cfgOpts, config.WithSharedConfigProfile(awsProfile))
}
cfg, err := config.LoadDefaultConfig(context.Background(), cfgOpts...)
if err != nil {
return nil, err
}
client := cognitoidentityprovider.NewFromConfig(cfg)
return client, nil
}
func createCognitoUser(ctx context.Context, client *cognitoidentityprovider.Client, config *CognitoConfig, user map[string]string) (*CognitoUserResponse, error) {
// Use email as username
username := user["email"]
// Prepare user attributes
userAttributes := []types.AttributeType{
{
Name: aws.String("email"),
Value: aws.String(user["email"]),
},
{
Name: aws.String("given_name"),
Value: aws.String(user["first_name"]),
},
{
Name: aws.String("family_name"),
Value: aws.String(user["last_name"]),
},
{
Name: aws.String("email_verified"),
Value: aws.String("true"),
},
}
// Prepare the AdminCreateUser input
createUserInput := &cognitoidentityprovider.AdminCreateUserInput{
UserPoolId: aws.String(config.UserPoolID),
Username: aws.String(username),
UserAttributes: userAttributes,
DesiredDeliveryMediums: []types.DeliveryMediumType{types.DeliveryMediumTypeEmail},
}
// Create the user
result, err := client.AdminCreateUser(ctx, createUserInput)
if err != nil {
return nil, err
}
// Extract the subject ID from attributes
var subjectID string
for _, attr := range result.User.Attributes {
if aws.ToString(attr.Name) == "sub" {
subjectID = aws.ToString(attr.Value)
break
}
}
// Build response
response := &CognitoUserResponse{
SubjectID: subjectID,
Username: aws.ToString(result.User.Username),
Email: user["email"],
FirstName: user["first_name"],
LastName: user["last_name"],
Status: string(result.User.UserStatus),
Enabled: result.User.Enabled,
}
return response, nil
}
func deleteCognitoUser(ctx context.Context, client *cognitoidentityprovider.Client, userPoolID, username string) error {
deleteInput := &cognitoidentityprovider.AdminDeleteUserInput{
UserPoolId: aws.String(userPoolID),
Username: aws.String(username),
}
_, err := client.AdminDeleteUser(ctx, deleteInput)
if err != nil {
return err
}
return nil
}
// getCognitoUser retrieves an existing user from Cognito
func getCognitoUser(ctx context.Context, client *cognitoidentityprovider.Client, userPoolID, username string) (*CognitoUserResponse, error) {
input := &cognitoidentityprovider.AdminGetUserInput{
UserPoolId: aws.String(userPoolID),
Username: aws.String(username),
}
result, err := client.AdminGetUser(ctx, input)
if err != nil {
return nil, err
}
// Extract attributes
var subjectID, email, firstName, lastName string
for _, attr := range result.UserAttributes {
switch aws.ToString(attr.Name) {
case "sub":
subjectID = aws.ToString(attr.Value)
case "email":
email = aws.ToString(attr.Value)
case "given_name":
firstName = aws.ToString(attr.Value)
case "family_name":
lastName = aws.ToString(attr.Value)
}
}
return &CognitoUserResponse{
SubjectID: subjectID,
Username: aws.ToString(result.Username),
Email: email,
FirstName: firstName,
LastName: lastName,
Status: string(result.UserStatus),
Enabled: result.Enabled,
}, nil
}
// userExistsInCognito checks if a user exists in Cognito
func userExistsInCognito(ctx context.Context, client *cognitoidentityprovider.Client, userPoolID, username string) bool {
_, err := getCognitoUser(ctx, client, userPoolID, username)
return err == nil
}
// disableCognitoUser disables a user in Cognito
func disableCognitoUser(ctx context.Context, client *cognitoidentityprovider.Client, userPoolID, username string) error {
input := &cognitoidentityprovider.AdminDisableUserInput{
UserPoolId: aws.String(userPoolID),
Username: aws.String(username),
}
_, err := client.AdminDisableUser(ctx, input)
return err
}
// enableCognitoUser enables a user in Cognito
func enableCognitoUser(ctx context.Context, client *cognitoidentityprovider.Client, userPoolID, username string) error {
input := &cognitoidentityprovider.AdminEnableUserInput{
UserPoolId: aws.String(userPoolID),
Username: aws.String(username),
}
_, err := client.AdminEnableUser(ctx, input)
return err
}
// isCognitoUserEnabled checks if a user is enabled in Cognito
func isCognitoUserEnabled(ctx context.Context, client *cognitoidentityprovider.Client, userPoolID, username string) (bool, error) {
user, err := getCognitoUser(ctx, client, userPoolID, username)
if err != nil {
return false, err
}
return user.Enabled, nil
}