Skip to content

Commit cdcd7b4

Browse files
committed
feat(auth): add tokens api
1 parent 2d9e4b2 commit cdcd7b4

File tree

9 files changed

+454
-83
lines changed

9 files changed

+454
-83
lines changed

cmd/auth.go

+14-80
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,11 @@
11
package cmd
22

33
import (
4-
"errors"
5-
"fmt"
6-
"net/url"
7-
"os/exec"
8-
"runtime"
9-
"time"
10-
114
drycc "github.com/drycc/controller-sdk-go"
12-
"github.com/drycc/controller-sdk-go/api"
135
"github.com/drycc/controller-sdk-go/auth"
146
"github.com/drycc/workflow-cli/settings"
157
)
168

17-
func (d *DryccCmd) doLogin(s settings.Settings, username, password string) error {
18-
key, err := auth.Login(s.Client, username, password)
19-
if d.checkAPICompatibility(s.Client, err) != nil {
20-
return err
21-
}
22-
if err != nil {
23-
return nil
24-
}
25-
if username == "" || password == "" {
26-
fmt.Printf("Opening browser to %s\n", key)
27-
d.Print("Waiting for login... ")
28-
err = d.openBrower(key)
29-
if err != nil {
30-
d.Print("Cannot open browser, please visit the website in yourself")
31-
}
32-
u, err := url.Parse(key)
33-
if err != nil {
34-
return err
35-
}
36-
key = u.Query()["key"][0]
37-
}
38-
quit := progress(d.WOut)
39-
d.doToken(s, key)
40-
quit <- true
41-
<-quit
42-
return nil
43-
}
44-
45-
func (d *DryccCmd) openBrower(URL string) error {
46-
var commands = map[string]string{
47-
"windows": "start",
48-
"darwin": "open",
49-
"linux": "xdg-open",
50-
}
51-
run, ok := commands[runtime.GOOS]
52-
if !ok {
53-
return errors.New("warning: Cannot open browser")
54-
}
55-
cmd := exec.Command(run, URL)
56-
err := cmd.Start()
57-
if err != nil {
58-
return errors.New("warning: Cannot open browser")
59-
}
60-
61-
return nil
62-
}
63-
64-
func (d *DryccCmd) doToken(s settings.Settings, key string) error {
65-
var token api.AuthTokenResponse
66-
for i := 0; i <= 120; i++ {
67-
token, _ = auth.Token(s.Client, key)
68-
time.Sleep(time.Duration(5) * time.Second)
69-
if token.Token != "" && token.Username != "" {
70-
break
71-
}
72-
}
73-
if token.Token == "" || token.Token == "fail" {
74-
d.Printf("Logged fail")
75-
} else {
76-
s.Client.Token = token.Token
77-
s.Username = token.Username
78-
filename, err := s.Save(d.ConfigFile)
79-
if err != nil {
80-
return nil
81-
}
82-
d.Printf("Logged in as %s\n", token.Username)
83-
d.Printf("Configuration file written to %s\n", filename)
84-
}
85-
return nil
86-
}
87-
889
// Login to a Drycc controller.
8910
func (d *DryccCmd) Login(controller string, sslVerify bool, username, password string) error {
9011
c, err := drycc.New(sslVerify, controller, "")
@@ -100,8 +21,21 @@ func (d *DryccCmd) Login(controller string, sslVerify bool, username, password s
10021
return err
10122
}
10223

24+
token, err := d.TokensAdd(c, username, password, "workflow-cli", "yes", false)
25+
if err != nil {
26+
return err
27+
}
28+
// save settings
10329
s := settings.Settings{Client: c}
104-
return d.doLogin(s, username, password)
30+
s.Client.Token = token.Token
31+
s.Username = token.Username
32+
filename, err := s.Save(d.ConfigFile)
33+
if err != nil {
34+
return err
35+
}
36+
d.Printf("Logged in as %s\n", token.Username)
37+
d.Printf("Configuration file written to %s\n", filename)
38+
return nil
10539
}
10640

10741
// Logout from a Drycc controller.

cmd/cmd.go

+4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"io"
66

7+
drycc "github.com/drycc/controller-sdk-go"
78
"github.com/drycc/controller-sdk-go/api"
89
)
910

@@ -23,6 +24,9 @@ type Commander interface {
2324
Login(string, bool, string, string) error
2425
Logout() error
2526
Whoami(bool) error
27+
TokensList(int) error
28+
TokensAdd(*drycc.Client, string, string, string, string, bool) (*api.AuthTokenResponse, error)
29+
TokensRemove(string, string) error
2630
BuildsList(string, int) error
2731
BuildsCreate(string, string, string, string, string) error
2832
CanaryInfo(string) error

cmd/token.go

+166
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
package cmd
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"net/url"
7+
"os/exec"
8+
"runtime"
9+
"time"
10+
11+
drycc "github.com/drycc/controller-sdk-go"
12+
"github.com/drycc/controller-sdk-go/api"
13+
"github.com/drycc/controller-sdk-go/auth"
14+
"github.com/drycc/controller-sdk-go/tokens"
15+
"github.com/drycc/workflow-cli/settings"
16+
)
17+
18+
func (d *DryccCmd) TokensList(results int) error {
19+
s, err := settings.Load(d.ConfigFile)
20+
21+
if err != nil {
22+
return err
23+
}
24+
25+
if results == defaultLimit {
26+
results = s.Limit
27+
}
28+
29+
tokens, _, err := tokens.List(s.Client, results)
30+
if d.checkAPICompatibility(s.Client, err) != nil {
31+
return err
32+
}
33+
table := d.getDefaultFormatTable([]string{"UUID", "OWNER", "ALIAS", "KEY", "CREATE", "UPDATED"})
34+
for _, token := range tokens {
35+
table.Append([]string{
36+
token.UUID,
37+
token.Owner,
38+
safeGetString(token.Alias),
39+
token.Key,
40+
token.Created,
41+
token.Updated,
42+
})
43+
}
44+
table.Render()
45+
return nil
46+
}
47+
48+
func (d *DryccCmd) TokensAdd(c *drycc.Client, username, password, alias, confirm string, render bool) (*api.AuthTokenResponse, error) {
49+
if c == nil {
50+
s, err := settings.Load(d.ConfigFile)
51+
52+
if err != nil {
53+
return nil, err
54+
}
55+
c = s.Client
56+
}
57+
58+
if confirm == "" {
59+
d.Printf(` ! WARNING: Make sure to copy your token now.
60+
! You won't be able to see it again, please confirm whether to continue.
61+
! To proceed, type "yes" !
62+
63+
> `)
64+
65+
fmt.Scanln(&confirm)
66+
}
67+
68+
if confirm != "yes" {
69+
return nil, fmt.Errorf("cancel the creation of %s's token, aborting", username)
70+
}
71+
72+
key, err := auth.Login(c, username, password)
73+
if d.checkAPICompatibility(c, err) != nil {
74+
return nil, err
75+
}
76+
if err != nil {
77+
return nil, err
78+
}
79+
if username == "" || password == "" {
80+
fmt.Printf("Opening browser to %s\n", key)
81+
d.Print("Waiting for login... ")
82+
err = d.openBrower(key)
83+
if err != nil {
84+
d.Print("Cannot open browser, please visit the website in yourself")
85+
}
86+
u, err := url.Parse(key)
87+
if err != nil {
88+
return nil, err
89+
}
90+
key = u.Query()["key"][0]
91+
}
92+
quit := progress(d.WOut)
93+
token, err := d.doToken(c, key, alias)
94+
quit <- true
95+
<-quit
96+
if render {
97+
table := d.getDefaultFormatTable([]string{"USERNAME", "TOKEN"})
98+
table.Append([]string{token.Username, token.Token})
99+
table.Render()
100+
}
101+
return token, err
102+
}
103+
104+
func (d *DryccCmd) TokensRemove(id, confirm string) error {
105+
s, err := settings.Load(d.ConfigFile)
106+
107+
if err != nil {
108+
return err
109+
}
110+
111+
if confirm == "" {
112+
d.Printf(` ! WARNING: You cannot undo this action.
113+
! Any using this token will no longer be able to access the Controller API.
114+
! To proceed, type "yes" !
115+
116+
> `)
117+
118+
fmt.Scanln(&confirm)
119+
}
120+
121+
if confirm != "yes" {
122+
d.Println("skip")
123+
return nil
124+
}
125+
126+
err = tokens.Delete(s.Client, id)
127+
if err != nil {
128+
return err
129+
}
130+
d.Println("done")
131+
return nil
132+
}
133+
134+
func (d *DryccCmd) openBrower(URL string) error {
135+
var commands = map[string]string{
136+
"windows": "start",
137+
"darwin": "open",
138+
"linux": "xdg-open",
139+
}
140+
run, ok := commands[runtime.GOOS]
141+
if !ok {
142+
return errors.New("warning: Cannot open browser")
143+
}
144+
cmd := exec.Command(run, URL)
145+
err := cmd.Start()
146+
if err != nil {
147+
return errors.New("warning: Cannot open browser")
148+
}
149+
150+
return nil
151+
}
152+
153+
func (d *DryccCmd) doToken(c *drycc.Client, key, alias string) (*api.AuthTokenResponse, error) {
154+
var token api.AuthTokenResponse
155+
for i := 0; i <= 120; i++ {
156+
token, _ = auth.Token(c, key, alias)
157+
time.Sleep(time.Duration(5) * time.Second)
158+
if token.Token != "" && token.Username != "" {
159+
break
160+
}
161+
}
162+
if token.Token == "" || token.Token == "fail" {
163+
return nil, errors.New("logged fail")
164+
}
165+
return &token, nil
166+
}

cmd/token_test.go

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package cmd
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"net/http"
7+
"testing"
8+
9+
"github.com/stretchr/testify/assert"
10+
11+
"github.com/drycc/workflow-cli/pkg/testutil"
12+
)
13+
14+
func TestTokensList(t *testing.T) {
15+
t.Parallel()
16+
cf, server, err := testutil.NewTestServerAndClient()
17+
if err != nil {
18+
t.Fatal(err)
19+
}
20+
defer server.Close()
21+
var b bytes.Buffer
22+
cmdr := DryccCmd{WOut: &b, ConfigFile: cf}
23+
24+
server.Mux.HandleFunc("/v2/tokens/", func(w http.ResponseWriter, _ *http.Request) {
25+
testutil.SetHeaders(w)
26+
fmt.Fprintf(w, `{
27+
"count": 2,
28+
"next": null,
29+
"previous": null,
30+
"results": [
31+
{
32+
"uuid": "f71e3b18-e702-409e-bd7f-8fb0a66d7b12",
33+
"owner": "test",
34+
"alias": "",
35+
"fuzzy_key": "c8e74fa4cbf...e4954d602ec5ed19ba",
36+
"created": "2023-04-18T00:00:00UTC",
37+
"updated": "2023-04-19T00:00:00UTC"
38+
},
39+
{
40+
"uuid": "f71e3b18-e702-499e-bd7f-8fb0a66d7b12",
41+
"owner": "test",
42+
"alias": "test",
43+
"fuzzy_key": "c8e74fa4cbf...e4954d60cec5ed19ba",
44+
"created": "2023-04-18T10:00:00UTC",
45+
"updated": "2023-04-19T12:00:00UTC"
46+
}
47+
]
48+
}`)
49+
})
50+
51+
err = cmdr.TokensList(-1)
52+
assert.NoError(t, err)
53+
54+
assert.Equal(t, b.String(), `UUID OWNER ALIAS KEY CREATE UPDATED
55+
f71e3b18-e702-409e-bd7f-8fb0a66d7b12 test <none> c8e74fa4cbf...e4954d602ec5ed19ba 2023-04-18T00:00:00UTC 2023-04-19T00:00:00UTC
56+
f71e3b18-e702-499e-bd7f-8fb0a66d7b12 test test c8e74fa4cbf...e4954d60cec5ed19ba 2023-04-18T10:00:00UTC 2023-04-19T12:00:00UTC
57+
`, "output")
58+
}
59+
60+
func TestTokenDelete(t *testing.T) {
61+
t.Parallel()
62+
cf, server, err := testutil.NewTestServerAndClient()
63+
if err != nil {
64+
t.Fatal(err)
65+
}
66+
defer server.Close()
67+
var b bytes.Buffer
68+
cmdr := DryccCmd{WOut: &b, ConfigFile: cf}
69+
70+
server.Mux.HandleFunc("/v2/tokens/f71e3b18-e702-499e-bd7f-8fb0a66d7b12/", func(w http.ResponseWriter, r *http.Request) {
71+
testutil.SetHeaders(w)
72+
w.WriteHeader(204)
73+
})
74+
75+
err = cmdr.TokensRemove("f71e3b18-e702-499e-bd7f-8fb0a66d7b12", "yes")
76+
77+
assert.NoError(t, err)
78+
assert.Equal(t, b.String(), "done\n")
79+
}

drycc.go

+3
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ Subcommands, use 'drycc help [subcommand]' to learn more:
6767
tags manage tags for application containers
6868
tls manage TLS settings for applications
6969
users manage users
70+
tokens manage tokens
7071
version display client version
7172
services manage services for your applications
7273
routes manage routes for your applications
@@ -166,6 +167,8 @@ Use 'git push drycc main' to deploy to an application.
166167
err = parser.TLS(argv, &cmdr)
167168
case "users":
168169
err = parser.Users(argv, &cmdr)
170+
case "tokens":
171+
err = parser.Tokens(argv, &cmdr)
169172
case "version":
170173
err = parser.Version(argv, &cmdr)
171174
case "volumes":

0 commit comments

Comments
 (0)