github.com/argoproj/argo-cd@v1.8.7/util/dex/dex_test.go (about)

     1  package dex
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"net/http"
     7  	"net/http/httptest"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/ghodss/yaml"
    12  	"github.com/stretchr/testify/assert"
    13  
    14  	// "github.com/argoproj/argo-cd/common"
    15  	"github.com/argoproj/argo-cd/util/settings"
    16  )
    17  
    18  const invalidURL = ":://localhost/foo/bar"
    19  
    20  var malformedDexConfig = `
    21  valid:
    22    yaml: valid
    23  yaml:
    24    valid
    25  `
    26  
    27  var goodDexConfig = `
    28  connectors:
    29  # GitHub example
    30  - type: github
    31    id: github
    32    name: GitHub
    33    config:
    34      clientID: aabbccddeeff00112233
    35      clientSecret: $dex.github.clientSecret
    36      orgs:
    37      - name: your-github-org
    38  
    39  # GitHub enterprise example
    40  - type: github
    41    id: acme-github
    42    name: Acme GitHub
    43    config:
    44      hostName: github.acme.com
    45      clientID: abcdefghijklmnopqrst
    46      clientSecret: $dex.acme.clientSecret
    47      orgs:
    48      - name: your-github-org
    49  `
    50  var customStaticClientDexConfig = `
    51  connectors:
    52  # GitHub example
    53  - type: github
    54    id: github
    55    name: GitHub
    56    config:
    57      clientID: aabbccddeeff00112233
    58      clientSecret: abcdefghijklmnopqrst
    59      orgs:
    60      - name: your-github-org
    61  staticClients:
    62  - id: argo-workflow
    63    name: Argo Workflow
    64    redirectURIs:
    65    - https://argo/oauth2/callback
    66    secret: abcdefghijklmnopqrst
    67  `
    68  var badDexConfig = `
    69  connectors:
    70  # GitHub example
    71  - type: github
    72    id: github
    73    name: GitHub
    74    config: foo
    75  
    76  # GitHub enterprise example
    77  - type: github
    78    id: acme-github
    79    name: Acme GitHub
    80    config:
    81      hostName: github.acme.com
    82      clientID: abcdefghijklmnopqrst
    83      clientSecret: $dex.acme.clientSecret
    84      orgs:
    85      - name: your-github-org
    86  `
    87  var goodSecrets = map[string]string{
    88  	"dex.github.clientSecret": "foobar",
    89  	"dex.acme.clientSecret":   "barfoo",
    90  }
    91  
    92  func Test_GenerateDexConfig(t *testing.T) {
    93  
    94  	t.Run("Empty settings", func(t *testing.T) {
    95  		s := settings.ArgoCDSettings{}
    96  		config, err := GenerateDexConfigYAML(&s)
    97  		assert.NoError(t, err)
    98  		assert.Nil(t, config)
    99  	})
   100  
   101  	t.Run("Invalid URL", func(t *testing.T) {
   102  		s := settings.ArgoCDSettings{
   103  			URL:       invalidURL,
   104  			DexConfig: goodDexConfig,
   105  		}
   106  		config, err := GenerateDexConfigYAML(&s)
   107  		assert.Error(t, err)
   108  		assert.Nil(t, config)
   109  	})
   110  
   111  	t.Run("No URL set", func(t *testing.T) {
   112  		s := settings.ArgoCDSettings{
   113  			URL:       "",
   114  			DexConfig: "invalidyaml",
   115  		}
   116  		config, err := GenerateDexConfigYAML(&s)
   117  		assert.NoError(t, err)
   118  		assert.Nil(t, config)
   119  	})
   120  
   121  	t.Run("Invalid YAML", func(t *testing.T) {
   122  		s := settings.ArgoCDSettings{
   123  			URL:       "http://localhost",
   124  			DexConfig: "invalidyaml",
   125  		}
   126  		config, err := GenerateDexConfigYAML(&s)
   127  		assert.NoError(t, err)
   128  		assert.Nil(t, config)
   129  	})
   130  
   131  	t.Run("Valid YAML but incorrect Dex config", func(t *testing.T) {
   132  		s := settings.ArgoCDSettings{
   133  			URL:       "http://localhost",
   134  			DexConfig: malformedDexConfig,
   135  		}
   136  		config, err := GenerateDexConfigYAML(&s)
   137  		assert.Error(t, err)
   138  		assert.Nil(t, config)
   139  	})
   140  
   141  	t.Run("Valid YAML but incorrect Dex config", func(t *testing.T) {
   142  		s := settings.ArgoCDSettings{
   143  			URL:       "http://localhost",
   144  			DexConfig: badDexConfig,
   145  		}
   146  		config, err := GenerateDexConfigYAML(&s)
   147  		assert.Error(t, err)
   148  		assert.Nil(t, config)
   149  	})
   150  
   151  	t.Run("Valid YAML and correct Dex config", func(t *testing.T) {
   152  		s := settings.ArgoCDSettings{
   153  			URL:       "http://localhost",
   154  			DexConfig: goodDexConfig,
   155  		}
   156  		config, err := GenerateDexConfigYAML(&s)
   157  		assert.NoError(t, err)
   158  		assert.NotNil(t, config)
   159  	})
   160  
   161  	t.Run("Secret dereference", func(t *testing.T) {
   162  		s := settings.ArgoCDSettings{
   163  			URL:       "http://localhost",
   164  			DexConfig: goodDexConfig,
   165  			Secrets:   goodSecrets,
   166  		}
   167  		config, err := GenerateDexConfigYAML(&s)
   168  		assert.NoError(t, err)
   169  		assert.NotNil(t, config)
   170  		var dexCfg map[string]interface{}
   171  		err = yaml.Unmarshal(config, &dexCfg)
   172  		if err != nil {
   173  			panic(err.Error())
   174  		}
   175  		connectors, ok := dexCfg["connectors"].([]interface{})
   176  		assert.True(t, ok)
   177  		for i, connectorsIf := range connectors {
   178  			config := connectorsIf.(map[string]interface{})["config"].(map[string]interface{})
   179  			if i == 0 {
   180  				assert.Equal(t, "foobar", config["clientSecret"])
   181  			} else if i == 1 {
   182  				assert.Equal(t, "barfoo", config["clientSecret"])
   183  			}
   184  		}
   185  	})
   186  
   187  	t.Run("Redirect config", func(t *testing.T) {
   188  		types := []string{"oidc", "saml", "microsoft", "linkedin", "gitlab", "github", "bitbucket-cloud"}
   189  		for _, c := range types {
   190  			assert.True(t, needsRedirectURI(c))
   191  		}
   192  		assert.False(t, needsRedirectURI("invalid"))
   193  	})
   194  
   195  	t.Run("Custom static clients", func(t *testing.T) {
   196  		s := settings.ArgoCDSettings{
   197  			URL:       "http://localhost",
   198  			DexConfig: customStaticClientDexConfig,
   199  		}
   200  		config, err := GenerateDexConfigYAML(&s)
   201  		assert.NoError(t, err)
   202  		assert.NotNil(t, config)
   203  		var dexCfg map[string]interface{}
   204  		err = yaml.Unmarshal(config, &dexCfg)
   205  		if err != nil {
   206  			panic(err.Error())
   207  		}
   208  		clients, ok := dexCfg["staticClients"].([]interface{})
   209  		assert.True(t, ok)
   210  		assert.Equal(t, 3, len(clients))
   211  
   212  		customClient := clients[2].(map[string]interface{})
   213  		assert.Equal(t, "argo-workflow", customClient["id"].(string))
   214  		assert.Equal(t, 1, len(customClient["redirectURIs"].([]interface{})))
   215  	})
   216  }
   217  
   218  func Test_DexReverseProxy(t *testing.T) {
   219  	t.Run("Good case", func(t *testing.T) {
   220  		fakeDex := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
   221  			rw.WriteHeader(http.StatusOK)
   222  		}))
   223  		defer fakeDex.Close()
   224  		fmt.Printf("Fake Dex listening on %s\n", fakeDex.URL)
   225  		server := httptest.NewServer(http.HandlerFunc(NewDexHTTPReverseProxy(fakeDex.URL, "/")))
   226  		fmt.Printf("Fake API Server listening on %s\n", server.URL)
   227  		defer server.Close()
   228  		resp, err := http.Get(server.URL)
   229  		assert.NotNil(t, resp)
   230  		assert.NoError(t, err)
   231  		assert.Equal(t, http.StatusOK, resp.StatusCode)
   232  		fmt.Printf("%s\n", resp.Status)
   233  	})
   234  
   235  	t.Run("Bad case", func(t *testing.T) {
   236  		fakeDex := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
   237  			rw.WriteHeader(http.StatusInternalServerError)
   238  		}))
   239  		defer fakeDex.Close()
   240  		fmt.Printf("Fake Dex listening on %s\n", fakeDex.URL)
   241  		server := httptest.NewServer(http.HandlerFunc(NewDexHTTPReverseProxy(fakeDex.URL, "/")))
   242  		fmt.Printf("Fake API Server listening on %s\n", server.URL)
   243  		defer server.Close()
   244  		client := &http.Client{
   245  			CheckRedirect: func(req *http.Request, via []*http.Request) error {
   246  				return http.ErrUseLastResponse
   247  			}}
   248  		resp, err := client.Get(server.URL)
   249  		assert.NotNil(t, resp)
   250  		assert.NoError(t, err)
   251  		assert.Equal(t, http.StatusSeeOther, resp.StatusCode)
   252  		location, _ := resp.Location()
   253  		fmt.Printf("%s %s\n", resp.Status, location.RequestURI())
   254  		assert.True(t, strings.HasPrefix(location.RequestURI(), "/login?sso_error"))
   255  	})
   256  
   257  	t.Run("Invalid URL for Dex reverse proxy", func(t *testing.T) {
   258  		// Can't test for now, since it would call exit
   259  		t.Skip()
   260  		f := NewDexHTTPReverseProxy(invalidURL, "/")
   261  		assert.Nil(t, f)
   262  	})
   263  
   264  	t.Run("Round Tripper", func(t *testing.T) {
   265  		server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
   266  			assert.Equal(t, "/", req.URL.String())
   267  		}))
   268  		defer server.Close()
   269  		rt := NewDexRewriteURLRoundTripper(server.URL, http.DefaultTransport)
   270  		assert.NotNil(t, rt)
   271  		req, err := http.NewRequest("GET", "/", bytes.NewBuffer([]byte("")))
   272  		assert.NoError(t, err)
   273  		_, err = rt.RoundTrip(req)
   274  		assert.NoError(t, err)
   275  	})
   276  }