github.com/argoproj/argo-cd@v1.8.7/util/oidc/oidc_test.go (about) 1 package oidc 2 3 import ( 4 "encoding/json" 5 "io/ioutil" 6 "net/http/httptest" 7 "net/url" 8 "testing" 9 10 gooidc "github.com/coreos/go-oidc" 11 "github.com/stretchr/testify/assert" 12 "golang.org/x/oauth2" 13 14 "github.com/argoproj/argo-cd/server/settings/oidc" 15 ) 16 17 func TestInferGrantType(t *testing.T) { 18 for _, path := range []string{"dex", "okta", "auth0", "onelogin"} { 19 t.Run(path, func(t *testing.T) { 20 rawConfig, err := ioutil.ReadFile("testdata/" + path + ".json") 21 assert.NoError(t, err) 22 var config OIDCConfiguration 23 err = json.Unmarshal(rawConfig, &config) 24 assert.NoError(t, err) 25 grantType := InferGrantType(&config) 26 assert.Equal(t, GrantTypeAuthorizationCode, grantType) 27 28 var noCodeResponseTypes []string 29 for _, supportedResponseType := range config.ResponseTypesSupported { 30 if supportedResponseType != ResponseTypeCode { 31 noCodeResponseTypes = append(noCodeResponseTypes, supportedResponseType) 32 } 33 } 34 35 config.ResponseTypesSupported = noCodeResponseTypes 36 grantType = InferGrantType(&config) 37 assert.Equal(t, GrantTypeImplicit, grantType) 38 }) 39 } 40 } 41 42 func TestIDTokenClaims(t *testing.T) { 43 oauth2Config := &oauth2.Config{ 44 ClientID: "DUMMY_OIDC_PROVIDER", 45 ClientSecret: "0987654321", 46 Endpoint: oauth2.Endpoint{AuthURL: "https://argocd-dev.onelogin.com/oidc/auth", TokenURL: "https://argocd-dev.onelogin.com/oidc/token"}, 47 Scopes: []string{"oidc", "profile", "groups"}, 48 RedirectURL: "https://argocd-dev.io/redirect_url", 49 } 50 51 var opts []oauth2.AuthCodeOption 52 requestedClaims := make(map[string]*oidc.Claim) 53 54 opts = AppendClaimsAuthenticationRequestParameter(opts, requestedClaims) 55 assert.Len(t, opts, 0) 56 57 requestedClaims["groups"] = &oidc.Claim{Essential: true} 58 opts = AppendClaimsAuthenticationRequestParameter(opts, requestedClaims) 59 assert.Len(t, opts, 1) 60 61 authCodeURL, err := url.Parse(oauth2Config.AuthCodeURL("TEST", opts...)) 62 assert.NoError(t, err) 63 64 values, err := url.ParseQuery(authCodeURL.RawQuery) 65 assert.NoError(t, err) 66 67 assert.Equal(t, "{\"id_token\":{\"groups\":{\"essential\":true}}}", values.Get("claims")) 68 } 69 70 type fakeProvider struct { 71 } 72 73 func (p *fakeProvider) Endpoint() (*oauth2.Endpoint, error) { 74 return &oauth2.Endpoint{}, nil 75 } 76 77 func (p *fakeProvider) ParseConfig() (*OIDCConfiguration, error) { 78 return nil, nil 79 } 80 81 func (p *fakeProvider) Verify(_, _ string) (*gooidc.IDToken, error) { 82 return nil, nil 83 } 84 85 func TestHandleCallback(t *testing.T) { 86 app := ClientApp{provider: &fakeProvider{}} 87 88 req := httptest.NewRequest("GET", "http://example.com/foo", nil) 89 req.Form = url.Values{ 90 "error": []string{"login-failed"}, 91 "error_description": []string{"<script>alert('hello')</script>"}, 92 } 93 w := httptest.NewRecorder() 94 95 app.HandleCallback(w, req) 96 97 assert.Equal(t, "login-failed: <script>alert('hello')</script>\n", w.Body.String()) 98 } 99 100 func TestIsValidRedirect(t *testing.T) { 101 var tests = []struct { 102 name string 103 valid bool 104 redirectURL string 105 allowedURLs []string 106 }{ 107 { 108 name: "Single allowed valid URL", 109 valid: true, 110 redirectURL: "https://localhost:4000", 111 allowedURLs: []string{"https://localhost:4000/"}, 112 }, 113 { 114 name: "Empty URL", 115 valid: true, 116 redirectURL: "", 117 allowedURLs: []string{"https://localhost:4000/"}, 118 }, 119 { 120 name: "Trailing single slash and empty suffix are handled the same", 121 valid: true, 122 redirectURL: "https://localhost:4000/", 123 allowedURLs: []string{"https://localhost:4000"}, 124 }, 125 { 126 name: "Multiple valid URLs with one allowed", 127 valid: true, 128 redirectURL: "https://localhost:4000", 129 allowedURLs: []string{"https://wherever:4000", "https://localhost:4000"}, 130 }, 131 { 132 name: "Multiple valid URLs with none allowed", 133 valid: false, 134 redirectURL: "https://localhost:4000", 135 allowedURLs: []string{"https://wherever:4000", "https://invalid:4000"}, 136 }, 137 { 138 name: "Invalid redirect URL because path prefix does not match", 139 valid: false, 140 redirectURL: "https://localhost:4000/applications", 141 allowedURLs: []string{"https://localhost:4000/argocd"}, 142 }, 143 { 144 name: "Valid redirect URL because prefix matches", 145 valid: true, 146 redirectURL: "https://localhost:4000/argocd/applications", 147 allowedURLs: []string{"https://localhost:4000/argocd"}, 148 }, 149 { 150 name: "Invalid redirect URL because resolved path does not match prefix", 151 valid: false, 152 redirectURL: "https://localhost:4000/argocd/../applications", 153 allowedURLs: []string{"https://localhost:4000/argocd"}, 154 }, 155 { 156 name: "Invalid redirect URL because scheme mismatch", 157 valid: false, 158 redirectURL: "http://localhost:4000", 159 allowedURLs: []string{"https://localhost:4000"}, 160 }, 161 { 162 name: "Invalid redirect URL because port mismatch", 163 valid: false, 164 redirectURL: "https://localhost", 165 allowedURLs: []string{"https://localhost:80"}, 166 }, 167 { 168 name: "Invalid redirect URL because of CRLF in path", 169 valid: false, 170 redirectURL: "https://localhost:80/argocd\r\n", 171 allowedURLs: []string{"https://localhost:80/argocd\r\n"}, 172 }, 173 } 174 175 for _, tt := range tests { 176 t.Run(tt.name, func(t *testing.T) { 177 res := isValidRedirectURL(tt.redirectURL, tt.allowedURLs) 178 assert.Equal(t, res, tt.valid) 179 }) 180 } 181 }