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

implement custom templated launch for sso browser option #750

Merged
merged 3 commits into from
Sep 24, 2024
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
1 change: 1 addition & 0 deletions pkg/assume/entrypoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ func GetCliApp() *cli.App {
if err != nil {
return err
}

if !hasSetup {
browserName, err := browser.HandleBrowserWizard(c)
if err != nil {
Expand Down
7 changes: 6 additions & 1 deletion pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ type Config struct {
// and CustomBrowserPath fields.
AWSConsoleBrowserLaunchTemplate *BrowserLaunchTemplate `toml:",omitempty"`

// SSOBrowserLaunchTemplate is an optional launch template to use
// for opening the SSO auth flow. If specified it overrides the DefaultBrowser
// and CustomSSOBrowserPath fields.
SSOBrowserLaunchTemplate *BrowserLaunchTemplate `toml:",omitempty"`

Keyring *KeyringConfig `toml:",omitempty"`
Ordering string
ExportCredentialSuffix string
Expand Down Expand Up @@ -280,7 +285,7 @@ func Load() (*Config, error) {
_, err = toml.NewDecoder(file).Decode(&c)
if err != nil {
// if there is an error just reset the file
return &c, nil
return nil, err
}
return &c, nil
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/granted/console.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ var ConsoleCommand = cli.Command{
}

if startErr != nil {
return clierr.New(fmt.Sprintf("Granted was unable to open a browser session automatically due to the following error: %s", err.Error()),
return clierr.New(fmt.Sprintf("Granted was unable to open a browser session automatically due to the following error: %s", startErr.Error()),
// allow them to try open the url manually
clierr.Info("You can open the browser session manually using the following url:"),
clierr.Info(consoleURL),
Expand Down
44 changes: 43 additions & 1 deletion pkg/idclogin/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@ package idclogin

import (
"context"
"errors"
"fmt"
"os/exec"
"time"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ssooidc"
"github.com/common-fate/clio"
"github.com/common-fate/clio/clierr"
grantedConfig "github.com/common-fate/granted/pkg/config"
"github.com/common-fate/granted/pkg/forkprocess"
"github.com/common-fate/granted/pkg/launcher"
"github.com/common-fate/granted/pkg/securestorage"
"github.com/pkg/browser"
)
Expand Down Expand Up @@ -54,7 +59,44 @@ func Login(ctx context.Context, cfg aws.Config, startUrl string, scopes []string
return nil, err
}

if config.CustomSSOBrowserPath != "" {
if config.SSOBrowserLaunchTemplate != nil {
l, err := launcher.CustomFromLaunchTemplate(config.SSOBrowserLaunchTemplate, []string{})
if err == launcher.ErrLaunchTemplateNotConfigured {
return nil, errors.New("error configuring custom browser, ensure that [SSOBrowserLaunchTemplate] is specified in your Granted config file")
}
if err != nil {
return nil, err
}

// now build the actual command to run - e.g. 'firefox --new-tab <URL>'
args, err := l.LaunchCommand(url, "")
if err != nil {
return nil, fmt.Errorf("error building browser launch command: %w", err)
}

var startErr error
if l.UseForkProcess() {
clio.Debugf("running command using forkprocess: %s", args)
cmd, err := forkprocess.New(args...)
if err != nil {
return nil, err
}
startErr = cmd.Start()
} else {
clio.Debugf("running command without forkprocess: %s", args)
cmd := exec.Command(args[0], args[1:]...)
startErr = cmd.Start()
}

if startErr != nil {
return nil, clierr.New(fmt.Sprintf("Granted was unable to open a browser session automatically due to the following error: %s", startErr.Error()),
// allow them to try open the url manually
clierr.Info("You can open the browser session manually using the following url:"),
clierr.Info(url),
)
}

} else if config.CustomSSOBrowserPath != "" {
cmd := exec.Command(config.CustomSSOBrowserPath, url)
err = cmd.Start()
if err != nil {
Expand Down
24 changes: 23 additions & 1 deletion pkg/launcher/custom.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package launcher
import (
"errors"
"fmt"
"regexp"
"strings"
"text/template"

Expand Down Expand Up @@ -52,10 +53,31 @@ func (l Custom) LaunchCommand(url string, profile string) ([]string, error) {
return nil, fmt.Errorf("executing command template (check that your browser launch template is valid in your Granted config): %w", err)
}

commandParts := strings.Fields(renderedCommand.String())
commandParts := splitCommand(renderedCommand.String())
return commandParts, nil
}

// splits each component of the command. Anything within quotes will be handled as one component of the command
// eg open -a "Google Chrome" <URL> returns ["open", "-a", "Google Chrome", "<URL>"]
func splitCommand(command string) []string {

re := regexp.MustCompile(`"([^"]+)"|(\S+)`)
matches := re.FindAllStringSubmatch(command, -1)

var result []string
for _, match := range matches {

if match[1] != "" {
result = append(result, match[1])
} else {

result = append(result, match[2])
}
}

return result
}

func (l Custom) UseForkProcess() bool { return l.ForkProcess }

var ErrLaunchTemplateNotConfigured = errors.New("launch template is not configured")
Expand Down
Loading