Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support SSO (Single Sign-On) #1010

Merged
merged 115 commits into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
115 commits
Select commit Hold shift + click to select a range
342f17e
feat(authenticate): support pkce
Integralist Sep 1, 2023
00e522a
refactor: authentication
Integralist Sep 4, 2023
4c6afbc
fix: acquire session token from api endpoint
Integralist Sep 4, 2023
53735b3
feat(undocumented): support debug output
Integralist Sep 5, 2023
1bde0e0
fix(app): avoid auth for config command
Integralist Sep 5, 2023
f828637
fix: acquire api token
Integralist Sep 5, 2023
e22ce56
feat(auth): implement refresh behaviour
Integralist Sep 5, 2023
1659e40
refactor: move FastlyAPIClient out of app package
Integralist Sep 5, 2023
2549adc
refactor: variable naming
Integralist Sep 5, 2023
b6972c0
refactor: display profile name
Integralist Sep 5, 2023
0db7fcf
refactor: clarify tokenless commands
Integralist Sep 5, 2023
4b77ceb
doc(testing): explain delve debugging
Integralist Sep 6, 2023
97d8d51
tests: fix
Integralist Sep 6, 2023
b1658eb
refactor(app): move kingpin config
Integralist Sep 7, 2023
7912ed7
refactor(app): small doc clean-ups
Integralist Sep 7, 2023
a8d68be
refactor: support flag/env-var override for accounts endpoint
Integralist Sep 7, 2023
c0314a7
refactor: move token exchange to separate function
Integralist Sep 7, 2023
79946be
refactor: move auth logic to separate functions
Integralist Sep 7, 2023
371d4db
fix(config): update config version
Integralist Sep 7, 2023
da3c322
refactor(app): make prompt less scary
Integralist Sep 7, 2023
25e27f8
test: fix whoami test
Integralist Sep 7, 2023
691761d
fix: support skipping OAuth
Integralist Sep 8, 2023
2ca5124
refactor(app): move more logic from Run function
Integralist Sep 8, 2023
58c5471
refactor(app): move more logic to separate functions + support new en…
Integralist Sep 8, 2023
43d4e9c
fix(profile/create): support OAuth token flow
Integralist Sep 8, 2023
2356009
refactor(authenticate): move new default logic to separate function
Integralist Sep 8, 2023
abf1216
fix: allow profile override for authenticate command + skip auth prompt
Integralist Sep 8, 2023
b9f7bd4
fix: allow profile override for profile update command
Integralist Sep 8, 2023
07d43e3
refactor(profile/update): move static token flow to separate function
Integralist Sep 8, 2023
7d23c8b
fix(profile/update): support OAuth flow
Integralist Sep 8, 2023
5303645
fix(app): move endpoint display before token validation
Integralist Sep 11, 2023
ec13571
fix: avoid breaking change by switching SSO to be opt-in
Integralist Sep 11, 2023
4314e98
refactor(app): group token processing logic
Integralist Sep 11, 2023
d244549
refactor(profile/create): update prompt description
Integralist Sep 11, 2023
c02a914
fix: profile logic
Integralist Sep 11, 2023
45bffff
fix: resolve semgrep concern for modified variable
Integralist Sep 11, 2023
dcd8507
refactor(authenticate): move profile logic to separate function
Integralist Sep 11, 2023
06604ff
fix(app): wrap prompt in flag checks
Integralist Sep 12, 2023
1373930
doc(authenticate): explain prompt skip
Integralist Sep 12, 2023
47621bf
doc(authenticate): explain processProfiles logic
Integralist Sep 12, 2023
7abd86f
refactor(authenticate): move case conditionals to separate functions
Integralist Sep 12, 2023
b9b2a6e
doc(profile/update): clarify else if conditional
Integralist Sep 12, 2023
c465176
fix: set Default correctly when invoking authenticate command directly
Integralist Sep 12, 2023
977d110
refactor: clean-up profile.Get interface
Integralist Sep 12, 2023
89f6e02
fix: refactor profile.Default interface
Integralist Sep 12, 2023
a899b14
refactor: rename OAuth to SSO
Integralist Sep 12, 2023
fcdce4e
refactor: inject browser opener behaviour
Integralist Sep 12, 2023
72d7dee
refactor: inject auth server
Integralist Sep 12, 2023
c20b495
test(authenticate): add validation for authenticate command
Integralist Sep 12, 2023
b832964
doc(app): clarify env var usage
Integralist Sep 12, 2023
c65e4d7
fix: vcl/conditions test after rebase from main
Integralist Sep 13, 2023
fe115e2
refactor(app): hide token message with --quiet
Integralist Sep 13, 2023
84d33dc
feat: add Important text header
Integralist Sep 13, 2023
f7c2b3a
feat(profile): add SetADefault
Integralist Sep 13, 2023
a63e88b
test(authenticate): add more test assertions
Integralist Sep 13, 2023
2bbd7a9
refactor: support profile arg to authenticate command
Integralist Sep 13, 2023
a21fb9a
refactor: rename authenticate command to sso
Integralist Sep 13, 2023
24ea69d
doc: rename DEVELOP to DEVELOPMENT
Integralist Sep 13, 2023
acf1bb5
fix: split command name to avoid arguments being included
Integralist Sep 13, 2023
2405855
fix: use correct profile name after authentication
Integralist Sep 13, 2023
3a20b52
refactor: rename all instances of authenticate to sso
Integralist Sep 13, 2023
4a365a3
refactor(app): reword instructions for SSO opt-in
Integralist Sep 14, 2023
5293e69
refactor(profile/update): avoid excessive config writes
Integralist Sep 14, 2023
d2b26d6
refactor(sso): change text function depending on how command is invoked
Integralist Sep 14, 2023
10e7b33
feat(sso): validate azp/aud claims
Integralist Sep 14, 2023
3cf2297
fix: resolve linter feedback
Integralist Sep 14, 2023
6177f5f
refactor: rename testcase field
Integralist Sep 14, 2023
e8b5502
test: add more tests to validate pre-command token processing
Integralist Sep 14, 2023
c6a3c3f
refactor: move INFO output related to expired tokens to verbose mode
Integralist Sep 15, 2023
467f4ee
fix: set Authorization header alongside Fastly-Key
Integralist Sep 15, 2023
1c644df
doc: warn callers of memory concern with undocumented.Call()
Integralist Sep 15, 2023
2134a32
refactor(whoami): replace manual request with existing abstraction
Integralist Sep 15, 2023
9ec48d0
feat: FASTLY_DEBUG_MODE
Integralist Sep 15, 2023
f679115
refactor: remove unnecessary profile check
Integralist Sep 15, 2023
2623d24
fix(undocumented): print correct output
Integralist Sep 15, 2023
a623267
refactor: deduplicate JWT validation and token logic
Integralist Sep 15, 2023
dd77740
fix(profile/update): ensure SetDefault() is called
Integralist Sep 15, 2023
71eb748
fix: avoid breaking flow in profile commands
Integralist Sep 21, 2023
a81423c
fix(profile/update): put back the flow to how it was prior to sso
Integralist Sep 21, 2023
49eea6e
fix(testutil): correct annotations
Integralist Sep 22, 2023
eeea0ac
fix(undocumented): move response output to before error check
Integralist Nov 7, 2023
8c6e731
style(profile/update): add line breaks to info output
Integralist Nov 7, 2023
42edcda
fix(config): remove duplicate key
Integralist Nov 7, 2023
5291f7a
fix(app): move verbose flag out of signature
Integralist Nov 7, 2023
1e2b281
fix(app): add compute metadata to the no token switch
Integralist Nov 7, 2023
fb0f286
fix(tests): remove extra line break
Integralist Nov 7, 2023
b5a3303
fix(undocumented): remove unnecessary Authorization header
Integralist Nov 9, 2023
e88be83
fix(config): bump config_version
Integralist Nov 9, 2023
e91b946
refactor: rename Endpoint to APIEndpoint for clarity
Integralist Nov 9, 2023
258ae61
style(app): add line breaks
Integralist Nov 9, 2023
233b63b
doc(auth): add well-known path
Integralist Nov 9, 2023
26425c8
style: tweak line breaks
Integralist Nov 10, 2023
d2d677c
fix(sso): hide command until GA
Integralist Nov 10, 2023
ec87ab5
remove: SSO messaging until GA
Integralist Nov 10, 2023
8c3de5d
refactor(global): rename constants
Integralist Nov 10, 2023
2f72741
refactor(profile): hide sso flag until GA
Integralist Nov 10, 2023
59bd239
fix(auth): check type assert
Integralist Nov 10, 2023
59ff6ac
refactor(main): rename s to authServer
Integralist Nov 10, 2023
4c50a34
feat: store .well-known inside of auth.Server
Integralist Nov 10, 2023
cfb8449
refactor: store well-known as struct not bytes
Integralist Nov 10, 2023
d0552f7
refactor: rename Account to AccountEndpoint
Integralist Nov 10, 2023
45a5e55
refactor: move main logic to custom init
Integralist Nov 13, 2023
6ef7348
refactor: correct some linter items
Integralist Nov 13, 2023
253abe5
refactor: all the things
Integralist Nov 13, 2023
3d4b24c
fix: move functions inside auth server + set api endpoint
Integralist Nov 14, 2023
2cc61b7
remove(testutil): SetAccountEndpoint mock method
Integralist Nov 14, 2023
1dad712
fix: stop processing if user doesn't want to continue
Integralist Nov 14, 2023
6822a03
test: add DontWantOutput for TestSSO
Integralist Nov 14, 2023
2e53e0f
feat: add --enable-sso flag
Integralist Nov 14, 2023
fda9488
fix: don't os.Exit(1) for Yes/No
Integralist Nov 14, 2023
5160d5d
style(app): remove line break
Integralist Nov 14, 2023
01f5a7f
fix(app): don't auto SSO if no profiles
Integralist Nov 14, 2023
254e2a0
refactor: auth flow
Integralist Nov 14, 2023
b2ad781
refactor: naming of variables
Integralist Nov 15, 2023
de1ebb1
feat: support account endpoint override
Integralist Nov 15, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 2 additions & 1 deletion .fastly/config.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
config_version = 4
config_version = 5

[fastly]
account_endpoint = "https://accounts.fastly.com"
api_endpoint = "https://api.fastly.com"

[wasm-metadata]
Expand Down
5 changes: 0 additions & 5 deletions .tmpl/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,6 @@ type CreateCommand struct {

// Exec invokes the application logic for the command.
func (c *CreateCommand) Exec(in io.Reader, out io.Writer) error {
_, s := c.Globals.Token()
if s == config.SourceUndefined {
return errors.ErrNoToken
}

serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{
AutoCloneFlag: c.autoClone,
Client: c.Globals.Client,
Expand Down
5 changes: 0 additions & 5 deletions .tmpl/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,6 @@ type DeleteCommand struct {

// Exec invokes the application logic for the command.
func (c *DeleteCommand) Exec(in io.Reader, out io.Writer) error {
_, s := c.Globals.Token()
if s == config.SourceUndefined {
return errors.ErrNoToken
}

serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{
AutoCloneFlag: c.autoClone,
Client: c.Globals.Client,
Expand Down
5 changes: 0 additions & 5 deletions .tmpl/describe.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,6 @@ type DescribeCommand struct {

// Exec invokes the application logic for the command.
func (c *DescribeCommand) Exec(in io.Reader, out io.Writer) error {
_, s := c.Globals.Token()
if s == config.SourceUndefined {
return errors.ErrNoToken
}

serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{
AllowActiveLocked: true,
Client: c.Globals.Client,
Expand Down
5 changes: 0 additions & 5 deletions .tmpl/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,6 @@ type ListCommand struct {

// Exec invokes the application logic for the command.
func (c *ListCommand) Exec(in io.Reader, out io.Writer) error {
_, s := c.Globals.Token()
if s == config.SourceUndefined {
return errors.ErrNoToken
}

serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{
AllowActiveLocked: true,
Client: c.Globals.Client,
Expand Down
5 changes: 0 additions & 5 deletions .tmpl/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,6 @@ type UpdateCommand struct {

// Exec invokes the application logic for the command.
func (c *UpdateCommand) Exec(in io.Reader, out io.Writer) error {
_, s := c.Globals.Token()
if s == config.SourceUndefined {
return errors.ErrNoToken
}

serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{
AutoCloneFlag: c.autoClone,
Client: c.Globals.Client,
Expand Down
File renamed without changes.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@
</div>

## Quick links

- [Installation](https://developer.fastly.com/learning/tools/cli#installing)
- [Shell auto-completion](https://developer.fastly.com/learning/tools/cli#shell-auto-completion)
- [Configuring](https://developer.fastly.com/learning/tools/cli#configuring)
- [Commands](https://developer.fastly.com/reference/cli/#command-groups)
- [Development](DEVELOP.md)
- [Development](DEVELOPMENT.md)
- [Testing](TESTING.md)
- [Documentation](DOCUMENTATION.md)

Expand All @@ -27,8 +28,6 @@ Refer to [CONTRIBUTING.md](./CONTRIBUTING.md)
If you encounter any non-security-related bug or unexpected behavior, please [file an issue][bug]
using the bug report template.

[bug]: https://github.com/fastly/cli/issues/new?labels=bug&template=bug_report.md

Please also check the [CHANGELOG](./CHANGELOG.md) for any breaking-changes or migration guidance.

### Security issues
Expand All @@ -38,3 +37,5 @@ Please see our [SECURITY.md](SECURITY.md) for guidance on reporting security-rel
## License

[Apache 2.0](LICENSE).

[bug]: https://github.com/fastly/cli/issues/new?labels=bug&template=bug_report.md
18 changes: 18 additions & 0 deletions TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,21 @@ TEST_COMPUTE_INIT=1 TEST_COMPUTE_BUILD=1 TEST_COMPUTE_DEPLOY=1 TEST_COMMAND=gote
```

> **NOTE**: `TEST_COMMAND` is optional and allows the use of https://github.com/rakyll/gotest to improve test output.

### Debugging

To debug failing tests you can use [Delve](<>).

Essentially, `cd` into a package directory (where the `_test.go` file is you want to run) and then execute...

```
TEST_COMPUTE_BUILD=1 dlv test -- -test.v -test.run TestNameGoesHere
```

Once that is done, you can set breakpoints. For example:

```
break ../../app/run.go:152
```

> **NOTE:** The path is relative to the package directory you're running the test file.
134 changes: 12 additions & 122 deletions cmd/fastly/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,148 +3,38 @@ package main

import (
"errors"
"io"
"net/http"
"os"
"time"

"github.com/fastly/go-fastly/v8/fastly"
"github.com/fatih/color"

"github.com/fastly/cli/pkg/api"
"github.com/fastly/cli/pkg/app"
"github.com/fastly/cli/pkg/commands/compute"
"github.com/fastly/cli/pkg/config"
"github.com/fastly/cli/pkg/env"
fsterr "github.com/fastly/cli/pkg/errors"
"github.com/fastly/cli/pkg/github"
"github.com/fastly/cli/pkg/manifest"
"github.com/fastly/cli/pkg/sync"
"github.com/fastly/cli/pkg/text"
)

func main() {
// Parse the arguments provided by the user via the command-line interface.
args := os.Args[1:]

// Define a HTTP client that will be used for making arbitrary HTTP requests.
httpClient := &http.Client{Timeout: time.Minute * 2}

// Define the standard input/output streams.
var (
in io.Reader = os.Stdin
out io.Writer = sync.NewWriter(color.Output)
)

// Read relevant configuration options from the user's environment.
var e config.Environment
e.Read(env.Parse(os.Environ()))

// Identify verbose flag early (before Kingpin parser has executed) so we can
// print additional output related to the CLI configuration.
var verboseOutput bool
for _, seg := range args {
if seg == "-v" || seg == "--verbose" {
verboseOutput = true
}
}

// Identify auto-yes/non-interactive flag early (before Kingpin parser has
// executed) so we can handle the interactive prompts appropriately with
// regards to processing the CLI configuration.
var autoYes, nonInteractive bool
for _, seg := range args {
if seg == "-y" || seg == "--auto-yes" {
autoYes = true
}
if seg == "-i" || seg == "--non-interactive" {
nonInteractive = true
if err := app.Run(os.Args, os.Stdin); err != nil {
if skipExit := processErr(err, os.Args); skipExit {
return
}
}

// Extract a subset of configuration options from the local app directory.
var cfg config.File
cfg.SetAutoYes(autoYes)
cfg.SetNonInteractive(nonInteractive)
// The CLI relies on a valid configuration, otherwise we can't continue.
err := cfg.Read(config.FilePath, in, out, fsterr.Log, verboseOutput)
if err != nil {
fsterr.Deduce(err).Print(color.Error)
// WARNING: os.Exit will exit, and any `defer` calls will not be run.
os.Exit(1)
}
}

// Extract user's project configuration from the fastly.toml manifest.
var md manifest.Data
md.File.Args = args
md.File.SetErrLog(fsterr.Log)
md.File.SetOutput(out)

// NOTE: We skip handling the error because not all commands relate to Compute.
_ = md.File.Read(manifest.Filename)

// The `main` function is a shim for calling `app.Run()`.
err = app.Run(app.RunOpts{
APIClient: func(token, endpoint string, debugMode bool) (api.Interface, error) {
client, err := fastly.NewClientForEndpoint(token, endpoint)
if debugMode {
client.DebugMode = true
}
return client, err
},
Args: args,
ConfigFile: cfg,
ConfigPath: config.FilePath,
Env: e,
ErrLog: fsterr.Log,
ExecuteWasmTools: compute.ExecuteWasmTools,
HTTPClient: httpClient,
Manifest: &md,
Stdin: in,
Stdout: out,
Versioners: app.Versioners{
CLI: github.New(github.Opts{
HTTPClient: httpClient,
Org: "fastly",
Repo: "cli",
Binary: "fastly",
}),
Viceroy: github.New(github.Opts{
HTTPClient: httpClient,
Org: "fastly",
Repo: "viceroy",
Binary: "viceroy",
Version: md.File.LocalServer.ViceroyVersion,
}),
WasmTools: github.New(github.Opts{
HTTPClient: httpClient,
Org: "bytecodealliance",
Repo: "wasm-tools",
Binary: "wasm-tools",
External: true,
Nested: true,
}),
},
})

// processErr persists the error log to disk and deduces the error type.
func processErr(err error, args []string) (skipExit bool) {
// NOTE: We persist any error log entries to disk before attempting to handle
// a possible error response from app.Run as there could be errors recorded
// during the execution flow but were otherwise handled without bubbling an
// error back the call stack, and so if the user still experiences something
// unexpected we will have a record of any errors that happened along the way.
logErr := fsterr.Log.Persist(fsterr.LogPath, args)
logErr := fsterr.Log.Persist(fsterr.LogPath, args[1:])
if logErr != nil {
fsterr.Deduce(logErr).Print(color.Error)
}
if err != nil {
text.Break(out)
fsterr.Deduce(err).Print(color.Error)
exitError := fsterr.SkipExitError{}
if errors.As(err, &exitError) {
if exitError.Skip {
return // skip returning an error for 'help' output
}
}
os.Exit(1)
exitError := fsterr.SkipExitError{}
if errors.As(err, &exitError) {
return exitError.Skip
}
fsterr.Deduce(err).Print(color.Error)
return false
}
13 changes: 13 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@ require (

require (
github.com/fastly/go-fastly/v8 v8.6.4
github.com/hashicorp/cap v0.3.4
github.com/kennygrant/sanitize v1.2.4
github.com/mholt/archiver v3.1.1+incompatible
github.com/otiai10/copy v1.14.0
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
github.com/theckman/yacspin v0.13.12
golang.org/x/crypto v0.14.0
golang.org/x/exp v0.0.0-20231006140011-7918f672742d
Expand All @@ -47,23 +49,34 @@ require (

require (
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/coreos/go-oidc/v3 v3.5.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/jsonapi v1.0.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-hclog v1.4.0 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/klauspost/compress v1.16.6 // indirect
github.com/klauspost/pgzip v1.2.5 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/nwaples/rardecode v1.1.2 // indirect
github.com/peterhellberg/link v1.1.0 // indirect
github.com/pierrec/lz4/v4 v4.1.18 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.4.2 // indirect
github.com/ulikunitz/xz v0.5.11 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/oauth2 v0.5.0 // indirect
golang.org/x/sync v0.4.0 // indirect
golang.org/x/text v0.14.0
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading
Loading