ab7072ac90
permit keys * permit keys use the environment from the api key if requested
259 lines
7.4 KiB
Go
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
|
|
}
|