Skip to content
This repository has been archived by the owner on Mar 16, 2024. It is now read-only.

Commit

Permalink
Introduce a login retry when a user's token is expired
Browse files Browse the repository at this point in the history
Additionally, a user-agent string is added to requests to manager.

Signed-off-by: Donnie Adams <donnie@acorn.io>
  • Loading branch information
thedadams committed Jul 24, 2023
1 parent c1b7f44 commit 177dc44
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 47 deletions.
53 changes: 20 additions & 33 deletions pkg/cli/credential_login.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,52 +96,39 @@ func (a *CredentialLogin) Run(cmd *cobra.Command, args []string) error {
return err
}

if isManager {
user, pass, err := manager.Login(cmd.Context(), a.Password, serverAddress)
if err != nil {
if !isManager {
if err = survey.Ask(q, a); err != nil {
return err
}
a.Username = user
a.Password = pass
a.LocalStorage = true
a.SkipChecks = true
} else {
if err := survey.Ask(q, a); err != nil {
return err

if !a.LocalStorage {
client, err = a.client.CreateDefault()
if err != nil {
return err
}
}
}

if !a.LocalStorage {
client, err = a.client.CreateDefault()
store, err := credentials.NewStore(cfg, client)
if err != nil {
return err
}
}

store, err := credentials.NewStore(cfg, client)
if err != nil {
return err
}

err = store.Add(cmd.Context(), apiv1.Credential{
ServerAddress: serverAddress,
Username: a.Username,
Password: &a.Password,
LocalStorage: a.LocalStorage,
}, a.SkipChecks)
if err != nil {
return err
}

if isManager {
// reload config, could have changed
cfg, err = a.client.Options().CLIConfig()
if err = store.Add(cmd.Context(), apiv1.Credential{
ServerAddress: serverAddress,
Username: a.Username,
Password: &a.Password,
LocalStorage: a.LocalStorage,
}, a.SkipChecks); err != nil {
return err
}
} else {
a.Username, a.Password, err = manager.Login(cmd.Context(), cfg, a.Password, serverAddress)
if err != nil {
return err
}

var projectSet bool
def, err := manager.DefaultProject(cmd.Context(), serverAddress, a.Username, a.Password)
def, err := manager.DefaultProject(cmd.Context(), serverAddress, a.Password)
if err != nil {
return err
}
Expand Down
34 changes: 26 additions & 8 deletions pkg/manager/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,18 @@ import (
"fmt"
"io"
"net/http"
"runtime"

"github.com/acorn-io/runtime/pkg/version"
"github.com/sirupsen/logrus"
)

var ErrTokenNotFound = fmt.Errorf("token not found")
var (
ErrTokenNotFound = fmt.Errorf("token not found")
ErrForbidden = fmt.Errorf("forbidden")

userAgent = fmt.Sprintf("acorn/%s (%s; %s)", version.Get().String(), runtime.GOOS, runtime.GOARCH)
)

type tokenRequest struct {
Spec tokenRequestSpec `json:"spec,omitempty"`
Expand Down Expand Up @@ -46,11 +53,10 @@ type accountStatus struct {

func httpDelete(ctx context.Context, url, token string) {
logrus.Debugf("Delete %s", url)
req, err := http.NewRequest(http.MethodDelete, url, nil)
req, err := newRequest(url, http.MethodDelete, token)
if err != nil {
return
}
req.Header.Set("Authorization", "Bearer "+token)

resp, err := http.DefaultClient.Do(req.WithContext(ctx))
if err != nil {
Expand All @@ -61,15 +67,11 @@ func httpDelete(ctx context.Context, url, token string) {

func httpGet(ctx context.Context, url, token string, into interface{}) error {
logrus.Debugf("Looking up %s", url)
req, err := http.NewRequest(http.MethodGet, url, nil)
req, err := newRequest(url, http.MethodGet, token)
if err != nil {
return err
}

if token != "" {
req.Header.Add("Authorization", "Bearer "+token)
}

resp, err := http.DefaultClient.Do(req.WithContext(ctx))
if err != nil {
return err
Expand All @@ -81,6 +83,8 @@ func httpGet(ctx context.Context, url, token string, into interface{}) error {
break
case http.StatusNotFound:
return fmt.Errorf("%w: %v", ErrTokenNotFound, resp.StatusCode)
case http.StatusForbidden:
return ErrForbidden
default:
return fmt.Errorf("invalid status code: %v", resp.StatusCode)
}
Expand All @@ -94,3 +98,17 @@ func httpGet(ctx context.Context, url, token string, into interface{}) error {

return json.Unmarshal(body, into)
}

func newRequest(url, method, token string) (*http.Request, error) {
req, err := http.NewRequest(method, url, nil)
if err != nil {
return nil, err
}

if token != "" {
req.Header.Add("Authorization", "Bearer "+token)
}
req.Header.Add("User-Agent", userAgent)

return req, nil
}
32 changes: 29 additions & 3 deletions pkg/manager/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import (
"time"

"github.com/acorn-io/baaah/pkg/randomtoken"
apiv1 "github.com/acorn-io/runtime/pkg/apis/api.acorn.io/v1"
"github.com/acorn-io/runtime/pkg/config"
"github.com/acorn-io/runtime/pkg/credentials"
"github.com/acorn-io/runtime/pkg/system"
"github.com/pkg/browser"
"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -77,7 +79,7 @@ func ProjectURL(ctx context.Context, serverAddress, accountName, token string) (
return obj.Status.EndpointURL, nil
}

func Login(ctx context.Context, password, address string) (user string, pass string, err error) {
func Login(ctx context.Context, cfg *config.CLIConfig, password, address string) (user string, pass string, err error) {
passwordIsSpecified := password != ""
if !passwordIsSpecified {
password, err = randomtoken.Generate()
Expand Down Expand Up @@ -106,7 +108,9 @@ func Login(ctx context.Context, password, address string) (user string, pass str
}
if tokenRequest.Status.Token != "" {
httpDelete(ctx, tokenRequestURL, tokenRequest.Status.Token)
return tokenRequest.Spec.AccountName, tokenRequest.Status.Token, nil
user = tokenRequest.Spec.AccountName
pass = tokenRequest.Status.Token
break
} else {
logrus.Debugf("tokenRequest.Status.Token is empty")
}
Expand All @@ -122,9 +126,31 @@ func Login(ctx context.Context, password, address string) (user string, pass str
return "", "", ctx.Err()
}
}

store, err := credentials.NewStore(cfg, nil)
if err != nil {
return user, pass, err
}

if err = store.Add(ctx, apiv1.Credential{
ServerAddress: address,
Username: user,
Password: &pass,
LocalStorage: true,
}, true); err != nil {
return user, pass, err
}

// reload config, could have changed
if newCfg, err := config.ReadCLIConfig(false); err != nil {
return user, pass, err
} else {
*cfg = *newCfg
}
return user, pass, nil
}

func DefaultProject(ctx context.Context, address, user, token string) (string, error) {
func DefaultProject(ctx context.Context, address, token string) (string, error) {
projects, err := Projects(ctx, address, token)
if err != nil {
return "", err
Expand Down
18 changes: 15 additions & 3 deletions pkg/project/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package project

import (
"context"
"errors"
"fmt"
"regexp"
"strings"
Expand Down Expand Up @@ -213,9 +214,20 @@ func getClient(ctx context.Context, cfg *config.CLIConfig, opts Options, project
New: func() (client.Client, error) {
url := cfg.ProjectURLs[server+"/"+account]
if url == "" {
url, err = manager.ProjectURL(ctx, server, account, cred.Password)
if err != nil {
return nil, err
loginRetry := true
for {
url, err = manager.ProjectURL(ctx, server, account, cred.Password)
if loginRetry && errors.Is(err, manager.ErrForbidden) {
cred.Username, cred.Password, err = manager.Login(ctx, cfg, "", server)
if err != nil {
return nil, err
}
loginRetry = false
continue
} else if err != nil {
return nil, err
}
break
}
}
return client.New(&rest.Config{
Expand Down

0 comments on commit 177dc44

Please sign in to comment.