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