Files
query-orchestration/internal/usermanagement/permitio.go
T
Jacob Mathison e3d9047143 Merged in jmathison/DEVOPS-722 (pull request #226)
DEVOPS-722 enforce team management visibility

* DEVOPS-722 enforce team management visibility

* DEVOPS-722 reduce admin user list complexity

* DEVOPS-722 cover admin access helpers

* DEVOPS-722 address team management PR feedback

* DEVOPS-722 fix admin access tests in CI

* DEVOPS-722 fix remaining admin PR feedback
2026-05-12 16:49:12 +00:00

876 lines
28 KiB
Go

// permitio.go handles Permit.io API operations for user management and role-based access control (RBAC).
// This file provides functions to create, delete, search, and manage users in Permit.io, as well as
// assign/unassign roles and validate role definitions.
package usermanagement
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strings"
)
const (
permitRoleAssignmentsPerPage = 100
maxPermitRoleAssignmentPages = 100
)
// CreatePermitUser creates a new user in Permit.io using data from a Cognito user.
// The Cognito Subject ID is used as the Permit.io user key for consistency across systems.
//
// Parameters:
// - client: HTTP client for making API requests
// - config: Permit.io configuration containing API credentials and project details
// - cognitoUser: The Cognito user response containing user details and Subject ID
//
// Returns:
// - error: Error if the user creation fails. Returns nil if user is created or already exists.
func CreatePermitUser(client *http.Client, config *PermitConfig, cognitoUser *CognitoUserResponse) error {
projectID, err := config.GetProjectID(client)
if err != nil {
return fmt.Errorf("failed to get project ID: %w", err)
}
envID, err := config.GetEnvID(client)
if err != nil {
return fmt.Errorf("failed to get environment ID: %w", err)
}
endpoint := fmt.Sprintf("%s/v2/facts/%s/%s/users", config.BaseURL, projectID, envID)
attrs := map[string]interface{}{
"cognito_username": cognitoUser.Username,
"cognito_status": cognitoUser.Status,
}
if cognitoUser.EnvironmentID != "" {
attrs["environment_id"] = cognitoUser.EnvironmentID
}
userObj := PermitUser{
Key: cognitoUser.SubjectID, // Use Cognito Subject ID as the key
Email: cognitoUser.Email,
FirstName: cognitoUser.FirstName,
LastName: cognitoUser.LastName,
Attributes: attrs,
}
jsonData, err := json.Marshal(userObj)
if err != nil {
return fmt.Errorf("error marshaling user data: %w", err)
}
req, err := http.NewRequest("POST", endpoint, bytes.NewBuffer(jsonData))
if err != nil {
return fmt.Errorf("error creating request: %w", err)
}
req.Header.Set("Authorization", "Bearer "+config.APIKey)
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("error making request: %w", err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
// If we get a 404, provide helpful debugging info
if resp.StatusCode == http.StatusNotFound {
var errResp map[string]interface{}
if err := json.Unmarshal(body, &errResp); err == nil {
return fmt.Errorf("API error (status 404): %v", errResp["message"])
}
}
if resp.StatusCode == http.StatusConflict || resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusCreated {
// User already exists or was created successfully
return nil
}
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
var errResp map[string]interface{}
if err := json.Unmarshal(body, &errResp); err == nil {
if errorObj, ok := errResp["error"].(map[string]interface{}); ok {
if msg, ok := errorObj["message"].(string); ok {
return fmt.Errorf("API error (status %d): %s", resp.StatusCode, msg)
}
}
}
return fmt.Errorf("API error (status %d): %s", resp.StatusCode, string(body))
}
return nil
}
// GetPermitUser retrieves a user from Permit.io by their user key, returning the full user
// object including custom attributes. This is useful for verifying that attributes like
// environment_id were correctly stored when the user was created.
//
// Parameters:
// - client: HTTP client for making API requests
// - config: Permit.io configuration containing API credentials and project details
// - userKey: The Permit.io user key (typically Cognito SubjectID)
//
// Returns:
// - *PermitUser: The full user object including attributes
// - error: Error if the retrieval fails or user is not found
func GetPermitUser(client *http.Client, config *PermitConfig, userKey string) (*PermitUser, error) {
projectID, err := config.GetProjectID(client)
if err != nil {
return nil, fmt.Errorf("failed to get project ID: %w", err)
}
envID, err := config.GetEnvID(client)
if err != nil {
return nil, fmt.Errorf("failed to get environment ID: %w", err)
}
endpoint := fmt.Sprintf("%s/v2/facts/%s/%s/users/%s", config.BaseURL, projectID, envID, userKey)
req, err := http.NewRequest("GET", endpoint, nil)
if err != nil {
return nil, fmt.Errorf("error creating request: %w", err)
}
req.Header.Set("Authorization", "Bearer "+config.APIKey)
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("error making request: %w", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("error reading response: %w", err)
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("API error (status %d): %s", resp.StatusCode, string(body))
}
var user PermitUser
if err := json.Unmarshal(body, &user); err != nil {
return nil, fmt.Errorf("error parsing response: %w", err)
}
return &user, nil
}
// DeletePermitUser permanently deletes a user from Permit.io by their user key.
//
// Parameters:
// - client: HTTP client for making API requests
// - config: Permit.io configuration containing API credentials and project details
// - userKey: The Permit.io user key (typically Cognito Subject ID)
//
// Returns:
// - error: Error if the deletion fails
func DeletePermitUser(client *http.Client, config *PermitConfig, userKey string) error {
projectID, err := config.GetProjectID(client)
if err != nil {
return fmt.Errorf("failed to get project ID: %w", err)
}
envID, err := config.GetEnvID(client)
if err != nil {
return fmt.Errorf("failed to get environment ID: %w", err)
}
endpoint := fmt.Sprintf("%s/v2/facts/%s/%s/users/%s", config.BaseURL, projectID, envID, userKey)
req, err := http.NewRequest("DELETE", endpoint, nil)
if err != nil {
return fmt.Errorf("error creating request: %w", err)
}
req.Header.Set("Authorization", "Bearer "+config.APIKey)
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("error making request: %w", err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent {
var errResp map[string]interface{}
if err := json.Unmarshal(body, &errResp); err == nil {
if errorObj, ok := errResp["error"].(map[string]interface{}); ok {
if msg, ok := errorObj["message"].(string); ok {
return fmt.Errorf("API error (status %d): %s", resp.StatusCode, msg)
}
}
}
return fmt.Errorf("API error (status %d): %s", resp.StatusCode, string(body))
}
return nil
}
// FindPermitUserByEmail searches for a user in Permit.io by their email address.
// Returns the user's key (typically Cognito Subject ID) if found.
//
// Parameters:
// - client: HTTP client for making API requests
// - config: Permit.io configuration containing API credentials and project details
// - email: The email address to search for
//
// Returns:
// - string: The Permit.io user key if found, empty string if not found
// - error: Error if the search fails
func FindPermitUserByEmail(client *http.Client, config *PermitConfig, email string) (string, error) {
projectID, err := config.GetProjectID(client)
if err != nil {
return "", fmt.Errorf("failed to get project ID: %w", err)
}
envID, err := config.GetEnvID(client)
if err != nil {
return "", fmt.Errorf("failed to get environment ID: %w", err)
}
// Search for user by email
endpoint := fmt.Sprintf("%s/v2/facts/%s/%s/users?search=%s", config.BaseURL, projectID, envID, email)
req, err := http.NewRequest("GET", endpoint, nil)
if err != nil {
return "", fmt.Errorf("error creating request: %w", err)
}
req.Header.Set("Authorization", "Bearer "+config.APIKey)
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
return "", fmt.Errorf("error making request: %w", err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("API error (status %d): %s", resp.StatusCode, string(body))
}
// Parse response to find user
var response map[string]interface{}
if err := json.Unmarshal(body, &response); err != nil {
return "", fmt.Errorf("error parsing response: %w", err)
}
// Look for users in common pagination structures
var users []interface{}
if data, ok := response["data"].([]interface{}); ok {
users = data
} else if items, ok := response["items"].([]interface{}); ok {
users = items
} else if usersList, ok := response["users"].([]interface{}); ok {
users = usersList
} else if _, ok := response["key"]; ok {
// Single user object returned
users = []interface{}{response}
} else {
// Try direct array unmarshaling as fallback
var directUsers []interface{}
if err := json.Unmarshal(body, &directUsers); err == nil {
users = directUsers
} else {
return "", fmt.Errorf("unexpected response format")
}
}
// Find user with matching email
for _, u := range users {
if user, ok := u.(map[string]interface{}); ok {
if userEmail, ok := user["email"].(string); ok && userEmail == email {
if key, ok := user["key"].(string); ok {
return key, nil
}
}
}
}
return "", nil // User not found
}
// UserExistsInPermit checks if a user exists in Permit.io by email.
//
// Parameters:
// - client: HTTP client for making API requests
// - config: Permit.io configuration containing API credentials and project details
// - email: The email address to check
//
// Returns:
// - bool: true if the user exists, false otherwise
// - string: The user's key if found, empty string otherwise
// - error: Error if the check fails
func UserExistsInPermit(client *http.Client, config *PermitConfig, email string) (bool, string, error) {
userKey, err := FindPermitUserByEmail(client, config, email)
if err != nil {
return false, "", err
}
return userKey != "", userKey, nil
}
// AssignRole assigns a role to a user in Permit.io for the configured tenant.
//
// Parameters:
// - client: HTTP client for making API requests
// - config: Permit.io configuration containing API credentials, project details, and default tenant
// - userID: The Permit.io user key (typically Cognito Subject ID)
// - role: The role key to assign
//
// Returns:
// - error: Error if the role assignment fails. Returns nil if role is assigned or already assigned.
func AssignRole(client *http.Client, config *PermitConfig, userID, role string) error {
projectID, err := config.GetProjectID(client)
if err != nil {
return fmt.Errorf("failed to get project ID: %w", err)
}
envID, err := config.GetEnvID(client)
if err != nil {
return fmt.Errorf("failed to get environment ID: %w", err)
}
endpoint := fmt.Sprintf("%s/v2/facts/%s/%s/role_assignments", config.BaseURL, projectID, envID)
tenant := config.DefaultTenant
if tenant == "" {
tenant = "default"
}
assignment := RoleAssignment{
User: userID,
Role: role,
Tenant: tenant,
}
jsonData, err := json.Marshal(assignment)
if err != nil {
return fmt.Errorf("error marshaling role assignment: %w", err)
}
req, err := http.NewRequest("POST", endpoint, bytes.NewBuffer(jsonData))
if err != nil {
return fmt.Errorf("error creating request: %w", err)
}
req.Header.Set("Authorization", "Bearer "+config.APIKey)
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("error making request: %w", err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
if resp.StatusCode == http.StatusConflict || resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusCreated {
// Role already assigned or was assigned successfully
return nil
}
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
var errResp map[string]interface{}
if err := json.Unmarshal(body, &errResp); err == nil {
if errorObj, ok := errResp["error"].(map[string]interface{}); ok {
if msg, ok := errorObj["message"].(string); ok {
return fmt.Errorf("API error (status %d): %s", resp.StatusCode, msg)
}
}
}
return fmt.Errorf("API error (status %d): %s", resp.StatusCode, string(body))
}
return nil
}
// UnassignRole removes a role assignment from a user in Permit.io.
//
// Parameters:
// - client: HTTP client for making API requests
// - config: Permit.io configuration containing API credentials, project details, and default tenant
// - userID: The Permit.io user key (typically Cognito Subject ID)
// - role: The role key to unassign
//
// Returns:
// - error: Error if the role unassignment fails. Returns nil if role is unassigned or was already not assigned.
func UnassignRole(client *http.Client, config *PermitConfig, userID, role string) error {
projectID, err := config.GetProjectID(client)
if err != nil {
return fmt.Errorf("failed to get project ID: %w", err)
}
envID, err := config.GetEnvID(client)
if err != nil {
return fmt.Errorf("failed to get environment ID: %w", err)
}
endpoint := fmt.Sprintf("%s/v2/facts/%s/%s/role_assignments", config.BaseURL, projectID, envID)
tenant := config.DefaultTenant
if tenant == "" {
tenant = "default"
}
assignment := RoleAssignment{
User: userID,
Role: role,
Tenant: tenant,
}
jsonData, err := json.Marshal(assignment)
if err != nil {
return fmt.Errorf("error marshaling role assignment: %w", err)
}
req, err := http.NewRequest("DELETE", endpoint, bytes.NewBuffer(jsonData))
if err != nil {
return fmt.Errorf("error creating request: %w", err)
}
req.Header.Set("Authorization", "Bearer "+config.APIKey)
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("error making request: %w", err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
if resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusNoContent || resp.StatusCode == http.StatusNotFound {
// Role was unassigned, already unassigned, or didn't exist
return nil
}
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent {
var errResp map[string]interface{}
if err := json.Unmarshal(body, &errResp); err == nil {
if errorObj, ok := errResp["error"].(map[string]interface{}); ok {
if msg, ok := errorObj["message"].(string); ok {
return fmt.Errorf("API error (status %d): %s", resp.StatusCode, msg)
}
}
}
return fmt.Errorf("API error (status %d): %s", resp.StatusCode, string(body))
}
return nil
}
// GetPermitUserRoles retrieves all roles assigned to a user in Permit.io.
//
// Parameters:
// - client: HTTP client for making API requests
// - config: Permit.io configuration containing API credentials and project details
// - userKey: The Permit.io user key (typically Cognito Subject ID)
//
// Returns:
// - []string: List of role keys assigned to the user
// - error: Error if the retrieval fails
func GetPermitUserRoles(client *http.Client, config *PermitConfig, userKey string) ([]string, error) {
projectID, err := config.GetProjectID(client)
if err != nil {
return nil, fmt.Errorf("failed to get project ID: %w", err)
}
envID, err := config.GetEnvID(client)
if err != nil {
return nil, fmt.Errorf("failed to get environment ID: %w", err)
}
endpoint := fmt.Sprintf("%s/v2/facts/%s/%s/role_assignments?user=%s",
config.BaseURL, projectID, envID, url.QueryEscape(userKey))
req, err := http.NewRequest("GET", endpoint, nil)
if err != nil {
return nil, fmt.Errorf("error creating request: %w", err)
}
req.Header.Set("Authorization", "Bearer "+config.APIKey)
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("error making request: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("API error (status %d): %s", resp.StatusCode, string(body))
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("error reading response: %w", err)
}
assignments, err := decodePermitRoleAssignments(body)
if err != nil {
return nil, err
}
roles := make([]string, 0, len(assignments))
for _, assignment := range assignments {
if assignment.Role != "" {
roles = append(roles, assignment.Role)
}
}
return roles, nil
}
// ListAllPermitRoleAssignments retrieves all role assignments in the configured Permit.io environment.
func ListAllPermitRoleAssignments(client *http.Client, config *PermitConfig) ([]RoleAssignment, error) {
projectID, err := config.GetProjectID(client)
if err != nil {
return nil, fmt.Errorf("failed to get project ID: %w", err)
}
envID, err := config.GetEnvID(client)
if err != nil {
return nil, fmt.Errorf("failed to get environment ID: %w", err)
}
assignments := []RoleAssignment{}
for page := 1; page <= maxPermitRoleAssignmentPages; page++ {
endpoint := fmt.Sprintf("%s/v2/facts/%s/%s/role_assignments?page=%d&per_page=%d",
config.BaseURL, projectID, envID, page, permitRoleAssignmentsPerPage)
req, err := http.NewRequest("GET", endpoint, nil)
if err != nil {
return nil, fmt.Errorf("error creating request: %w", err)
}
req.Header.Set("Authorization", "Bearer "+config.APIKey)
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("error making request: %w", err)
}
body, readErr := io.ReadAll(resp.Body)
closeErr := resp.Body.Close()
if readErr != nil {
return nil, fmt.Errorf("error reading response: %w", readErr)
}
if closeErr != nil {
return nil, fmt.Errorf("error closing response body: %w", closeErr)
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("API error (status %d): %s", resp.StatusCode, string(body))
}
pageAssignments, err := decodePermitRoleAssignments(body)
if err != nil {
return nil, err
}
assignments = append(assignments, pageAssignments...)
if len(pageAssignments) < permitRoleAssignmentsPerPage {
return assignments, nil
}
}
return nil, fmt.Errorf("Permit.io role assignment listing exceeded %d pages", maxPermitRoleAssignmentPages)
}
func decodePermitRoleAssignments(body []byte) ([]RoleAssignment, error) {
var directAssignments []RoleAssignment
if err := json.Unmarshal(body, &directAssignments); err == nil {
return directAssignments, nil
}
var response struct {
Data []RoleAssignment `json:"data"`
Items []RoleAssignment `json:"items"`
Results []RoleAssignment `json:"results"`
}
if err := json.Unmarshal(body, &response); err != nil {
return nil, fmt.Errorf("error parsing response as object or array: %w", err)
}
switch {
case response.Data != nil:
return response.Data, nil
case response.Items != nil:
return response.Items, nil
case response.Results != nil:
return response.Results, nil
default:
return nil, fmt.Errorf("unexpected Permit.io role assignments response format")
}
}
// GetExistingRoles retrieves all role definitions from the specified Permit.io project/environment.
//
// Parameters:
// - client: HTTP client for making API requests
// - config: Permit.io configuration containing API credentials and project details
//
// Returns:
// - []string: List of role keys that exist in the Permit.io environment
// - error: Error if the retrieval fails
func GetExistingRoles(client *http.Client, config *PermitConfig) ([]string, error) {
projectID, err := config.GetProjectID(client)
if err != nil {
return nil, fmt.Errorf("failed to get project ID: %w", err)
}
envID, err := config.GetEnvID(client)
if err != nil {
return nil, fmt.Errorf("failed to get environment ID: %w", err)
}
// Construct API URL for roles endpoint
endpoint := fmt.Sprintf("%s/v2/schema/%s/%s/roles", config.BaseURL, projectID, envID)
// Create HTTP request
req, err := http.NewRequest("GET", endpoint, nil)
if err != nil {
return nil, fmt.Errorf("error creating request: %w", err)
}
// Set authorization header
req.Header.Set("Authorization", "Bearer "+config.APIKey)
req.Header.Set("Content-Type", "application/json")
// Execute request
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("error making request: %w", err)
}
defer resp.Body.Close()
// Check for successful response
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("API error (status %d): %s", resp.StatusCode, string(body))
}
// Read and parse response body
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("error reading response: %w", err)
}
// Parse JSON response - try different response structures
var roles []string
var rolesData []interface{}
// First try to unmarshal as direct array
var directRoles []interface{}
if err := json.Unmarshal(body, &directRoles); err == nil {
rolesData = directRoles
} else {
// If that fails, try as object with data/items/etc
var response map[string]interface{}
if err := json.Unmarshal(body, &response); err != nil {
return nil, fmt.Errorf("error parsing response as object or array: %w", err)
}
// Look for roles in common response structures
if data, ok := response["data"].([]interface{}); ok {
rolesData = data
} else if items, ok := response["items"].([]interface{}); ok {
rolesData = items
} else if results, ok := response["results"].([]interface{}); ok {
rolesData = results
} else {
return nil, fmt.Errorf("unexpected response format: unable to find roles array")
}
}
// Extract role names from the data
for _, roleData := range rolesData {
if roleObj, ok := roleData.(map[string]interface{}); ok {
// Try different possible field names for role identifier
if key, ok := roleObj["key"].(string); ok {
roles = append(roles, key)
} else if name, ok := roleObj["name"].(string); ok {
roles = append(roles, name)
} else if id, ok := roleObj["id"].(string); ok {
roles = append(roles, id)
}
}
}
return roles, nil
}
// GetUserRoles is an alias for GetPermitUserRoles for convenience.
// Retrieves all roles assigned to a user in Permit.io.
//
// Parameters:
// - client: HTTP client for making API requests
// - config: Permit.io configuration containing API credentials and project details
// - userKey: The Permit.io user key (typically Cognito Subject ID)
//
// Returns:
// - []string: List of role keys assigned to the user
// - error: Error if the retrieval fails
func GetUserRoles(client *http.Client, config *PermitConfig, userKey string) ([]string, error) {
return GetPermitUserRoles(client, config, userKey)
}
// ValidateRoles validates that all requested roles exist in the Permit.io environment.
//
// Parameters:
// - client: HTTP client for making API requests
// - config: Permit.io configuration containing API credentials and project details
// - requestedRoles: List of role keys to validate
//
// Returns:
// - []string: List of missing roles (empty if all roles exist)
// - error: Error if the validation check fails
func ValidateRoles(client *http.Client, config *PermitConfig, requestedRoles []string) ([]string, error) {
if len(requestedRoles) == 0 {
return []string{}, nil // No roles to validate
}
// Get all existing roles from Permit.io
existingRoles, err := GetExistingRoles(client, config)
if err != nil {
return requestedRoles, fmt.Errorf("failed to retrieve existing roles from Permit.io: %w", err)
}
// Create a set of existing roles for efficient lookup
existingSet := make(map[string]bool)
for _, role := range existingRoles {
existingSet[role] = true
}
// Find requested roles that don't exist
var missingRoles []string
for _, requestedRole := range requestedRoles {
if !existingSet[requestedRole] {
missingRoles = append(missingRoles, requestedRole)
}
}
if len(missingRoles) > 0 {
return missingRoles, fmt.Errorf("the following roles do not exist in Permit.io: %s",
strings.Join(missingRoles, ", "))
}
return []string{}, nil
}
// RoleDetails contains the details of a role including its permissions.
type RoleDetails struct {
Key string // The role key/identifier
Name string // Human-readable role name
Description string // Optional description of the role
Permissions []string // List of permissions granted by this role
}
// GetAPIKeyScope retrieves the scope information for a Permit.io API key.
// This allows deriving the project and environment IDs from the API key itself,
// eliminating the need for separate PERMIT_IO_PROJECT_ID and PERMIT_IO_ENV_ID
// environment variables.
//
// Parameters:
// - client: HTTP client for making API requests
// - apiKey: The Permit.io API key
// - baseURL: The Permit.io API base URL (e.g., https://api.permit.io)
//
// Returns:
// - *APIKeyScope: The scope containing organization, project, and environment IDs
// - error: Error if the API call fails or response cannot be parsed
func GetAPIKeyScope(client *http.Client, apiKey, baseURL string) (*APIKeyScope, error) {
endpoint := fmt.Sprintf("%s/v2/api-key/scope", baseURL)
req, err := http.NewRequest("GET", endpoint, nil)
if err != nil {
return nil, fmt.Errorf("error creating request: %w", err)
}
req.Header.Set("Authorization", "Bearer "+apiKey)
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("error making request: %w", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("error reading response: %w", err)
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("API error (status %d): %s", resp.StatusCode, string(body))
}
var scope APIKeyScope
if err := json.Unmarshal(body, &scope); err != nil {
return nil, fmt.Errorf("error parsing response: %w", err)
}
return &scope, nil
}
// GetRoleDetails retrieves the details of a role from Permit.io, including its permissions.
// Uses the schema API endpoint to get role definitions.
//
// Parameters:
// - client: HTTP client for making API requests
// - config: Permit.io configuration containing API credentials and project details
// - roleKey: The role key to retrieve details for
//
// Returns:
// - *RoleDetails: The role details including permissions
// - error: Error if the retrieval fails
func GetRoleDetails(client *http.Client, config *PermitConfig, roleKey string) (*RoleDetails, error) {
projectID, err := config.GetProjectID(client)
if err != nil {
return nil, fmt.Errorf("failed to get project ID: %w", err)
}
envID, err := config.GetEnvID(client)
if err != nil {
return nil, fmt.Errorf("failed to get environment ID: %w", err)
}
endpoint := fmt.Sprintf("%s/v2/schema/%s/%s/roles/%s",
config.BaseURL, projectID, envID, roleKey)
req, err := http.NewRequest("GET", endpoint, nil)
if err != nil {
return nil, fmt.Errorf("error creating request: %w", err)
}
req.Header.Set("Authorization", "Bearer "+config.APIKey)
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("error making request: %w", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("error reading response: %w", err)
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("API error (status %d): %s", resp.StatusCode, string(body))
}
var roleData struct {
Key string `json:"key"`
Name string `json:"name"`
Description string `json:"description"`
Permissions []string `json:"permissions"`
}
if err := json.Unmarshal(body, &roleData); err != nil {
return nil, fmt.Errorf("error parsing response: %w", err)
}
return &RoleDetails{
Key: roleData.Key,
Name: roleData.Name,
Description: roleData.Description,
Permissions: roleData.Permissions,
}, nil
}