Skip to content

Commit b806ea3

Browse files
committed
feat(account): add login command for saving token to system keyring
1 parent b17a93e commit b806ea3

File tree

4 files changed

+76
-0
lines changed

4 files changed

+76
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Added
1111

1212
- Experimental support for reading password or token from system keyring.
13+
- Experimental support from saving token to system keyring with `upctl account login --with-token`.
1314

1415
### Changed
1516

internal/commands/account/login.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package account
2+
3+
import (
4+
"bufio"
5+
"fmt"
6+
"strings"
7+
8+
"github.com/UpCloudLtd/upcloud-cli/v3/internal/commands"
9+
"github.com/UpCloudLtd/upcloud-cli/v3/internal/config"
10+
"github.com/UpCloudLtd/upcloud-cli/v3/internal/output"
11+
"github.com/spf13/pflag"
12+
)
13+
14+
// LoginCommand creates the "account login" command
15+
func LoginCommand() commands.Command {
16+
return &loginCommand{
17+
BaseCommand: commands.New(
18+
"login",
19+
"Configure an authentication token to the system keyring.",
20+
"upctl account login --with-token",
21+
),
22+
}
23+
}
24+
25+
type loginCommand struct {
26+
*commands.BaseCommand
27+
28+
withToken config.OptionalBoolean
29+
}
30+
31+
// InitCommand implements Command.InitCommand
32+
func (s *loginCommand) InitCommand() {
33+
fs := &pflag.FlagSet{}
34+
config.AddToggleFlag(fs, &s.withToken, "with-token", false, "Read token from standard input.")
35+
s.AddFlags(fs)
36+
37+
// Require the with-token flag until we support using browser to authenticate.
38+
commands.Must(s.Cobra().MarkFlagRequired("with-token"))
39+
}
40+
41+
// DoesNotUseServices implements commands.OfflineCommand as this command does not use services
42+
func (s *loginCommand) DoesNotUseServices() {}
43+
44+
// ExecuteWithoutArguments implements commands.NoArgumentCommand
45+
func (s *loginCommand) ExecuteWithoutArguments(exec commands.Executor) (output.Output, error) {
46+
if s.withToken.Value() {
47+
return s.executeWithToken(exec)
48+
}
49+
50+
return output.None{}, nil
51+
}
52+
53+
func (s *loginCommand) executeWithToken(exec commands.Executor) (output.Output, error) {
54+
in := bufio.NewReader(s.Cobra().InOrStdin())
55+
token, err := in.ReadString('\n')
56+
if err != nil {
57+
return nil, fmt.Errorf("failed to read token from standard input: %w", err)
58+
}
59+
60+
msg := "Saving provided token to the system keyring."
61+
exec.PushProgressStarted(msg)
62+
err = config.SaveToken(strings.TrimSpace(token))
63+
if err != nil {
64+
return commands.HandleError(exec, msg, err)
65+
}
66+
67+
exec.PushProgressSuccess(msg)
68+
69+
return output.None{}, nil
70+
}

internal/commands/all/all.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ func BuildCommands(rootCmd *cobra.Command, conf *config.Config) {
115115

116116
// Account
117117
accountCommand := commands.BuildCommand(account.BaseAccountCommand(), rootCmd, conf)
118+
commands.BuildCommand(account.LoginCommand(), accountCommand.Cobra(), conf)
118119
commands.BuildCommand(account.ShowCommand(), accountCommand.Cobra(), conf)
119120
commands.BuildCommand(account.ListCommand(), accountCommand.Cobra(), conf)
120121
commands.BuildCommand(account.DeleteCommand(), accountCommand.Cobra(), conf)

internal/config/config.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,10 @@ func GetVersion() string {
249249
return version
250250
}
251251

252+
func SaveToken(token string) error {
253+
return keyring.Set(keyringServiceName, "", token)
254+
}
255+
252256
func getVersion() string {
253257
// Version was overridden during the build
254258
if Version != "dev" {

0 commit comments

Comments
 (0)