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: &lt;script&gt;alert(&#39;hello&#39;)&lt;/script&gt;\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  }