• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

UiPath / uipathcli / 14925811953

09 May 2025 09:25AM UTC coverage: 90.6% (+0.06%) from 90.539%
14925811953

push

github

web-flow
Merge pull request #194 from UiPath/feature/login-confidential-app

Add support for user login flow using confidential applications

244 of 264 new or added lines in 9 files covered. (92.42%)

4 existing lines in 3 files now uncovered.

6496 of 7170 relevant lines covered (90.6%)

1.02 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.24
/commandline/config_command_handler.go
1
package commandline
2

3
import (
4
        "bufio"
5
        "fmt"
6
        "io"
7
        "strings"
8

9
        "github.com/UiPath/uipathcli/config"
10
)
11

12
// configCommandHandler implements commands for configuring the CLI.
13
// The CLI can be configured interactively or by setting config values
14
// programmatically.
15
//
16
// Example:
17
// uipath config ==> interactive configuration of the CLI
18
// uipath config set ==> stores a value in the configuration file
19
type configCommandHandler struct {
20
        StdIn          io.Reader
21
        StdOut         io.Writer
22
        ConfigProvider config.ConfigProvider
23
}
24

25
const notSetMessage = "not set"
26
const maskMessage = "*******"
27
const successfullyConfiguredMessage = "Successfully configured uipath CLI"
28
const successfullySetMessage = "Successfully set config value"
29

30
func (h configCommandHandler) Set(key string, value string, profileName string) error {
1✔
31
        cfg := h.getOrCreateProfile(profileName)
1✔
32
        err := h.setConfigValue(&cfg, key, value)
1✔
33
        if err != nil {
2✔
34
                return err
1✔
35
        }
1✔
36
        err = h.ConfigProvider.Update(profileName, cfg)
1✔
37
        if err != nil {
1✔
38
                return err
×
39
        }
×
40
        _, _ = fmt.Fprintln(h.StdOut, successfullySetMessage)
1✔
41
        return nil
1✔
42
}
43

44
func (h configCommandHandler) setConfigValue(cfg *config.Config, key string, value string) error {
1✔
45
        keyParts := strings.Split(key, ".")
1✔
46
        if key == "serviceVersion" {
2✔
47
                cfg.SetServiceVersion(value)
1✔
48
                return nil
1✔
49
        } else if key == "organization" {
3✔
50
                cfg.SetOrganization(value)
1✔
51
                return nil
1✔
52
        } else if key == "tenant" {
3✔
53
                cfg.SetTenant(value)
1✔
54
                return nil
1✔
55
        } else if key == "uri" {
3✔
56
                return cfg.SetUri(value)
1✔
57
        } else if key == "insecure" {
3✔
58
                insecure, err := h.convertToBool(value)
1✔
59
                if err != nil {
2✔
60
                        return fmt.Errorf("Invalid value for 'insecure': %w", err)
1✔
61
                }
1✔
62
                cfg.SetInsecure(insecure)
1✔
63
                return nil
1✔
64
        } else if key == "debug" {
2✔
65
                debug, err := h.convertToBool(value)
1✔
66
                if err != nil {
2✔
67
                        return fmt.Errorf("Invalid value for 'debug': %w", err)
1✔
68
                }
1✔
69
                cfg.SetDebug(debug)
1✔
70
                return nil
1✔
71
        } else if key == "auth.grantType" {
2✔
72
                cfg.SetAuthGrantType(value)
1✔
73
                return nil
1✔
74
        } else if key == "auth.scopes" {
3✔
75
                cfg.SetAuthScopes(value)
1✔
76
                return nil
1✔
77
        } else if h.isHeaderKey(keyParts) {
3✔
78
                cfg.SetHeader(keyParts[1], value)
1✔
79
                return nil
1✔
80
        } else if h.isParameterKey(keyParts) {
3✔
81
                cfg.SetParameter(keyParts[1], value)
1✔
82
                return nil
1✔
83
        } else if h.isAuthPropertyKey(keyParts) {
3✔
84
                cfg.SetAuthProperty(keyParts[2], value)
1✔
85
                return nil
1✔
86
        }
1✔
87
        return fmt.Errorf("Unknown config key '%s'", key)
1✔
88
}
89

90
func (h configCommandHandler) isHeaderKey(keyParts []string) bool {
1✔
91
        return len(keyParts) == 2 && keyParts[0] == "header"
1✔
92
}
1✔
93

94
func (h configCommandHandler) isParameterKey(keyParts []string) bool {
1✔
95
        return len(keyParts) == 2 && keyParts[0] == "parameter"
1✔
96
}
1✔
97

98
func (h configCommandHandler) isAuthPropertyKey(keyParts []string) bool {
1✔
99
        return len(keyParts) == 3 && keyParts[0] == "auth" && keyParts[1] == "properties"
1✔
100
}
1✔
101

102
func (h configCommandHandler) convertToBool(value string) (bool, error) {
1✔
103
        if strings.EqualFold(value, "true") {
2✔
104
                return true, nil
1✔
105
        }
1✔
106
        if strings.EqualFold(value, "false") {
1✔
107
                return false, nil
×
108
        }
×
109
        return false, fmt.Errorf("Invalid boolean value: %s", value)
1✔
110
}
111

112
func (h configCommandHandler) Configure(auth string, profileName string) error {
1✔
113
        switch auth {
1✔
114
        case config.AuthTypeCredentials:
1✔
115
                return h.configureCredentials(profileName)
1✔
116
        case config.AuthTypeLogin:
1✔
117
                return h.configureLogin(profileName)
1✔
118
        case config.AuthTypePat:
1✔
119
                return h.configurePat(profileName)
1✔
120
        case "":
1✔
121
                return h.configure(profileName)
1✔
122
        }
NEW
123
        return fmt.Errorf("Invalid auth, supported values: %s, %s, %s", config.AuthTypeCredentials, config.AuthTypeLogin, config.AuthTypePat)
×
124
}
125

126
func (h configCommandHandler) configure(profileName string) error {
1✔
127
        cfg := h.getOrCreateProfile(profileName)
1✔
128
        reader := bufio.NewReader(h.StdIn)
1✔
129

1✔
130
        builder := config.NewConfigBuilder(cfg)
1✔
131
        err := h.readOrgTenantInput(builder, reader)
1✔
132
        if err != nil {
1✔
133
                return nil
×
134
        }
×
135
        err = h.readAuthInput(builder, reader)
1✔
136
        if err != nil {
2✔
137
                return nil
1✔
138
        }
1✔
139

140
        return h.updateConfigIfNeeded(builder, profileName)
1✔
141
}
142

143
func (h configCommandHandler) readAuthInput(builder *config.ConfigBuilder, reader *bufio.Reader) error {
1✔
144
        authType := h.readAuthTypeInput(builder.Config, reader)
1✔
145
        switch authType {
1✔
146
        case config.AuthTypeCredentials:
1✔
147
                return h.readCredentialsInput(builder, reader)
1✔
148
        case config.AuthTypeLogin:
1✔
149
                return h.readLoginInput(builder, reader)
1✔
150
        case config.AuthTypePat:
1✔
151
                return h.readPatInput(builder, reader)
1✔
152
        default:
1✔
153
                return nil
1✔
154
        }
155
}
156

157
func (h configCommandHandler) configureCredentials(profileName string) error {
1✔
158
        cfg := h.getOrCreateProfile(profileName)
1✔
159
        reader := bufio.NewReader(h.StdIn)
1✔
160

1✔
161
        builder := config.NewConfigBuilder(cfg)
1✔
162
        err := h.readOrgTenantInput(builder, reader)
1✔
163
        if err != nil {
1✔
164
                return nil
×
165
        }
×
166
        err = h.readCredentialsInput(builder, reader)
1✔
167
        if err != nil {
1✔
168
                return nil
×
169
        }
×
170

171
        return h.updateConfigIfNeeded(builder, profileName)
1✔
172
}
173

174
func (h configCommandHandler) configureLogin(profileName string) error {
1✔
175
        cfg := h.getOrCreateProfile(profileName)
1✔
176
        reader := bufio.NewReader(h.StdIn)
1✔
177

1✔
178
        builder := config.NewConfigBuilder(cfg)
1✔
179
        err := h.readOrgTenantInput(builder, reader)
1✔
180
        if err != nil {
1✔
181
                return nil
×
182
        }
×
183
        err = h.readLoginInput(builder, reader)
1✔
184
        if err != nil {
2✔
185
                return nil
1✔
186
        }
1✔
187

188
        return h.updateConfigIfNeeded(builder, profileName)
1✔
189
}
190

191
func (h configCommandHandler) configurePat(profileName string) error {
1✔
192
        cfg := h.getOrCreateProfile(profileName)
1✔
193
        reader := bufio.NewReader(h.StdIn)
1✔
194

1✔
195
        builder := config.NewConfigBuilder(cfg)
1✔
196
        err := h.readOrgTenantInput(builder, reader)
1✔
197
        if err != nil {
1✔
198
                return nil
×
199
        }
×
200
        err = h.readPatInput(builder, reader)
1✔
201
        if err != nil {
1✔
202
                return nil
×
203
        }
×
204

205
        return h.updateConfigIfNeeded(builder, profileName)
1✔
206
}
207

208
func (h configCommandHandler) updateConfigIfNeeded(builder *config.ConfigBuilder, profileName string) error {
1✔
209
        updatedConfig, changed := builder.Build()
1✔
210
        if !changed {
2✔
211
                return nil
1✔
212
        }
1✔
213

214
        err := h.ConfigProvider.Update(profileName, updatedConfig)
1✔
215
        if err != nil {
1✔
NEW
216
                return err
×
UNCOV
217
        }
×
218
        _, _ = fmt.Fprintln(h.StdOut, successfullyConfiguredMessage)
1✔
219
        return nil
1✔
220
}
221

222
func (h configCommandHandler) getOrCreateProfile(profileName string) config.Config {
1✔
223
        cfg := h.ConfigProvider.Config(profileName)
1✔
224
        if cfg == nil {
2✔
225
                return h.ConfigProvider.New()
1✔
226
        }
1✔
227
        return *cfg
1✔
228
}
229

230
func (h configCommandHandler) getDisplayValue(value string, masked bool) string {
1✔
231
        if value == "" {
2✔
232
                return notSetMessage
1✔
233
        }
1✔
234
        if masked {
2✔
235
                return h.maskValue(value)
1✔
236
        }
1✔
237
        return value
1✔
238
}
239

240
func (h configCommandHandler) maskValue(value string) string {
1✔
241
        if len(value) < 10 {
2✔
242
                return maskMessage
1✔
243
        }
1✔
244
        return maskMessage + value[len(value)-4:]
1✔
245
}
246

247
func (h configCommandHandler) readUserInput(message string, reader *bufio.Reader) (*string, error) {
1✔
248
        _, err := fmt.Fprint(h.StdOut, message+" ")
1✔
249
        if err != nil {
1✔
NEW
250
                return nil, err
×
251
        }
×
252
        value, err := reader.ReadString('\n')
1✔
253
        if err != nil {
2✔
254
                return nil, err
1✔
255
        }
1✔
256
        value = strings.Trim(value, "\r\n")
1✔
257
        if value == "" {
2✔
258
                return nil, nil
1✔
259
        }
1✔
260
        value = strings.Trim(value, " \t")
1✔
261
        return &value, nil
1✔
262
}
263

264
func (h configCommandHandler) readOrgTenantInput(builder *config.ConfigBuilder, reader *bufio.Reader) error {
1✔
265
        cfg := builder.Config
1✔
266
        message := fmt.Sprintf("Enter organization [%s]:", h.getDisplayValue(cfg.Organization, false))
1✔
267
        organization, err := h.readUserInput(message, reader)
1✔
268
        if err != nil {
1✔
NEW
269
                return err
×
270
        }
×
271

272
        message = fmt.Sprintf("Enter tenant [%s]:", h.getDisplayValue(cfg.Tenant, false))
1✔
273
        tenant, err := h.readUserInput(message, reader)
1✔
274
        if err != nil {
1✔
NEW
275
                return err
×
276
        }
×
277

278
        builder.
1✔
279
                WithOrganization(organization).
1✔
280
                WithTenant(tenant)
1✔
281
        return nil
1✔
282
}
283

284
func (h configCommandHandler) readCredentialsInput(builder *config.ConfigBuilder, reader *bufio.Reader) error {
1✔
285
        cfg := builder.Config
1✔
286
        message := fmt.Sprintf("Enter client id [%s]:", h.getDisplayValue(cfg.ClientId(), true))
1✔
287
        clientId, err := h.readUserInput(message, reader)
1✔
288
        if err != nil {
1✔
NEW
289
                return err
×
290
        }
×
291

292
        message = fmt.Sprintf("Enter client secret [%s]:", h.getDisplayValue(cfg.ClientSecret(), true))
1✔
293
        clientSecret, err := h.readUserInput(message, reader)
1✔
294
        if err != nil {
2✔
295
                return err
1✔
296
        }
1✔
297

298
        builder.WithCredentials(clientId, clientSecret)
1✔
299
        return nil
1✔
300
}
301

302
func (h configCommandHandler) readLoginInput(builder *config.ConfigBuilder, reader *bufio.Reader) error {
1✔
303
        cfg := builder.Config
1✔
304
        message := fmt.Sprintf("Enter client id [%s]:", h.getDisplayValue(cfg.ClientId(), true))
1✔
305
        clientId, err := h.readUserInput(message, reader)
1✔
306
        if err != nil {
1✔
NEW
307
                return err
×
NEW
308
        }
×
309
        message = fmt.Sprintf("Enter client secret (only for confidential apps) [%s]:", h.getDisplayValue(cfg.ClientSecret(), true))
1✔
310
        clientSecret, err := h.readUserInput(message, reader)
1✔
311
        if err != nil {
1✔
NEW
312
                return err
×
UNCOV
313
        }
×
314
        message = fmt.Sprintf("Enter redirect uri [%s]:", h.getDisplayValue(cfg.RedirectUri(), false))
1✔
315
        redirectUri, err := h.readUserInput(message, reader)
1✔
316
        if err != nil {
2✔
317
                return err
1✔
318
        }
1✔
319
        message = fmt.Sprintf("Enter scopes [%s]:", h.getDisplayValue(cfg.Scopes(), false))
1✔
320
        scopes, err := h.readUserInput(message, reader)
1✔
321
        if err != nil {
2✔
322
                return err
1✔
323
        }
1✔
324

325
        builder.WithLogin(clientId, clientSecret, redirectUri, scopes)
1✔
326
        return nil
1✔
327
}
328

329
func (h configCommandHandler) readPatInput(builder *config.ConfigBuilder, reader *bufio.Reader) error {
1✔
330
        cfg := builder.Config
1✔
331
        message := fmt.Sprintf("Enter personal access token [%s]:", h.getDisplayValue(cfg.Pat(), true))
1✔
332
        pat, err := h.readUserInput(message, reader)
1✔
333
        if err != nil {
2✔
334
                return err
1✔
335
        }
1✔
336

337
        builder.WithPat(pat)
1✔
338
        return nil
1✔
339
}
340

341
func (h configCommandHandler) readAuthTypeInput(cfg config.Config, reader *bufio.Reader) string {
1✔
342
        authType := cfg.AuthType()
1✔
343
        for {
2✔
344
                message := fmt.Sprintf(`Authentication type [%s]:
1✔
345
  [1] credentials - Client Id and Client Secret
1✔
346
  [2] login - OAuth login using the browser
1✔
347
  [3] pat - Personal Access Token
1✔
348
Select:`, h.getDisplayValue(authType, false))
1✔
349
                input, err := h.readUserInput(message, reader)
1✔
350
                if err != nil {
1✔
351
                        return ""
×
352
                }
×
353
                if input == nil {
2✔
354
                        return authType
1✔
355
                }
1✔
356
                switch *input {
1✔
NEW
357
                case "":
×
358
                case "1":
1✔
359
                        return config.AuthTypeCredentials
1✔
360
                case "2":
1✔
361
                        return config.AuthTypeLogin
1✔
362
                case "3":
1✔
363
                        return config.AuthTypePat
1✔
364
                }
365
        }
366
}
367

368
func newConfigCommandHandler(stdIn io.Reader, stdOut io.Writer, configProvider config.ConfigProvider) *configCommandHandler {
1✔
369
        return &configCommandHandler{
1✔
370
                StdIn:          stdIn,
1✔
371
                StdOut:         stdOut,
1✔
372
                ConfigProvider: configProvider,
1✔
373
        }
1✔
374
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc