Skip to content

Commit

Permalink
engflow_auth: Add import subcommand
Browse files Browse the repository at this point in the history
This change adds an `import` subcommand that can store data exported
from `engflow_auth export` read from stdin.

Some usage/UX notes:
* there is purposely no option to read the data from `argv`, to
  discourage accidental insecure use. To "work around" this, users could
  do something like `echo "TOKEN DATA" | engflow_auth import`.
* since we have no way to list credentials (yet - we would need to
  upstream this in the libraries we use - doesn't look impossible) -
  aliases must either be set when exporting the credentials, or added to
  the payload afterward (i.e. with `jq`), though this latter way is not
  guaranteed to be supported forever.

Bug: linear/CUS-367
Tested: Round-tripped payload through export and import:

```
bazel-bin/cmd/engflow_auth/engflow_auth_/engflow_auth \
  export \
  --alias kyanite.local.engflow.com:8982 \
  kyanite.cluster.engflow.com \
| bazel-bin/cmd/engflow_auth/engflow_auth_/engflow_auth \
  import
```
  • Loading branch information
minor-fixes committed Jul 31, 2024
1 parent ddfce52 commit 8995485
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 0 deletions.
48 changes: 48 additions & 0 deletions cmd/engflow_auth/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,49 @@ func (r *appState) export(cliCtx *cli.Context) error {
return nil
}

func (r *appState) import_(cliCtx *cli.Context) error {
ctx := cliCtx.Context

var token ExportedToken
if err := json.NewDecoder(cliCtx.App.Reader).Decode(&token); err != nil {
return autherr.CodedErrorf(autherr.CodeBadParams, "failed to unmarshal token data from stdin: %w", err)
}

toValidate := append([]string{token.ClusterHost}, token.Aliases...)
var storeURLs []*url.URL
for _, u := range toValidate {
clusterURL, err := sanitizedURL(u)
if err != nil {
return autherr.CodedErrorf(autherr.CodeBadParams, "token data contains invalid cluster: %w", err)
}
storeURLs = append(storeURLs, clusterURL)
}

var storeErrs []error
for _, storeURL := range storeURLs {
if err := r.tokenStore.Store(ctx, storeURL.Host, token.Token); err != nil {
storeErrs = append(storeErrs, fmt.Errorf("failed to save token for host %q: %w", storeURL.Host, err))
}
}

if err := errors.Join(storeErrs...); err != nil {
return autherr.CodedErrorf(
autherr.CodeTokenStoreFailure,
"%d token store operation(s) failed:\n%v",
len(storeErrs),
err,
)
}

fmt.Fprintf(
cliCtx.App.ErrWriter,
"Successfully saved credentials for %[1]s.\nTo use, ensure the line below is in your .bazelrc:\n\n\tbuild --credential_helper=%[1]s=%s\n",
storeURLs[0].Hostname(),
os.Args[0])

return nil
}

func (r *appState) login(cliCtx *cli.Context) error {
ctx := cliCtx.Context

Expand Down Expand Up @@ -243,6 +286,11 @@ credential helper protocol.`),
},
},
},
{
Name: "import",
Usage: "Imports a data blob containing auth token(s) exported via `engflow_auth export` from stdin",
Action: root.import_,
},
{
Name: "login",
Usage: "Generate and store credentials for a particular cluster",
Expand Down
43 changes: 43 additions & 0 deletions cmd/engflow_auth/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,49 @@ func TestRun(t *testing.T) {
`,"aliases":["cluster.local.example.com:8080"]`, // Should have aliases
},
},
{
desc: "import with no data",
args: []string{"import"},
machineInput: strings.NewReader(""),
wantCode: autherr.CodeBadParams,
wantErr: "failed to unmarshal token data from stdin",
},
{
desc: "import with valid data",
args: []string{"import"},
machineInput: strings.NewReader(`{"token":{"access_token":"token_data"},"cluster_host":"cluster.example.com"}`),
tokenStore: &fakeStore{},
wantStoreCallsFor: []string{
"cluster.example.com",
},
},
{
desc: "import with alias",
args: []string{"import"},
machineInput: strings.NewReader(`{"token":{"access_token":"token_data"},"cluster_host":"cluster.example.com","aliases":["cluster.local.example.com"]}`),
tokenStore: &fakeStore{},
wantStoreCallsFor: []string{
"cluster.example.com",
"cluster.local.example.com",
},
},
{
desc: "import with store error",
args: []string{"import"},
machineInput: strings.NewReader(`{"token":{"access_token":"token_data"},"cluster_host":"cluster.example.com"}`),
tokenStore: &fakeStore{
storeErr: errors.New("token_store_fail"),
},
wantCode: autherr.CodeTokenStoreFailure,
wantErr: "token_store_fail",
},
{
desc: "import with invalid cluster",
args: []string{"import"},
machineInput: strings.NewReader(`{"token":{"access_token":"token_data"},"cluster_host":"grpcs://cluster.example.com:8080"}`),
wantCode: autherr.CodeBadParams,
wantErr: "token data contains invalid cluster",
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
Expand Down

0 comments on commit 8995485

Please sign in to comment.