Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions cmd/engflow_auth/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,11 @@ func (r *appState) import_(cliCtx *cli.Context) error {
storeURLs = append(storeURLs, clusterURL)
}

// Check early if the keyring works.
if err := r.testKeyringBeforeStore(storeURLs[0].Host); err != nil {
return err
}

var storeErrs []error
for _, storeURL := range storeURLs {
if err := r.storeToken(storeURL.Host, token.Token); err != nil {
Expand Down Expand Up @@ -228,6 +233,11 @@ func (r *appState) login(cliCtx *cli.Context) error {
}
oauthURL := oauthEndpoint(clusterURL)

// Check early if the keyring works.
if err := r.testKeyringBeforeStore(clusterURL.Host); err != nil {
return err
}

// Tokens fetched during the process will be associated with each URL in
// storeURLs in the token store
storeURLs := []*url.URL{clusterURL}
Expand Down
23 changes: 23 additions & 0 deletions cmd/engflow_auth/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,20 @@ func TestRun(t *testing.T) {
wantCode: autherr.CodeAuthFailure,
wantErr: "fetch_token_fail",
},
{
desc: "login token load failure",
args: []string{"login", "cluster.example.com"},
authenticator: &fakeAuth{
deviceResponse: &oauth2.DeviceAuthResponse{
VerificationURIComplete: "https://cluster.example.com/with/auth/code",
},
},
keyringStore: &oauthtoken.FakeTokenStore{
LoadErr: errors.New("token_load_fail"),
},
wantCode: autherr.CodeTokenStoreFailure,
wantErr: "-store=file", // error message recommends -store=file
},
{
desc: "login token store failure",
args: []string{"login", "cluster.example.com"},
Expand Down Expand Up @@ -628,6 +642,15 @@ func TestRun(t *testing.T) {
machineInput: strings.NewReader(`{"token":{"access_token":"token_data"},"cluster_host":"cluster.example.com"}`),
keyringStore: oauthtoken.NewFakeTokenStore().WithPanic("do not call"),
},
{
desc: "import with keyring load failure",
args: []string{"login", "cluster.example.com"},
keyringStore: &oauthtoken.FakeTokenStore{
LoadErr: errors.New("token_load_fail"),
},
wantCode: autherr.CodeTokenStoreFailure,
wantErr: "-store=file", // error message recommends -store=file
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
Expand Down
23 changes: 23 additions & 0 deletions cmd/engflow_auth/tokens.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,34 @@ import (
"io/fs"
"os"

"github.com/EngFlow/auth/internal/autherr"
"github.com/EngFlow/auth/internal/oauthtoken"
"github.com/golang-jwt/jwt/v5"
"golang.org/x/oauth2"
)

// testKeyringBeforeStore attempts to load a token for the given hostname from
// the encrypted keyring.
//
// If the file store is being used (-store=file) or if the token is missing or
// is returned successfully, this method returns nil.
//
// If there's an error loading the token, this method returns an actionable
// error, suggesting the user try -store=file.
//
// This should be called by commands that write the token store, before
// the user goes through the web login flow.
func (r *appState) testKeyringBeforeStore(hostname string) error {
if r.writeFileStore {
// Not using keyring.
return nil
}
if _, err := r.keyringStore.Load(hostname); err != nil && !errors.Is(err, fs.ErrNotExist) {
return autherr.CodedErrorf(autherr.CodeTokenStoreFailure, "failed to initialize encrypted keyring: %w\n\tNon-interactive environments typically don't support encrypted keyrings.\n\tUse -store=file to use unencrypted storage instead.", err)
}
return nil
}

// loadToken loads a token for the given cluster or returns an error equivalent
// to fs.ErrNotExist if the token is not found in any store.
//
Expand Down
15 changes: 14 additions & 1 deletion internal/oauthtoken/fake.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package oauthtoken

import (
"errors"
"fmt"
"io/fs"
"time"
Expand Down Expand Up @@ -53,7 +54,7 @@ func (f *FakeTokenStore) Load(cluster string) (*oauth2.Token, error) {
}
token, ok := f.Tokens[cluster]
if !ok {
return nil, fmt.Errorf("%s: token not found", cluster)
return nil, &tokenNotFoundError{cluster: cluster}
}
return token, nil
}
Expand Down Expand Up @@ -133,3 +134,15 @@ func NewFakeTokenForSubject(subject string) *oauth2.Token {
Expiry: expiry,
}
}

type tokenNotFoundError struct {
cluster string
}

func (e *tokenNotFoundError) Error() string {
return fmt.Sprintf("%s: token not found", e.cluster)
}

func (e *tokenNotFoundError) Is(err error) bool {
return errors.Is(err, fs.ErrNotExist)
}