Files
query-orchestration/cmd/cognito_test/user.creation.tool/cognito.go
T
Jay Brown daf4d4b94b Merged in feature/permit-io-demo (pull request #144)
draft pr for the test demo harness for permit.io

* harness working

for permit.io demo

* user is const

* aws profile

* permit import code

for research

* cleanup

* create user tool poc

* Merge branch 'main' of bitbucket.org:aarete/query-orchestration into feature/permit-io-demo

* tests for user creation

* add audit logs

and dry-run

* add tests

sanity check the requested roles

* update docs for delete

* disable/enable users

and test

* more tests

and make user name required only for create operations

* clean up

* add docs

* aws tag tests

* audit -> slog

* attempt build fix devbox

* edit

* just devbox

* remove lock change for merge

* Merge branch 'main' of bitbucket.org:aarete/query-orchestration into feature/permit-io-demo
2025-06-13 19:42:24 +00:00

249 lines
6.9 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
}
func sanityCheckCredentials() {
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)
}
permitKey := os.Getenv("PERMIT_KEY")
if permitKey == "" {
fmt.Fprintf(os.Stderr, "Permit.io: PERMIT_KEY environment variable is not set\n")
os.Exit(1)
}
req, _ := http.NewRequest("GET", "https://api.permit.io/v2/projects", nil)
req.Header.Add("Authorization", "Bearer "+permitKey)
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Do(req)
if err != nil || resp.StatusCode < 200 || resp.StatusCode >= 300 {
fmt.Fprintf(os.Stderr, "Permit.io: Credential check failed: %v, status=%v\n", err, resp.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
}