// adminHandlers.go implements the admin API handlers for user management operations. // These handlers provide REST API access to AWS Cognito and Permit.io user management functions. package queryapi import ( "context" "fmt" "net/http" "os" "time" "queryorchestration/internal/cognitoauth" "queryorchestration/internal/serviceconfig/aws" "queryorchestration/internal/usermanagement" "github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider" "github.com/labstack/echo/v4" "github.com/oapi-codegen/runtime/types" ) // getAdminUserForAudit retrieves the admin user information for audit logging. // When DISABLE_AUTH is set to "true", it returns a default system user for audit purposes. // Otherwise, it attempts to extract user info from the JWT token in the request. // // Parameters: // - ctx: The Echo context containing the HTTP request // // Returns: // - cognitoauth.UserInfo: The admin user information // - error: Error if user info cannot be retrieved and auth is not disabled func getAdminUserForAudit(ctx echo.Context) (cognitoauth.UserInfo, error) { // Try to get user info from JWT token adminUser, ok := cognitoauth.GetUserInfo(ctx) if ok { return adminUser, nil } // If auth is disabled, use a default system user for audit logging // Note: Email must be a valid format (with TLD) to pass openapi_types.Email validation if os.Getenv("DISABLE_AUTH") == "true" { return cognitoauth.UserInfo{ Email: "system@test.local", Username: "system", Groups: []string{"admin"}, }, nil } // Auth is enabled but user info not found - return error return cognitoauth.UserInfo{}, fmt.Errorf("could not identify admin user") } // getPermitConfig creates a PermitConfig by reading environment variables. // This centralizes Permit.io configuration to avoid code duplication. // Note: ProjectID and EnvID are automatically derived from the API key scope // when first needed, so they do not need to be set here. // // Parameters: // - ctrl: The Controllers instance containing the API key from config // // Returns: // - *usermanagement.PermitConfig: The Permit.io configuration func (ctrl *Controllers) getPermitConfig() *usermanagement.PermitConfig { tenant := os.Getenv("PERMIT_IO_TENANT") if tenant == "" { tenant = "default" } baseURL := os.Getenv("PERMIT_IO_BASE_URL") if baseURL == "" { baseURL = "https://api.permit.io" } return &usermanagement.PermitConfig{ APIKey: ctrl.cfg.GetPermitIOAPIKey(), BaseURL: baseURL, DefaultTenant: tenant, } } // CreateAdminUser creates a new user in both AWS Cognito and Permit.io. // POST /admin/users func (ctrl *Controllers) CreateAdminUser(ctx echo.Context) error { // Parse request body var req AdminUserCreate if err := ctx.Bind(&req); err != nil { return ctx.JSON(http.StatusBadRequest, ErrorMessage{ Message: fmt.Sprintf("Invalid request body: %v", err), }) } // Get admin user email from JWT for audit logging adminUser, err := getAdminUserForAudit(ctx) if err != nil { return ctx.JSON(http.StatusUnauthorized, ErrorMessage{ Message: "Could not identify admin user", }) } // Create AWS config and Cognito client awsCfg, err := aws.GetAWSConfig(context.Background()) if err != nil { ctrl.cfg.GetAuthLogger().Error("Failed to load AWS config", "error", err) return ctx.JSON(http.StatusInternalServerError, ErrorMessage{ Message: "Failed to initialize AWS configuration", }) } cognitoClient := cognitoidentityprovider.NewFromConfig(awsCfg) // Prepare Cognito configuration cognitoConfig := &usermanagement.CognitoConfig{ UserPoolID: ctrl.cfg.GetAuthUserPoolID(), Region: ctrl.cfg.GetAuthRegion(), } // Prepare user attributes userAttrs := usermanagement.UserAttributes{ Email: string(req.Email), FirstName: req.FirstName, LastName: req.LastName, } // Check if user already exists existingUser, err := usermanagement.GetCognitoUser(context.Background(), cognitoClient, cognitoConfig.UserPoolID, userAttrs.Email) if err == nil { // User exists - check if attributes match (idempotent) if existingUser.FirstName == userAttrs.FirstName && existingUser.LastName == userAttrs.LastName { // Return 200 OK for idempotent creation usermanagement.LogUserAdminAction( ctrl.cfg.GetAuthLogger(), usermanagement.ActionCreate, usermanagement.SystemCognito, adminUser.Email, userAttrs.Email, existingUser.SubjectID, usermanagement.ResultSuccess, "User already exists with matching attributes (idempotent)", ctx.Response().Header().Get(echo.HeaderXRequestID), ) createdAt := time.Now() return ctx.JSON(http.StatusOK, AdminUserCreateResponse{ CognitoSubjectId: existingUser.SubjectID, Email: types.Email(existingUser.Email), FirstName: &existingUser.FirstName, LastName: &existingUser.LastName, Status: existingUser.Status, Enabled: &existingUser.Enabled, PermitSynced: true, CreatedAt: &createdAt, }) } // User exists but attributes differ - conflict usermanagement.LogUserAdminAction( ctrl.cfg.GetAuthLogger(), usermanagement.ActionCreate, usermanagement.SystemCognito, adminUser.Email, userAttrs.Email, existingUser.SubjectID, usermanagement.ResultFailure, "User already exists with different attributes", ctx.Response().Header().Get(echo.HeaderXRequestID), ) return ctx.JSON(http.StatusConflict, ErrorMessage{ Message: "User already exists with different attributes", }) } // Create user in Cognito cognitoUser, err := usermanagement.CreateCognitoUser(context.Background(), cognitoClient, cognitoConfig, userAttrs) if err != nil { ctrl.cfg.GetAuthLogger().Error("Failed to create user in Cognito", "error", err, "email", userAttrs.Email) usermanagement.LogUserAdminAction( ctrl.cfg.GetAuthLogger(), usermanagement.ActionCreate, usermanagement.SystemCognito, adminUser.Email, userAttrs.Email, "", usermanagement.ResultFailure, fmt.Sprintf("Cognito creation failed: %v", err), ctx.Response().Header().Get(echo.HeaderXRequestID), ) return ctx.JSON(http.StatusBadGateway, ErrorMessage{ Message: fmt.Sprintf("Failed to create user in Cognito: %v", err), }) } // Log successful Cognito creation usermanagement.LogUserAdminAction( ctrl.cfg.GetAuthLogger(), usermanagement.ActionCreate, usermanagement.SystemCognito, adminUser.Email, userAttrs.Email, cognitoUser.SubjectID, usermanagement.ResultSuccess, "User created successfully in Cognito", ctx.Response().Header().Get(echo.HeaderXRequestID), ) // Create user in Permit.io httpClient := &http.Client{Timeout: 10 * time.Second} permitConfig := ctrl.getPermitConfig() permitSynced := false err = usermanagement.CreatePermitUser(httpClient, permitConfig, cognitoUser) if err != nil { ctrl.cfg.GetAuthLogger().Error("Failed to create user in Permit.io", "error", err, "email", userAttrs.Email) usermanagement.LogUserAdminAction( ctrl.cfg.GetAuthLogger(), usermanagement.ActionCreate, usermanagement.SystemPermit, adminUser.Email, userAttrs.Email, cognitoUser.SubjectID, usermanagement.ResultFailure, fmt.Sprintf("Permit.io creation failed: %v", err), ctx.Response().Header().Get(echo.HeaderXRequestID), ) } else { permitSynced = true usermanagement.LogUserAdminAction( ctrl.cfg.GetAuthLogger(), usermanagement.ActionCreate, usermanagement.SystemPermit, adminUser.Email, userAttrs.Email, cognitoUser.SubjectID, usermanagement.ResultSuccess, "User created successfully in Permit.io", ctx.Response().Header().Get(echo.HeaderXRequestID), ) } // Assign roles in Permit.io var rolesAssigned []string if permitSynced { for _, role := range req.Roles { err = usermanagement.AssignRole(httpClient, permitConfig, cognitoUser.SubjectID, role) if err != nil { ctrl.cfg.GetAuthLogger().Error("Failed to assign role", "error", err, "role", role, "user", userAttrs.Email) usermanagement.LogRoleAdminAction( ctrl.cfg.GetAuthLogger(), usermanagement.ActionAssignRole, adminUser.Email, userAttrs.Email, role, usermanagement.ResultFailure, fmt.Sprintf("Role assignment failed: %v", err), ctx.Response().Header().Get(echo.HeaderXRequestID), ) } else { rolesAssigned = append(rolesAssigned, role) usermanagement.LogRoleAdminAction( ctrl.cfg.GetAuthLogger(), usermanagement.ActionAssignRole, adminUser.Email, userAttrs.Email, role, usermanagement.ResultSuccess, "Role assigned successfully", ctx.Response().Header().Get(echo.HeaderXRequestID), ) } } } // Build response createdAt := time.Now() response := AdminUserCreateResponse{ CognitoSubjectId: cognitoUser.SubjectID, Email: types.Email(cognitoUser.Email), FirstName: &cognitoUser.FirstName, LastName: &cognitoUser.LastName, Status: cognitoUser.Status, Enabled: &cognitoUser.Enabled, PermitSynced: permitSynced, CreatedAt: &createdAt, } if len(rolesAssigned) > 0 { response.RolesAssigned = &rolesAssigned } return ctx.JSON(http.StatusCreated, response) } // GetAdminUser retrieves user details from both AWS Cognito and Permit.io. // GET /admin/users/{email} func (ctrl *Controllers) GetAdminUser(ctx echo.Context, email UserEmail) error { // Get admin user email from JWT for audit logging adminUser, err := getAdminUserForAudit(ctx) if err != nil { return ctx.JSON(http.StatusUnauthorized, ErrorMessage{ Message: "Could not identify admin user", }) } // Create AWS config and Cognito client awsCfg, err := aws.GetAWSConfig(context.Background()) if err != nil { ctrl.cfg.GetAuthLogger().Error("Failed to load AWS config", "error", err) return ctx.JSON(http.StatusInternalServerError, ErrorMessage{ Message: "Failed to initialize AWS configuration", }) } cognitoClient := cognitoidentityprovider.NewFromConfig(awsCfg) // Prepare Cognito configuration cognitoConfig := &usermanagement.CognitoConfig{ UserPoolID: ctrl.cfg.GetAuthUserPoolID(), Region: ctrl.cfg.GetAuthRegion(), } // Get user from Cognito cognitoUser, err := usermanagement.GetCognitoUser(context.Background(), cognitoClient, cognitoConfig.UserPoolID, string(email)) if err != nil { ctrl.cfg.GetAuthLogger().Error("Failed to get user from Cognito", "error", err, "email", email) usermanagement.LogUserAdminAction( ctrl.cfg.GetAuthLogger(), usermanagement.ActionUpdate, usermanagement.SystemCognito, adminUser.Email, string(email), "", usermanagement.ResultFailure, fmt.Sprintf("User not found in Cognito: %v", err), ctx.Response().Header().Get(echo.HeaderXRequestID), ) return ctx.JSON(http.StatusNotFound, ErrorMessage{ Message: fmt.Sprintf("User not found: %s", email), }) } // Get user roles from Permit.io httpClient := &http.Client{Timeout: 10 * time.Second} permitConfig := ctrl.getPermitConfig() roles, err := usermanagement.GetUserRoles(httpClient, permitConfig, cognitoUser.SubjectID) if err != nil { ctrl.cfg.GetAuthLogger().Warn("Failed to get user roles from Permit.io", "error", err, "email", email) } // Log successful retrieval usermanagement.LogUserListAction( ctrl.cfg.GetAuthLogger(), adminUser.Email, fmt.Sprintf("email=%s", email), 1, ctx.Response().Header().Get(echo.HeaderXRequestID), ) // Build response response := AdminUserDetails{ CognitoSubjectId: cognitoUser.SubjectID, Email: types.Email(cognitoUser.Email), FirstName: &cognitoUser.FirstName, LastName: &cognitoUser.LastName, Status: cognitoUser.Status, Enabled: cognitoUser.Enabled, } if len(roles) > 0 { response.Roles = &roles } return ctx.JSON(http.StatusOK, response) } // CheckAdminUserExists checks if a user exists in Cognito and/or Permit.io. // HEAD /admin/users/{email} func (ctrl *Controllers) CheckAdminUserExists(ctx echo.Context, email UserEmail) error { // Create AWS config and Cognito client awsCfg, err := aws.GetAWSConfig(context.Background()) if err != nil { ctrl.cfg.GetAuthLogger().Error("Failed to load AWS config", "error", err) return ctx.NoContent(http.StatusInternalServerError) } cognitoClient := cognitoidentityprovider.NewFromConfig(awsCfg) // Prepare Cognito configuration cognitoConfig := &usermanagement.CognitoConfig{ UserPoolID: ctrl.cfg.GetAuthUserPoolID(), Region: ctrl.cfg.GetAuthRegion(), } // Check if user exists in Cognito exists := usermanagement.UserExistsInCognito(context.Background(), cognitoClient, cognitoConfig.UserPoolID, string(email)) if !exists { return ctx.NoContent(http.StatusNotFound) } return ctx.NoContent(http.StatusOK) } // ListAdminUsers lists users from AWS Cognito with pagination, filtering, and sorting. // GET /admin/users func (ctrl *Controllers) ListAdminUsers(ctx echo.Context, params ListAdminUsersParams) error { // Get admin user email from JWT for audit logging adminUser, err := getAdminUserForAudit(ctx) if err != nil { return ctx.JSON(http.StatusUnauthorized, ErrorMessage{ Message: "Could not identify admin user", }) } // Create AWS config and Cognito client awsCfg, err := aws.GetAWSConfig(context.Background()) if err != nil { ctrl.cfg.GetAuthLogger().Error("Failed to load AWS config", "error", err) return ctx.JSON(http.StatusInternalServerError, ErrorMessage{ Message: "Failed to initialize AWS configuration", }) } cognitoClient := cognitoidentityprovider.NewFromConfig(awsCfg) // Prepare Cognito configuration cognitoConfig := &usermanagement.CognitoConfig{ UserPoolID: ctrl.cfg.GetAuthUserPoolID(), Region: ctrl.cfg.GetAuthRegion(), } // Set default pagination parameters page := int32(1) pageSize := int32(50) if params.Page != nil && *params.Page > 0 { page = *params.Page } if params.PageSize != nil && *params.PageSize > 0 && *params.PageSize <= 60 { pageSize = *params.PageSize } // List users from Cognito (note: Cognito doesn't support traditional page numbers, only pagination tokens) // For simplicity, we'll just return the first page result, err := usermanagement.ListCognitoUsers(context.Background(), cognitoClient, cognitoConfig.UserPoolID, int(pageSize), nil) if err != nil { ctrl.cfg.GetAuthLogger().Error("Failed to list users from Cognito", "error", err) return ctx.JSON(http.StatusBadGateway, ErrorMessage{ Message: fmt.Sprintf("Failed to list users from Cognito: %v", err), }) } // Convert to AdminUserDetails format users := make([]AdminUserDetails, 0, len(result.Users)) for _, cognitoUser := range result.Users { userDetail := AdminUserDetails{ CognitoSubjectId: cognitoUser.SubjectID, Email: types.Email(cognitoUser.Email), FirstName: &cognitoUser.FirstName, LastName: &cognitoUser.LastName, Status: cognitoUser.Status, Enabled: cognitoUser.Enabled, } users = append(users, userDetail) } // Log list action usermanagement.LogUserListAction( ctrl.cfg.GetAuthLogger(), adminUser.Email, fmt.Sprintf("page=%d,page_size=%d", page, pageSize), len(users), ctx.Response().Header().Get(echo.HeaderXRequestID), ) // Build response hasMore := result.PaginationKey != nil && *result.PaginationKey != "" // len(users) is bounded by pageSize (max 60), so this conversion is safe total := int32(len(users)) // #nosec G115 -- len(users) is bounded by pageSize which is max 60 response := AdminUserList{ Users: users, Total: total, Page: page, PageSize: pageSize, HasMore: &hasMore, } return ctx.JSON(http.StatusOK, response) } // updateUserAttributesInCognito updates user attributes in Cognito and logs the operation. func (ctrl *Controllers) updateUserAttributesInCognito(ctx echo.Context, cognitoClient *cognitoidentityprovider.Client, cognitoConfig *usermanagement.CognitoConfig, email UserEmail, req AdminUserUpdate, adminUser cognitoauth.UserInfo, cognitoUser *usermanagement.CognitoUserResponse) error { if req.FirstName == nil && req.LastName == nil { return nil } firstName := "" if req.FirstName != nil { firstName = *req.FirstName } lastName := "" if req.LastName != nil { lastName = *req.LastName } userAttrs := usermanagement.UserAttributes{ Email: string(email), FirstName: firstName, LastName: lastName, } err := usermanagement.UpdateCognitoUserAttributes(context.Background(), cognitoClient, cognitoConfig.UserPoolID, string(email), userAttrs) if err != nil { ctrl.cfg.GetAuthLogger().Error("Failed to update user in Cognito", "error", err, "email", email) usermanagement.LogUserAdminAction( ctrl.cfg.GetAuthLogger(), usermanagement.ActionUpdate, usermanagement.SystemCognito, adminUser.Email, string(email), cognitoUser.SubjectID, usermanagement.ResultFailure, fmt.Sprintf("Cognito update failed: %v", err), ctx.Response().Header().Get(echo.HeaderXRequestID), ) return err } usermanagement.LogUserAdminAction( ctrl.cfg.GetAuthLogger(), usermanagement.ActionUpdate, usermanagement.SystemCognito, adminUser.Email, string(email), cognitoUser.SubjectID, usermanagement.ResultSuccess, "User attributes updated successfully in Cognito", ctx.Response().Header().Get(echo.HeaderXRequestID), ) if req.FirstName != nil { cognitoUser.FirstName = *req.FirstName } if req.LastName != nil { cognitoUser.LastName = *req.LastName } return nil } // updateUserRolesInPermit updates user roles in Permit.io and logs each operation. func (ctrl *Controllers) updateUserRolesInPermit(ctx echo.Context, email UserEmail, req AdminUserUpdate, adminUser cognitoauth.UserInfo, subjectID string, httpClient *http.Client, permitConfig *usermanagement.PermitConfig) { if req.Roles == nil || len(*req.Roles) == 0 { return } currentRoles, err := usermanagement.GetUserRoles(httpClient, permitConfig, subjectID) if err != nil { ctrl.cfg.GetAuthLogger().Warn("Failed to get current user roles from Permit.io", "error", err, "email", email) currentRoles = []string{} } requestedRoles := *req.Roles currentRolesMap := make(map[string]bool) for _, role := range currentRoles { currentRolesMap[role] = true } requestedRolesMap := make(map[string]bool) for _, role := range requestedRoles { requestedRolesMap[role] = true } // Add new roles for _, role := range requestedRoles { if !currentRolesMap[role] { err = usermanagement.AssignRole(httpClient, permitConfig, subjectID, role) if err != nil { ctrl.cfg.GetAuthLogger().Error("Failed to assign role", "error", err, "role", role, "user", email) usermanagement.LogRoleAdminAction( ctrl.cfg.GetAuthLogger(), usermanagement.ActionAssignRole, adminUser.Email, string(email), role, usermanagement.ResultFailure, fmt.Sprintf("Role assignment failed: %v", err), ctx.Response().Header().Get(echo.HeaderXRequestID), ) } else { usermanagement.LogRoleAdminAction( ctrl.cfg.GetAuthLogger(), usermanagement.ActionAssignRole, adminUser.Email, string(email), role, usermanagement.ResultSuccess, "Role assigned successfully", ctx.Response().Header().Get(echo.HeaderXRequestID), ) } } } // Remove old roles for _, role := range currentRoles { if !requestedRolesMap[role] { err = usermanagement.UnassignRole(httpClient, permitConfig, subjectID, role) if err != nil { ctrl.cfg.GetAuthLogger().Error("Failed to unassign role", "error", err, "role", role, "user", email) usermanagement.LogRoleAdminAction( ctrl.cfg.GetAuthLogger(), usermanagement.ActionUnassignRole, adminUser.Email, string(email), role, usermanagement.ResultFailure, fmt.Sprintf("Role unassignment failed: %v", err), ctx.Response().Header().Get(echo.HeaderXRequestID), ) } else { usermanagement.LogRoleAdminAction( ctrl.cfg.GetAuthLogger(), usermanagement.ActionUnassignRole, adminUser.Email, string(email), role, usermanagement.ResultSuccess, "Role unassigned successfully", ctx.Response().Header().Get(echo.HeaderXRequestID), ) } } } } // UpdateAdminUser updates user attributes in Cognito (first_name, last_name) and manages roles in Permit.io. // PATCH /admin/users/{email} func (ctrl *Controllers) UpdateAdminUser(ctx echo.Context, email UserEmail) error { // Parse request body var req AdminUserUpdate if err := ctx.Bind(&req); err != nil { return ctx.JSON(http.StatusBadRequest, ErrorMessage{ Message: fmt.Sprintf("Invalid request body: %v", err), }) } // Get admin user email from JWT for audit logging adminUser, err := getAdminUserForAudit(ctx) if err != nil { return ctx.JSON(http.StatusUnauthorized, ErrorMessage{ Message: "Could not identify admin user", }) } // Create AWS config and Cognito client awsCfg, err := aws.GetAWSConfig(context.Background()) if err != nil { ctrl.cfg.GetAuthLogger().Error("Failed to load AWS config", "error", err) return ctx.JSON(http.StatusInternalServerError, ErrorMessage{ Message: "Failed to initialize AWS configuration", }) } cognitoClient := cognitoidentityprovider.NewFromConfig(awsCfg) // Prepare Cognito configuration cognitoConfig := &usermanagement.CognitoConfig{ UserPoolID: ctrl.cfg.GetAuthUserPoolID(), Region: ctrl.cfg.GetAuthRegion(), } // Get user from Cognito to verify existence and get Subject ID cognitoUser, err := usermanagement.GetCognitoUser(context.Background(), cognitoClient, cognitoConfig.UserPoolID, string(email)) if err != nil { ctrl.cfg.GetAuthLogger().Error("Failed to get user from Cognito", "error", err, "email", email) usermanagement.LogUserAdminAction( ctrl.cfg.GetAuthLogger(), usermanagement.ActionUpdate, usermanagement.SystemCognito, adminUser.Email, string(email), "", usermanagement.ResultFailure, fmt.Sprintf("User not found in Cognito: %v", err), ctx.Response().Header().Get(echo.HeaderXRequestID), ) return ctx.JSON(http.StatusNotFound, ErrorMessage{ Message: fmt.Sprintf("User not found: %s", email), }) } // Update user attributes in Cognito if provided err = ctrl.updateUserAttributesInCognito(ctx, cognitoClient, cognitoConfig, email, req, adminUser, cognitoUser) if err != nil { return ctx.JSON(http.StatusBadGateway, ErrorMessage{ Message: fmt.Sprintf("Failed to update user in Cognito: %v", err), }) } // Handle role updates in Permit.io if provided httpClient := &http.Client{Timeout: 10 * time.Second} permitConfig := ctrl.getPermitConfig() ctrl.updateUserRolesInPermit(ctx, email, req, adminUser, cognitoUser.SubjectID, httpClient, permitConfig) // Get updated user from Cognito updatedUser, err := usermanagement.GetCognitoUser(context.Background(), cognitoClient, cognitoConfig.UserPoolID, string(email)) if err != nil { ctrl.cfg.GetAuthLogger().Warn("Failed to get updated user from Cognito", "error", err, "email", email) updatedUser = cognitoUser } // Get updated roles updatedRoles, err := usermanagement.GetUserRoles(httpClient, permitConfig, cognitoUser.SubjectID) if err != nil { ctrl.cfg.GetAuthLogger().Warn("Failed to get updated user roles from Permit.io", "error", err, "email", email) } // Build response response := AdminUserDetails{ CognitoSubjectId: updatedUser.SubjectID, Email: types.Email(updatedUser.Email), FirstName: &updatedUser.FirstName, LastName: &updatedUser.LastName, Status: updatedUser.Status, Enabled: updatedUser.Enabled, } if len(updatedRoles) > 0 { response.Roles = &updatedRoles } return ctx.JSON(http.StatusOK, response) } // DeleteAdminUser permanently deletes a user from both AWS Cognito and Permit.io. // DELETE /admin/users/{email} func (ctrl *Controllers) DeleteAdminUser(ctx echo.Context, email UserEmail, params DeleteAdminUserParams) error { if !params.Confirm { return ctx.JSON(http.StatusBadRequest, ErrorMessage{ Message: "confirm=true query parameter is required to delete a user", }) } // Get admin user email from JWT for audit logging adminUser, err := getAdminUserForAudit(ctx) if err != nil { return ctx.JSON(http.StatusUnauthorized, ErrorMessage{ Message: "Could not identify admin user", }) } // Create AWS config and Cognito client awsCfg, err := aws.GetAWSConfig(context.Background()) if err != nil { ctrl.cfg.GetAuthLogger().Error("Failed to load AWS config", "error", err) return ctx.JSON(http.StatusInternalServerError, ErrorMessage{ Message: "Failed to initialize AWS configuration", }) } cognitoClient := cognitoidentityprovider.NewFromConfig(awsCfg) // Prepare Cognito configuration cognitoConfig := &usermanagement.CognitoConfig{ UserPoolID: ctrl.cfg.GetAuthUserPoolID(), Region: ctrl.cfg.GetAuthRegion(), } // Get user from Cognito to verify existence and get Subject ID cognitoUser, err := usermanagement.GetCognitoUser(context.Background(), cognitoClient, cognitoConfig.UserPoolID, string(email)) if err != nil { ctrl.cfg.GetAuthLogger().Error("Failed to get user from Cognito", "error", err, "email", email) usermanagement.LogUserAdminAction( ctrl.cfg.GetAuthLogger(), usermanagement.ActionDelete, usermanagement.SystemCognito, adminUser.Email, string(email), "", usermanagement.ResultFailure, fmt.Sprintf("User not found in Cognito: %v", err), ctx.Response().Header().Get(echo.HeaderXRequestID), ) return ctx.JSON(http.StatusNotFound, ErrorMessage{ Message: fmt.Sprintf("User not found: %s", email), }) } // Delete user from Cognito cognitoDeleted := false err = usermanagement.DeleteCognitoUser(context.Background(), cognitoClient, cognitoConfig.UserPoolID, string(email)) if err != nil { ctrl.cfg.GetAuthLogger().Error("Failed to delete user from Cognito", "error", err, "email", email) usermanagement.LogUserAdminAction( ctrl.cfg.GetAuthLogger(), usermanagement.ActionDelete, usermanagement.SystemCognito, adminUser.Email, string(email), cognitoUser.SubjectID, usermanagement.ResultFailure, fmt.Sprintf("Cognito deletion failed: %v", err), ctx.Response().Header().Get(echo.HeaderXRequestID), ) } else { cognitoDeleted = true usermanagement.LogUserAdminAction( ctrl.cfg.GetAuthLogger(), usermanagement.ActionDelete, usermanagement.SystemCognito, adminUser.Email, string(email), cognitoUser.SubjectID, usermanagement.ResultSuccess, "User deleted successfully from Cognito", ctx.Response().Header().Get(echo.HeaderXRequestID), ) } // Delete user from Permit.io httpClient := &http.Client{Timeout: 10 * time.Second} permitConfig := ctrl.getPermitConfig() permitDeleted := false err = usermanagement.DeletePermitUser(httpClient, permitConfig, cognitoUser.SubjectID) if err != nil { ctrl.cfg.GetAuthLogger().Error("Failed to delete user from Permit.io", "error", err, "email", email) usermanagement.LogUserAdminAction( ctrl.cfg.GetAuthLogger(), usermanagement.ActionDelete, usermanagement.SystemPermit, adminUser.Email, string(email), cognitoUser.SubjectID, usermanagement.ResultFailure, fmt.Sprintf("Permit.io deletion failed: %v", err), ctx.Response().Header().Get(echo.HeaderXRequestID), ) } else { permitDeleted = true usermanagement.LogUserAdminAction( ctrl.cfg.GetAuthLogger(), usermanagement.ActionDelete, usermanagement.SystemPermit, adminUser.Email, string(email), cognitoUser.SubjectID, usermanagement.ResultSuccess, "User deleted successfully from Permit.io", ctx.Response().Header().Get(echo.HeaderXRequestID), ) } // Build response response := AdminUserDeleteResponse{ Success: cognitoDeleted, // Success based on Cognito deletion (primary auth system) CognitoSubjectId: &cognitoUser.SubjectID, Email: types.Email(string(email)), } response.DeletedFrom.Cognito = cognitoDeleted response.DeletedFrom.Permit = permitDeleted return ctx.JSON(http.StatusOK, response) } // DisableAdminUser disables a user in AWS Cognito, preventing authentication. // POST /admin/users/{email}/disable func (ctrl *Controllers) DisableAdminUser(ctx echo.Context, email UserEmail) error { // Get admin user email from JWT for audit logging adminUser, err := getAdminUserForAudit(ctx) if err != nil { return ctx.JSON(http.StatusUnauthorized, ErrorMessage{ Message: "Could not identify admin user", }) } // Create AWS config and Cognito client awsCfg, err := aws.GetAWSConfig(context.Background()) if err != nil { ctrl.cfg.GetAuthLogger().Error("Failed to load AWS config", "error", err) return ctx.JSON(http.StatusInternalServerError, ErrorMessage{ Message: "Failed to initialize AWS configuration", }) } cognitoClient := cognitoidentityprovider.NewFromConfig(awsCfg) // Prepare Cognito configuration cognitoConfig := &usermanagement.CognitoConfig{ UserPoolID: ctrl.cfg.GetAuthUserPoolID(), Region: ctrl.cfg.GetAuthRegion(), } // Get user from Cognito to verify existence and get Subject ID cognitoUser, err := usermanagement.GetCognitoUser(context.Background(), cognitoClient, cognitoConfig.UserPoolID, string(email)) if err != nil { ctrl.cfg.GetAuthLogger().Error("Failed to get user from Cognito", "error", err, "email", email) usermanagement.LogUserAdminAction( ctrl.cfg.GetAuthLogger(), usermanagement.ActionDisable, usermanagement.SystemCognito, adminUser.Email, string(email), "", usermanagement.ResultFailure, fmt.Sprintf("User not found in Cognito: %v", err), ctx.Response().Header().Get(echo.HeaderXRequestID), ) return ctx.JSON(http.StatusNotFound, ErrorMessage{ Message: fmt.Sprintf("User not found: %s", email), }) } // Disable user in Cognito err = usermanagement.DisableCognitoUser(context.Background(), cognitoClient, cognitoConfig.UserPoolID, string(email)) if err != nil { ctrl.cfg.GetAuthLogger().Error("Failed to disable user in Cognito", "error", err, "email", email) usermanagement.LogUserAdminAction( ctrl.cfg.GetAuthLogger(), usermanagement.ActionDisable, usermanagement.SystemCognito, adminUser.Email, string(email), cognitoUser.SubjectID, usermanagement.ResultFailure, fmt.Sprintf("Cognito disable failed: %v", err), ctx.Response().Header().Get(echo.HeaderXRequestID), ) return ctx.JSON(http.StatusBadGateway, ErrorMessage{ Message: fmt.Sprintf("Failed to disable user in Cognito: %v", err), }) } // Log successful disable usermanagement.LogUserAdminAction( ctrl.cfg.GetAuthLogger(), usermanagement.ActionDisable, usermanagement.SystemCognito, adminUser.Email, string(email), cognitoUser.SubjectID, usermanagement.ResultSuccess, "User disabled successfully in Cognito", ctx.Response().Header().Get(echo.HeaderXRequestID), ) // Build response timestamp := time.Now() response := AdminUserActionResponse{ Action: Disable, CognitoSubjectId: &cognitoUser.SubjectID, Email: types.Email(string(email)), Success: true, Timestamp: ×tamp, } return ctx.JSON(http.StatusOK, response) } // EnableAdminUser re-enables a previously disabled user in AWS Cognito. // POST /admin/users/{email}/enable func (ctrl *Controllers) EnableAdminUser(ctx echo.Context, email UserEmail) error { // Get admin user email from JWT for audit logging adminUser, err := getAdminUserForAudit(ctx) if err != nil { return ctx.JSON(http.StatusUnauthorized, ErrorMessage{ Message: "Could not identify admin user", }) } // Create AWS config and Cognito client awsCfg, err := aws.GetAWSConfig(context.Background()) if err != nil { ctrl.cfg.GetAuthLogger().Error("Failed to load AWS config", "error", err) return ctx.JSON(http.StatusInternalServerError, ErrorMessage{ Message: "Failed to initialize AWS configuration", }) } cognitoClient := cognitoidentityprovider.NewFromConfig(awsCfg) // Prepare Cognito configuration cognitoConfig := &usermanagement.CognitoConfig{ UserPoolID: ctrl.cfg.GetAuthUserPoolID(), Region: ctrl.cfg.GetAuthRegion(), } // Get user from Cognito to verify existence and get Subject ID cognitoUser, err := usermanagement.GetCognitoUser(context.Background(), cognitoClient, cognitoConfig.UserPoolID, string(email)) if err != nil { ctrl.cfg.GetAuthLogger().Error("Failed to get user from Cognito", "error", err, "email", email) usermanagement.LogUserAdminAction( ctrl.cfg.GetAuthLogger(), usermanagement.ActionEnable, usermanagement.SystemCognito, adminUser.Email, string(email), "", usermanagement.ResultFailure, fmt.Sprintf("User not found in Cognito: %v", err), ctx.Response().Header().Get(echo.HeaderXRequestID), ) return ctx.JSON(http.StatusNotFound, ErrorMessage{ Message: fmt.Sprintf("User not found: %s", email), }) } // Enable user in Cognito err = usermanagement.EnableCognitoUser(context.Background(), cognitoClient, cognitoConfig.UserPoolID, string(email)) if err != nil { ctrl.cfg.GetAuthLogger().Error("Failed to enable user in Cognito", "error", err, "email", email) usermanagement.LogUserAdminAction( ctrl.cfg.GetAuthLogger(), usermanagement.ActionEnable, usermanagement.SystemCognito, adminUser.Email, string(email), cognitoUser.SubjectID, usermanagement.ResultFailure, fmt.Sprintf("Cognito enable failed: %v", err), ctx.Response().Header().Get(echo.HeaderXRequestID), ) return ctx.JSON(http.StatusBadGateway, ErrorMessage{ Message: fmt.Sprintf("Failed to enable user in Cognito: %v", err), }) } // Log successful enable usermanagement.LogUserAdminAction( ctrl.cfg.GetAuthLogger(), usermanagement.ActionEnable, usermanagement.SystemCognito, adminUser.Email, string(email), cognitoUser.SubjectID, usermanagement.ResultSuccess, "User enabled successfully in Cognito", ctx.Response().Header().Get(echo.HeaderXRequestID), ) // Build response timestamp := time.Now() response := AdminUserActionResponse{ Action: Enable, CognitoSubjectId: &cognitoUser.SubjectID, Email: types.Email(string(email)), Success: true, Timestamp: ×tamp, } return ctx.JSON(http.StatusOK, response) }