Skip to content

Commit

Permalink
Support modifier flags such as '-c' being placed after the profile na…
Browse files Browse the repository at this point in the history
…me (#45)

* initial

* working

* added int flag implementation

* added test for new flag functions

* added back normalize function

* add back copy function

* update descriptions

Co-authored-by: meyerjrr <[email protected]>
  • Loading branch information
JoshuaWilkes and meyerjrr authored Feb 24, 2022
1 parent 66a8cec commit 3ee7eac
Show file tree
Hide file tree
Showing 8 changed files with 253 additions and 19 deletions.
3 changes: 2 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"request": "launch",
"mode": "auto",
"program": "cmd/assume/main.go",
"args": ["-c", "cf-dev", "-r=hello"],
"env": { "FORCE_NO_ALIAS": true }
},
{
Expand All @@ -27,7 +28,7 @@
"request": "launch",
"mode": "auto",
"program": "cmd/assume/main.go",
"args": ["-console", "-active-role"],
"args": ["test-role", "-c", "-s iam", "-r region-name"],
"env": {
"FORCE_NO_ALIAS": true,
"GRANTED_AWS_ROLE_PROFILE": "cf-dev"
Expand Down
1 change: 1 addition & 0 deletions cmd/assume/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
func main() {

app := assume.GetCliApp()

err := app.Run(os.Args)
if err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err)
Expand Down
8 changes: 8 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,14 @@ require (
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/kr/pretty v0.1.0 // indirect
github.com/kr/text v0.1.0 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/pascaldekloe/name v0.0.0-20180628100202-0fd16699aae1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/testify v1.7.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.20.0 // indirect
Expand All @@ -32,6 +37,7 @@ require (
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
)

require (
Expand All @@ -45,6 +51,7 @@ require (
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.5 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.7.0 // indirect
github.com/aws/smithy-go v1.10.0 // indirect
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d // indirect
github.com/danieljoos/wincred v1.1.2 // indirect
github.com/dvsekhvalnov/jose2go v1.5.0 // indirect
Expand All @@ -59,6 +66,7 @@ require (
github.com/olekukonko/tablewriter v0.0.5
github.com/russross/blackfriday/v2 v2.0.1 // indirect
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
github.com/urfave/cli v1.22.5
golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838 // indirect
golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
Expand Down
5 changes: 5 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLj
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/bigkevmcd/go-configparser v0.0.0-20210106142102-909504547ead h1:UhYWAphNveMty305skySR5ST/hbYDexgsgkhcy0MDhM=
github.com/bigkevmcd/go-configparser v0.0.0-20210106142102-909504547ead/go.mod h1:RI5D4DqbDX0Kb0SvKTuAKMYlkSBND3zLQZI/wiS5Ij0=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
Expand Down Expand Up @@ -116,6 +118,7 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfC
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.4 h1:5Myjjh3JY/NaAi4IsUbHADytDyl1VE1Y9PXDlL+P/VQ=
Expand Down Expand Up @@ -170,6 +173,8 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU=
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
Expand Down
19 changes: 13 additions & 6 deletions pkg/assume/assume.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,17 @@ import (
"github.com/common-fate/granted/pkg/cfaws"
"github.com/common-fate/granted/pkg/debug"
"github.com/common-fate/granted/pkg/testable"
cfflags "github.com/common-fate/granted/pkg/urfav_overrides"
"github.com/urfave/cli/v2"
)

func AssumeCommand(c *cli.Context) error {
// this custom behavious allows flags to be passed on either side of the role arg
// to access flags in this command, use assumeFlags.String("region") etc instead of c.String("region")
assumeFlags, err := cfflags.New("assumeFlags", GlobalFlags, c)
if err != nil {
return err
}
var wg sync.WaitGroup

withStdio := survey.WithStdio(os.Stdin, os.Stderr, os.Stderr)
Expand All @@ -39,9 +46,9 @@ func AssumeCommand(c *cli.Context) error {
}
}

activeRoleProfile := c.String("granted-active-aws-role-profile")
activeRoleProfile := assumeFlags.String("granted-active-aws-role-profile")
//set the sesh creds using the active role if we have one and the flag is set
if c.Bool("active-role") && activeRoleProfile != "" {
if assumeFlags.Bool("active-role") && activeRoleProfile != "" {
//try opening using the active role
fmt.Fprintf(os.Stderr, "Attempting to open using active role...\n")
profile = awsProfiles[activeRoleProfile]
Expand Down Expand Up @@ -102,14 +109,14 @@ func AssumeCommand(c *cli.Context) error {
region, _, err := profile.Region(c.Context)

isIamWithoutAssumedRole := profile.ProfileType == cfaws.ProfileTypeIAM && profile.RawConfig.RoleARN == ""
openBrower := c.Bool("console") || c.Bool("active-role")
openBrower := assumeFlags.Bool("console") || assumeFlags.Bool("active-role")
if openBrower && isIamWithoutAssumedRole {
// @TODO check if we can launch the console as an IAM user
fmt.Fprintf(os.Stderr, "\nCannot open a browser session for profile: %s because it does not assume a role\n", profile.Name)
} else if openBrower {
service := c.String("service")
if c.String("region") != "" {
region = c.String("region")
service := assumeFlags.String("service")
if assumeFlags.String("region") != "" {
region = assumeFlags.String("region")
}

labels.Region = region
Expand Down
24 changes: 12 additions & 12 deletions pkg/assume/entrypoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,29 +14,29 @@ import (
"github.com/urfave/cli/v2"
)

var GlobalFlags = []cli.Flag{
&cli.BoolFlag{Name: "console", Aliases: []string{"c"}, Usage: "Open a web console to the role"},
&cli.StringFlag{Name: "service", Aliases: []string{"s"}, Usage: "Specify a service to open the console into"},
&cli.StringFlag{Name: "region", Aliases: []string{"r"}, Usage: "Specify a region to open the console into"},
&cli.BoolFlag{Name: "active-role", Aliases: []string{"ar"}, Usage: "Open console using active role"},
&cli.BoolFlag{Name: "verbose", Usage: "Log debug messages"},
&cli.StringFlag{Name: "update-checker-api-url", Value: build.UpdateCheckerApiUrl, EnvVars: []string{"UPDATE_CHECKER_API_URL"}, Hidden: true},
&cli.StringFlag{Name: "granted-active-aws-role-profile", EnvVars: []string{"GRANTED_AWS_ROLE_PROFILE"}, Hidden: true},
}

func GetCliApp() *cli.App {
cli.VersionPrinter = func(c *cli.Context) {
fmt.Fprintln(os.Stderr, banners.WithVersion(banners.Assume()))
}

flags := []cli.Flag{
&cli.BoolFlag{Name: "console", Aliases: []string{"c"}, Usage: "Open a web console to the role"},
&cli.StringFlag{Name: "service", Aliases: []string{"s"}, Usage: "Specify a service to open the console into"},
&cli.StringFlag{Name: "region", Aliases: []string{"r"}, Usage: "Specify a region to open the console into"},
&cli.BoolFlag{Name: "active-role", Aliases: []string{"ar"}, Usage: "Open console using active role"},
&cli.BoolFlag{Name: "verbose", Usage: "Log debug messages"},
&cli.StringFlag{Name: "update-checker-api-url", Value: build.UpdateCheckerApiUrl, EnvVars: []string{"UPDATE_CHECKER_API_URL"}, Hidden: true},
&cli.StringFlag{Name: "granted-active-aws-role-profile", EnvVars: []string{"GRANTED_AWS_ROLE_PROFILE"}, Hidden: true},
}

app := &cli.App{
Name: "assume",
Usage: "https://granted.dev",
UsageText: "assume [options][Profile]",
Version: build.Version,
HideVersion: false,
Flags: flags,
Action: updates.WithUpdateCheck(AssumeCommand),
Flags: GlobalFlags,
Action: updates.WithUpdateCheck(func(c *cli.Context) error { return AssumeCommand(c) }),
EnableBashCompletion: true,
Before: func(c *cli.Context) error {
if c.Bool("verbose") {
Expand Down
53 changes: 53 additions & 0 deletions pkg/urfav_overrides/flagoverride_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package cfflags

import (
"fmt"
"os"
"testing"

"github.com/bmizerany/assert"
"github.com/urfave/cli/v2"
)

var testingFlags = []cli.Flag{
&cli.BoolFlag{Name: "testbool", Aliases: []string{"c"}, Usage: "Open a web console to the role"},
&cli.StringFlag{Name: "teststringservice", Aliases: []string{"s"}, Usage: "Specify a service to open the console into"},
&cli.StringFlag{Name: "teststringregion", Aliases: []string{"r"}, Usage: "Specify a service to open the console into"},
}

func TestFlagsPassToCFFlags(t *testing.T) {

app := cli.App{
Name: "test",

Flags: testingFlags,

Action: func(c *cli.Context) error {

assumeFlags, err := New("assumeFlags", testingFlags, c)
if err != nil {
return err
}

booloutcome := assumeFlags.Bool("testbool")

serviceoutcome := assumeFlags.String("teststringservice")
regionoutcome := assumeFlags.String("teststringregion")

assert.Equal(t, booloutcome, true)
assert.Equal(t, serviceoutcome, "iam")
assert.Equal(t, regionoutcome, "region-name")
return nil
},
EnableBashCompletion: true,
}

os.Args = []string{"", "test-role", "-c", "-s", "iam", "-r", "region-name"}

err := app.Run(os.Args)
if err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err)
os.Exit(1)
}

}
159 changes: 159 additions & 0 deletions pkg/urfav_overrides/flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package cfflags

import (
"errors"
"flag"
"io/ioutil"
"os"
"strconv"
"strings"

"github.com/urfave/cli/v2"
)

type Flags struct {
*flag.FlagSet
urFavFlags []cli.Flag
}

// The purpose of this package is to allow the assume cli command to accept flags on either side of the "role" arg
// for example, `assume -c my-role -region=us-east-1` by default, urfav-cli, the cli framework that we are using does not
// support this usage pattern.
//
// We have extracted some methods from the original urfav-cli library to mimic the original behaviour but processing all the flags.
// to use this in a command,
// This package interacts with os.Args directly
//
// allFlags := cfflags.New("name",GlobalFlagsList, c)
// allFlags.String("region")
func New(name string, flags []cli.Flag, c *cli.Context) (*Flags, error) {
set := flag.NewFlagSet(name, flag.ContinueOnError)
for _, f := range flags {
if err := f.Apply(set); err != nil {
return nil, err
}
}
set.SetOutput(ioutil.Discard)
ca := []string{}
ca = append(ca, c.Args().Slice()...)
// context.Args() for this command will ONLY contain the role and any flags provided after the role
// this slice of os.Args will only contain flags and not the role if it was provided
ag := []string{}
ag = append(ag, os.Args[1:len(os.Args)-len(ca)]...)
ag = append(ag, ca[1:]...)
err := normalizeFlags(flags, set)
if err != nil {
return nil, err
}
err = set.Parse(ag)
if err != nil {
return nil, err
}
return &Flags{FlagSet: set, urFavFlags: flags}, nil
}
func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) {
switch ff.Value.(type) {
case cli.Serializer:
_ = set.Set(name, ff.Value.(cli.Serializer).Serialize())
default:
_ = set.Set(name, ff.Value.String())
}
}

func normalizeFlags(flags []cli.Flag, set *flag.FlagSet) error {
visited := make(map[string]bool)
set.Visit(func(f *flag.Flag) {
visited[f.Name] = true
})
for _, f := range flags {
parts := f.Names()
if len(parts) == 1 {
continue
}
var ff *flag.Flag
for _, name := range parts {
name = strings.Trim(name, " ")
if visited[name] {
if ff != nil {
return errors.New("Cannot use two forms of the same flag: " + name + " " + ff.Name)
}
ff = set.Lookup(name)
}
}
if ff == nil {
continue
}
for _, name := range parts {
name = strings.Trim(name, " ")
if !visited[name] {
copyFlag(name, ff, set)
}
}
}
return nil
}
func (set *Flags) searchFS(name string) []string {
for _, f := range set.urFavFlags {
for _, n := range f.Names() {
if n == name {
return f.Names()
}
}
}
return nil
}
func (set *Flags) String(name string) string {
names := set.searchFS(name)
for _, n := range names {
f := set.Lookup(n)
if f != nil {
parsed := f.Value.String()
if parsed != "" {
return parsed
}
}
}
return ""
}

func (set *Flags) Bool(name string) bool {
names := set.searchFS(name)
for _, n := range names {
f := set.Lookup(n)
if f != nil {
parsed, _ := strconv.ParseBool(f.Value.String())
if parsed {
return parsed
}
}
}
return false
}

func (set *Flags) Int(name string) int {
names := set.searchFS(name)
for _, n := range names {
f := set.Lookup(n)
if f != nil {
parsed, err := strconv.ParseInt(f.Value.String(), 0, 64)
if err != nil {
return int(parsed)
}
}
}
return 0
}

func (set *Flags) Int64(name string) int64 {
names := set.searchFS(name)
for _, n := range names {
f := set.Lookup(n)
if f != nil {
parsed, err := strconv.ParseInt(f.Value.String(), 0, 64)
if err != nil {
return parsed
}
}
}
return 0
}

0 comments on commit 3ee7eac

Please sign in to comment.