Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions packages/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const (
operationCallGetOrgRelays = "CallGetOrgRelays"
operationCallRegisterGateway = "CallRegisterGateway"
operationCallPAMAccess = "CallPAMAccess"
operationCallPAMAccessApprovalRequest = "CallPAMAccessApprovalRequest"
operationCallPAMSessionCredentials = "CallPAMSessionCredentials"
operationCallGetPamSessionKey = "CallGetPamSessionKey"
operationCallUploadPamSessionLog = "CallUploadPamSessionLog"
Expand Down Expand Up @@ -865,6 +866,26 @@ func CallPAMAccess(httpClient *resty.Client, request PAMAccessRequest) (PAMAcces
return pamAccessResponse, nil
}

func CallPAMAccessApprovalRequest(httpClient *resty.Client, request PAMAccessApprovalRequest) (PAMAccessApprovalRequestResponse, error) {
var pamAccessApprovalRequestResponse PAMAccessApprovalRequestResponse
response, err := httpClient.
R().
SetResult(&pamAccessApprovalRequestResponse).
SetHeader("User-Agent", USER_AGENT).
SetBody(request).
Post(fmt.Sprintf("%v/v1/approval-policies/pam-access/requests", config.INFISICAL_URL))

if err != nil {
return PAMAccessApprovalRequestResponse{}, NewGenericRequestError(operationCallPAMAccessApprovalRequest, err)
}

if response.IsError() {
return PAMAccessApprovalRequestResponse{}, NewAPIErrorWithResponse(operationCallPAMAccessApprovalRequest, response, nil)
}

return pamAccessApprovalRequestResponse, nil
}

func CallPAMSessionCredentials(httpClient *resty.Client, sessionId string) (PAMSessionCredentialsResponse, error) {
var pamSessionCredentialsResponse PAMSessionCredentialsResponse
response, err := httpClient.
Expand Down
18 changes: 18 additions & 0 deletions packages/api/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,24 @@ type PAMAccessResponse struct {
Metadata map[string]string `json:"metadata,omitempty"`
}

type PAMAccessApprovalRequestPayloadRequestData struct {
AccountPath string `json:"accountPath"`
AccessDuration string `json:"accessDuration"`
}

type PAMAccessApprovalRequest struct {
ProjectId string `json:"projectId"`
RequestData PAMAccessApprovalRequestPayloadRequestData `json:"requestData"`
}

type PAMAccessApprovalRequestResponse struct {
Request struct {
ID string `json:"id"`
ProjectId string `json:"projectId"`
OrgId string `json:"organizationId"`
} `json:"request"`
}

type PAMSessionCredentialsResponse struct {
Credentials PAMSessionCredentials `json:"credentials"`
}
Expand Down
58 changes: 58 additions & 0 deletions packages/pam/local/database-proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,22 @@ package pam

import (
"context"
"errors"
"fmt"
"io"
"net"
"os"
"os/signal"
"strings"
"syscall"
"time"

"github.com/Infisical/infisical-merge/packages/api"
"github.com/Infisical/infisical-merge/packages/config"
"github.com/Infisical/infisical-merge/packages/pam/session"
"github.com/Infisical/infisical-merge/packages/util"
"github.com/go-resty/resty/v2"
"github.com/manifoldco/promptui"
"github.com/rs/zerolog/log"
)

Expand All @@ -30,6 +34,18 @@ const (
ALPNInfisicalPAMCancellation ALPN = "infisical-pam-session-cancellation"
)

func askForApprovalRequestTrigger() (bool, error) {
prompt := promptui.Prompt{
Label: "This action requires approval. You may create an approval request now. Continue?",
IsConfirm: true,
}
result, err := prompt.Run()
if err != nil {
return false, err
}
return strings.ToLower(result) == "y", nil
}

func StartDatabaseLocalProxy(accessToken string, accountPath string, projectID string, durationStr string, port int) {
log.Info().Msgf("Starting database proxy for account: %s", accountPath)
log.Info().Msgf("Session duration: %s", durationStr)
Expand All @@ -46,6 +62,48 @@ func StartDatabaseLocalProxy(accessToken string, accountPath string, projectID s

pamResponse, err := api.CallPAMAccess(httpClient, pamRequest)
if err != nil {
var apiErr *api.APIError
if errors.As(err, &apiErr) && apiErr.ErrorMessage == "A policy is in place for this resource" {
if v, ok := apiErr.Details.(map[string]any); ok {
log.Info().Msgf("Account is protected by approval policy: %s", v["policyName"])

shouldSendRequest, err := askForApprovalRequestTrigger()
if err != nil {
if errors.Is(err, promptui.ErrAbort) {
log.Info().Msgf("Approval request was not created.")
} else {
util.HandleError(err, "Failed to send PAM account request")
}
return
}

if !shouldSendRequest {
log.Info().Msgf("Approval request was not created.")
return
}

approvalReq, err := api.CallPAMAccessApprovalRequest(httpClient, api.PAMAccessApprovalRequest{
ProjectId: projectID,
RequestData: api.PAMAccessApprovalRequestPayloadRequestData{
AccountPath: accountPath,
AccessDuration: durationStr,
},
})
if err != nil {
util.HandleError(err, "Failed to send PAM account request")
return
}

url := fmt.Sprintf("%s/organizations/%s/projects/pam/%s/approval-requests/%s", strings.TrimSuffix(config.INFISICAL_URL, "/api"), approvalReq.Request.OrgId, approvalReq.Request.ProjectId, approvalReq.Request.ID)
Comment thread
sheensantoscapadngan marked this conversation as resolved.
if err := util.OpenBrowser(url); err != nil {
log.Error().Msgf("Failed to do browser redirect: %v", err)
}
log.Info().Msgf("Approval request created.")
log.Info().Msgf("View details at: %s", url)
return
}
}

util.HandleError(err, "Failed to access PAM account")
return
}
Expand Down
15 changes: 15 additions & 0 deletions packages/util/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"os"
"os/exec"
"path"
"runtime"
"sort"
"strings"
"sync"
Expand Down Expand Up @@ -558,3 +559,17 @@ func GenerateETagFromSecrets(secrets []models.SingleEnvironmentVariable) string
func IsDevelopmentMode() bool {
return CLI_VERSION == "devel"
}

// OpenBrowser attempts to open a URL in the user's default browser
func OpenBrowser(url string) error {
var cmd *exec.Cmd
switch runtime.GOOS {
case "darwin":
cmd = exec.Command("open", url)
case "windows":
cmd = exec.Command("cmd", "/c", "start", url)
default: // linux and others
cmd = exec.Command("xdg-open", url)
}
return cmd.Start()
}
Comment thread
akhilmhdh marked this conversation as resolved.
Loading