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 }