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