Skip to content

Commit

Permalink
E2e fixes (#18)
Browse files Browse the repository at this point in the history
* feat: add context to dex client

* feat: restructure cli package

* feat: add cdk.Display

* feat: add project-by-slug using list-projects

* fix changes per ui feedback (#19)

* feat: check if project slug from path matches project fetched from shield using project id from header

* feat: allow updating description in firehose update request

* feat: add user email id to labels

* refactor: change team to group

* fix: remove readOnly from group field in Firehose

* refactor: change cluster to kube_cluster to express better

* refactor: remove duplicate projectSlug <> header projectID check

* fix: handle refresh-sync correctly

* chore: remove unused WithSpinner helper

Co-authored-by: Rohil Surana <[email protected]>
  • Loading branch information
spy16 and rohilsurana authored Nov 30, 2022
1 parent e368640 commit f1fc597
Show file tree
Hide file tree
Showing 38 changed files with 913 additions and 460 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,6 @@ coverage/
tmp/

expt/
requests.http
requests.http

test_*.yml
29 changes: 7 additions & 22 deletions cli/login.go → cli/auth/cobra.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package cli
package auth

import (
"fmt"
Expand All @@ -15,7 +15,7 @@ const (
redirectTo = "http://localhost:5454"
)

func loginCmd(cdk *CDK) *cobra.Command {
func LoginCommand() *cobra.Command {
var keyFile string

cmd := &cobra.Command{
Expand All @@ -25,26 +25,14 @@ func loginCmd(cdk *CDK) *cobra.Command {
ctx, cancel := signal.NotifyContext(cmd.Context(), syscall.SIGINT, syscall.SIGTERM)
defer cancel()

var ac AuthConfig
if err := cdk.Auth.Load(&ac); err != nil {
ac, err := LoadConfig()
if err != nil {
return err
}

oauth2Conf := &oauth2.Config{
ClientID: ac.OAuth.ClientID,
ClientSecret: ac.OAuth.ClientSecret,
Scopes: []string{scopeEmail},
Endpoint: oauth2.Endpoint{
AuthURL: ac.OAuth.Endpoint.AuthURL,
TokenURL: ac.OAuth.Endpoint.TokenURL,
AuthStyle: oauth2.AuthStyleInParams,
},
RedirectURL: redirectTo,
}

var ts oauth2.TokenSource
if keyFile == "" {
ts = oidc.NewTokenSource(ctx, oauth2Conf, ac.OAuth.Audience)
ts = oidc.NewTokenSource(ctx, ac.oauth2Config(), ac.OAuth.Audience)
} else {
var err error
ts, err = oidc.NewGoogleServiceAccountTokenSource(ctx, keyFile, ac.OAuth.Audience)
Expand All @@ -58,11 +46,8 @@ func loginCmd(cdk *CDK) *cobra.Command {
return err
}

ac.AccessToken = token.AccessToken
ac.RefreshToken = token.RefreshToken
ac.Expiry = token.Expiry.Unix()

if err := cdk.Auth.Write(ac); err != nil {
ac.setToken(token)
if err := saveConfig(*ac); err != nil {
return err
}

Expand Down
103 changes: 103 additions & 0 deletions cli/auth/configs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package auth

import (
"context"
"time"

"github.com/odpf/salt/cmdx"
"golang.org/x/oauth2"

"github.com/odpf/dex/pkg/errors"
)

type OAuthProviderConfig struct {
Audience string `mapstructure:"audience" yaml:"audience"`
Endpoint OAuthEndpoint `mapstructure:"endpoint" yaml:"endpoint"`
ClientID string `mapstructure:"client_id" yaml:"client_id"`
ClientSecret string `mapstructure:"client_secret" yaml:"client_secret"`
}

type OAuthEndpoint struct {
AuthURL string `mapstructure:"auth_url" yaml:"auth_url"`
TokenURL string `mapstructure:"token_url" yaml:"token_url"`
}

type Config struct {
OAuth OAuthProviderConfig `mapstructure:"oauth" yaml:"oauth"`
Expiry int64 `mapstructure:"expiry" yaml:"expiry"`
AccessToken string `mapstructure:"access_token" yaml:"access_token"`
RefreshToken string `mapstructure:"refresh_token" yaml:"refresh_token"`
}

func (ac *Config) setToken(t *oauth2.Token) {
ac.AccessToken = t.AccessToken
ac.RefreshToken = t.RefreshToken
ac.Expiry = t.Expiry.Unix()
}

func (ac *Config) oauth2Config() *oauth2.Config {
return &oauth2.Config{
ClientID: ac.OAuth.ClientID,
ClientSecret: ac.OAuth.ClientSecret,
Scopes: []string{scopeEmail},
Endpoint: oauth2.Endpoint{
AuthURL: ac.OAuth.Endpoint.AuthURL,
TokenURL: ac.OAuth.Endpoint.TokenURL,
AuthStyle: oauth2.AuthStyleInParams,
},
RedirectURL: redirectTo,
}
}

func LoadConfig() (*Config, error) {
var ac Config

auth := cmdx.SetConfig("auth")
if err := auth.Load(&ac); err != nil {
return nil, err
}
return &ac, nil
}

func saveConfig(conf Config) error {
auth := cmdx.SetConfig("auth")
return auth.Write(conf)
}

// Token returns a valid access token as per current log-in state. Refresh is performed
// if necessary.
func Token(ctx context.Context) (string, error) {
ac, err := LoadConfig()
if err != nil {
return "", err
}

curToken := oauth2.Token{
Expiry: time.Unix(ac.Expiry, 0),
AccessToken: ac.AccessToken,
RefreshToken: ac.RefreshToken,
}

newToken, err := ac.oauth2Config().TokenSource(ctx, &curToken).Token()
if err != nil {
return "", err
}

// Token may or may not have been refreshed now. We use old & new token
// expiry value to detect if a refresh occurred or not and flush to the
// auth file if it did.
if !newToken.Expiry.Equal(curToken.Expiry) {
idToken, ok := newToken.Extra("id_token").(string)
if !ok {
return "", errors.New("id_token not found in token response")
}
newToken.AccessToken = idToken

ac.setToken(newToken)
if err := saveConfig(*ac); err != nil {
return "", err
}
}

return newToken.AccessToken, nil
}
93 changes: 93 additions & 0 deletions cli/cdk/display.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package cdk

import (
"encoding/json"
"io"
"os"
"strings"

"github.com/BurntSushi/toml"
"github.com/spf13/cobra"
"github.com/yudai/pp"
"gopkg.in/yaml.v2"

"github.com/odpf/dex/pkg/errors"
)

type FormatFn func(w io.Writer, v any) error

// Display formats the given value 'v' using format specified as value for --format
// flag and writes to STDOUT. If --format=pretty/human, custom-formatter passed will
// be used.
func Display(cmd *cobra.Command, v any, prettyFormatter FormatFn) error {
format, _ := cmd.Flags().GetString("format")
format = strings.ToLower(strings.TrimSpace(format))

var formatter FormatFn
switch format {
case "json":
formatter = JSONFormat

case "yaml", "yml":
formatter = YAMLFormat

case "toml":
formatter = TOMLFormat

case "pretty", "human":
if prettyFormatter != nil {
formatter = prettyFormatter
} else {
formatter = GoFormat
}
}

if formatter == nil {
return errors.Errorf("--format value '%s' is not valid", format)
}

return formatter(os.Stdout, v)
}

// JSONFormat outputs 'v' formatted as indented JSON.
func JSONFormat(w io.Writer, v any) error {
enc := json.NewEncoder(w)
enc.SetIndent("", " ")
return enc.Encode(v)
}

// TOMLFormat outputs 'v' formatted as per TOML spec.
func TOMLFormat(w io.Writer, v any) error {
enc := toml.NewEncoder(w)
return enc.Encode(v)
}

// YAMLFormat outputs 'v' formatted as per YAML spec.
func YAMLFormat(w io.Writer, v any) error {
// note: since most values are json tagged but may not be
// yaml tagged, we do this to ensure keys are snake-cased.
val, err := jsonConvert(v)
if err != nil {
return err
}
return yaml.NewEncoder(w).Encode(val)
}

// GoFormat outputs 'v' formatted using pp package.
func GoFormat(w io.Writer, v any) error {
_, err := pp.Fprintln(w, v)
return err
}

func jsonConvert(v any) (any, error) {
b, err := json.Marshal(v)
if err != nil {
return nil, err
}

var val any
if err := json.Unmarshal(b, &val); err != nil {
return nil, err
}
return val, nil
}
45 changes: 32 additions & 13 deletions cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,24 @@ import (
"github.com/odpf/salt/cmdx"
"github.com/spf13/cobra"

"github.com/odpf/dex/cli/firehose"
"github.com/odpf/dex/cli/auth"
"github.com/odpf/dex/cli/config"
"github.com/odpf/dex/cli/firehoses"
"github.com/odpf/dex/cli/projects"
"github.com/odpf/dex/cli/server"
"github.com/odpf/dex/pkg/version"
)

type CDK struct {
Config *cmdx.Config
Auth *cmdx.Config
var envHelp = map[string]string{
"short": "List of supported environment variables",
"long": heredoc.Doc(`
ODPF_CONFIG_DIR: the directory where dex will store configuration files. Default:
"$XDG_CONFIG_HOME/odpf" or "$HOME/.config/odpf".
NO_COLOR: set to any value to avoid printing ANSI escape sequences for color output.
CLICOLOR: set to "0" to disable printing ANSI colors in output.
`),
}

// New root command.
Expand All @@ -32,18 +44,15 @@ func New() *cobra.Command {
`),
},
}

cdk := &CDK{
Config: cmdx.SetConfig("dex"),
Auth: cmdx.SetConfig("auth"),
}
cmd.PersistentFlags().StringP("format", "F", "pretty", "Output format (json, yaml, pretty)")

cmd.AddCommand(
serverCommand(),
versionCmd(),
configCmd(cdk),
loginCmd(cdk),
firehose.Command(cdk.Config),
auth.LoginCommand(),
config.Commands(),
server.Commands(),
projects.Commands(),
firehoses.Commands(),
)

// Help topics.
Expand All @@ -61,3 +70,13 @@ func New() *cobra.Command {

return cmd
}

func versionCmd() *cobra.Command {
return &cobra.Command{
Use: "version",
Short: "Show version information",
RunE: func(cmd *cobra.Command, args []string) error {
return version.Print()
},
}
}
Loading

0 comments on commit f1fc597

Please sign in to comment.