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 }