github.com/jfrog/jfrog-cli-core/v2@v2.51.0/common/commands/curl.go (about)

     1  package commands
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
     6  	"io"
     7  	"os/exec"
     8  	"strings"
     9  
    10  	gofrogcmd "github.com/jfrog/gofrog/io"
    11  	"github.com/jfrog/jfrog-cli-core/v2/utils/config"
    12  	"github.com/jfrog/jfrog-client-go/utils/errorutils"
    13  	"github.com/jfrog/jfrog-client-go/utils/log"
    14  )
    15  
    16  type CurlCommand struct {
    17  	arguments      []string
    18  	executablePath string
    19  	serverDetails  *config.ServerDetails
    20  	url            string
    21  }
    22  
    23  func NewCurlCommand() *CurlCommand {
    24  	return &CurlCommand{}
    25  }
    26  
    27  func (curlCmd *CurlCommand) SetArguments(arguments []string) *CurlCommand {
    28  	curlCmd.arguments = arguments
    29  	return curlCmd
    30  }
    31  
    32  func (curlCmd *CurlCommand) SetExecutablePath(executablePath string) *CurlCommand {
    33  	curlCmd.executablePath = executablePath
    34  	return curlCmd
    35  }
    36  
    37  func (curlCmd *CurlCommand) SetServerDetails(serverDetails *config.ServerDetails) *CurlCommand {
    38  	curlCmd.serverDetails = serverDetails
    39  	return curlCmd
    40  }
    41  
    42  func (curlCmd *CurlCommand) SetUrl(url string) *CurlCommand {
    43  	curlCmd.url = url
    44  	return curlCmd
    45  }
    46  
    47  func (curlCmd *CurlCommand) Run() error {
    48  	// Get curl execution path.
    49  	execPath, err := exec.LookPath("curl")
    50  	if err != nil {
    51  		return errorutils.CheckError(err)
    52  	}
    53  	curlCmd.SetExecutablePath(execPath)
    54  
    55  	// If the command already includes credentials flag, return an error.
    56  	if curlCmd.isCredentialsFlagExists() {
    57  		return errorutils.CheckErrorf("Curl command must not include credentials flag (-u or --user).")
    58  	}
    59  
    60  	// If the command already includes certificates flag, return an error.
    61  	if curlCmd.serverDetails.ClientCertPath != "" && curlCmd.isCertificateFlagExists() {
    62  		return errorutils.CheckErrorf("Curl command must not include certificate flag (--cert or --key).")
    63  	}
    64  
    65  	// Get target url for the curl command.
    66  	uriIndex, targetUri, err := curlCmd.buildCommandUrl(curlCmd.url)
    67  	if err != nil {
    68  		return err
    69  	}
    70  
    71  	// Replace url argument with complete url.
    72  	curlCmd.arguments[uriIndex] = targetUri
    73  
    74  	cmdWithoutCreds := strings.Join(curlCmd.arguments, " ")
    75  	// Add credentials to curl command.
    76  	credentialsMessage := curlCmd.addCommandCredentials()
    77  
    78  	// Run curl.
    79  	log.Debug(fmt.Sprintf("Executing curl command: '%s %s'", cmdWithoutCreds, credentialsMessage))
    80  	return gofrogcmd.RunCmd(curlCmd)
    81  }
    82  
    83  func (curlCmd *CurlCommand) addCommandCredentials() string {
    84  	certificateHelpPrefix := ""
    85  
    86  	if curlCmd.serverDetails.ClientCertPath != "" {
    87  		curlCmd.arguments = append(curlCmd.arguments,
    88  			"--cert", curlCmd.serverDetails.ClientCertPath,
    89  			"--key", curlCmd.serverDetails.ClientCertKeyPath)
    90  		certificateHelpPrefix = "--cert *** --key *** "
    91  	}
    92  
    93  	if curlCmd.serverDetails.AccessToken != "" {
    94  		// Add access token header.
    95  		tokenHeader := fmt.Sprintf("Authorization: Bearer %s", curlCmd.serverDetails.AccessToken)
    96  		curlCmd.arguments = append(curlCmd.arguments, "-H", tokenHeader)
    97  
    98  		return certificateHelpPrefix + "-H \"Authorization: Bearer ***\""
    99  	}
   100  
   101  	// Add credentials flag to Command. In case of flag duplication, the latter is used by Curl.
   102  	credFlag := fmt.Sprintf("-u%s:%s", curlCmd.serverDetails.User, curlCmd.serverDetails.Password)
   103  	curlCmd.arguments = append(curlCmd.arguments, credFlag)
   104  
   105  	return certificateHelpPrefix + "-u***:***"
   106  }
   107  
   108  func (curlCmd *CurlCommand) buildCommandUrl(url string) (uriIndex int, uriValue string, err error) {
   109  	// Find command's URL argument.
   110  	// Representing the target API for the Curl command.
   111  	uriIndex, uriValue = curlCmd.findUriValueAndIndex()
   112  	if uriIndex == -1 {
   113  		err = errorutils.CheckErrorf("Could not find argument in curl command.")
   114  		return
   115  	}
   116  
   117  	// If user provided full-url, throw an error.
   118  	if strings.HasPrefix(uriValue, "http://") || strings.HasPrefix(uriValue, "https://") {
   119  		err = errorutils.CheckErrorf("Curl command must not include full-url, but only the REST API URI (e.g '/api/system/ping').")
   120  		return
   121  	}
   122  
   123  	// Trim '/' prefix if exists.
   124  	uriValue = strings.TrimPrefix(uriValue, "/")
   125  
   126  	// Attach url to the api.
   127  	uriValue = url + uriValue
   128  
   129  	return
   130  }
   131  
   132  // Returns server details
   133  func (curlCmd *CurlCommand) GetServerDetails() (*config.ServerDetails, error) {
   134  	// Get --server-id flag value from the command, and remove it.
   135  	flagIndex, valueIndex, serverIdValue, err := coreutils.FindFlag("--server-id", curlCmd.arguments)
   136  	if err != nil {
   137  		return nil, err
   138  	}
   139  	coreutils.RemoveFlagFromCommand(&curlCmd.arguments, flagIndex, valueIndex)
   140  	return config.GetSpecificConfig(serverIdValue, true, true)
   141  }
   142  
   143  // Find the URL argument in the Curl Command.
   144  // A command flag is prefixed by '-' or '--'.
   145  // Use this method ONLY after removing all JFrog-CLI flags, i.e. flags in the form: '--my-flag=value' are not allowed.
   146  // An argument is any provided candidate which is not a flag or a flag value.
   147  func (curlCmd *CurlCommand) findUriValueAndIndex() (int, string) {
   148  	skipThisArg := false
   149  	for index, arg := range curlCmd.arguments {
   150  		// Check if shouldn't check current arg.
   151  		if skipThisArg {
   152  			skipThisArg = false
   153  			continue
   154  		}
   155  
   156  		// If starts with '--', meaning a flag which its value is at next slot.
   157  		if strings.HasPrefix(arg, "--") {
   158  			skipThisArg = true
   159  			continue
   160  		}
   161  
   162  		// Check if '-'.
   163  		if strings.HasPrefix(arg, "-") {
   164  			if len(arg) > 2 {
   165  				// Meaning that this flag also contains its value.
   166  				continue
   167  			}
   168  			// If reached here, means that the flag value is at the next arg.
   169  			skipThisArg = true
   170  			continue
   171  		}
   172  
   173  		// Found an argument
   174  		return index, arg
   175  	}
   176  
   177  	// If reached here, didn't find an argument.
   178  	return -1, ""
   179  }
   180  
   181  // Return true if the curl command includes credentials flag.
   182  // The searched flags are not CLI flags.
   183  func (curlCmd *CurlCommand) isCredentialsFlagExists() bool {
   184  	for _, arg := range curlCmd.arguments {
   185  		if strings.HasPrefix(arg, "-u") || arg == "--user" {
   186  			return true
   187  		}
   188  	}
   189  
   190  	return false
   191  }
   192  
   193  // Return true if the curl command includes certificates flag.
   194  // The searched flags are not CLI flags.
   195  func (curlCmd *CurlCommand) isCertificateFlagExists() bool {
   196  	for _, arg := range curlCmd.arguments {
   197  		if arg == "--cert" || arg == "--key" {
   198  			return true
   199  		}
   200  	}
   201  
   202  	return false
   203  }
   204  
   205  func (curlCmd *CurlCommand) GetCmd() *exec.Cmd {
   206  	var cmd []string
   207  	cmd = append(cmd, curlCmd.executablePath)
   208  	cmd = append(cmd, curlCmd.arguments...)
   209  	return exec.Command(cmd[0], cmd[1:]...)
   210  }
   211  
   212  func (curlCmd *CurlCommand) GetEnv() map[string]string {
   213  	return map[string]string{}
   214  }
   215  
   216  func (curlCmd *CurlCommand) GetStdWriter() io.WriteCloser {
   217  	return nil
   218  }
   219  
   220  func (curlCmd *CurlCommand) GetErrWriter() io.WriteCloser {
   221  	return nil
   222  }
   223  
   224  func (curlCmd *CurlCommand) ServerDetails() (*config.ServerDetails, error) {
   225  	return curlCmd.serverDetails, nil
   226  }