494 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			494 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
package providers
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"encoding/json"
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
	"net/http"
 | 
						|
	"net/http/httptest"
 | 
						|
	"net/url"
 | 
						|
	"testing"
 | 
						|
 | 
						|
	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
 | 
						|
 | 
						|
	"github.com/coreos/go-oidc/v3/oidc"
 | 
						|
	"github.com/stretchr/testify/assert"
 | 
						|
 | 
						|
	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
 | 
						|
)
 | 
						|
 | 
						|
func newCidaasProvider(serverURL *url.URL) *CIDAASProvider {
 | 
						|
	providerData := &ProviderData{
 | 
						|
		ProviderName: "cidaas",
 | 
						|
		ClientID:     oidcClientID,
 | 
						|
		ClientSecret: oidcSecret,
 | 
						|
		LoginURL: &url.URL{
 | 
						|
			Scheme: serverURL.Scheme,
 | 
						|
			Host:   serverURL.Host,
 | 
						|
			Path:   "/login/oauth/authorize"},
 | 
						|
		RedeemURL: &url.URL{
 | 
						|
			Scheme: serverURL.Scheme,
 | 
						|
			Host:   serverURL.Host,
 | 
						|
			Path:   "/login/oauth/access_token"},
 | 
						|
		ProfileURL: &url.URL{
 | 
						|
			Scheme: serverURL.Scheme,
 | 
						|
			Host:   serverURL.Host,
 | 
						|
			Path:   "/profile"},
 | 
						|
		ValidateURL: &url.URL{
 | 
						|
			Scheme: serverURL.Scheme,
 | 
						|
			Host:   serverURL.Host,
 | 
						|
			Path:   "/api"},
 | 
						|
		Scope:       "openid profile offline_access roles groups",
 | 
						|
		EmailClaim:  "email",
 | 
						|
		GroupsClaim: "groups",
 | 
						|
		Verifier: oidc.NewVerifier(
 | 
						|
			oidcIssuer,
 | 
						|
			mockJWKS{},
 | 
						|
			&oidc.Config{ClientID: oidcClientID},
 | 
						|
		),
 | 
						|
	}
 | 
						|
	cfg := options.Provider{
 | 
						|
		Type: options.CidaasProvider,
 | 
						|
	}
 | 
						|
 | 
						|
	p := NewCIDAASProvider(providerData, cfg)
 | 
						|
 | 
						|
	return p
 | 
						|
}
 | 
						|
 | 
						|
func newCidaasServer(pathBodyMap map[string][]byte) (*url.URL, *httptest.Server) {
 | 
						|
	s := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
 | 
						|
		body, ok := pathBodyMap[r.URL.Path]
 | 
						|
		if !ok {
 | 
						|
			rw.WriteHeader(404)
 | 
						|
			return
 | 
						|
		}
 | 
						|
		rw.Header().Add("content-type", "application/json")
 | 
						|
		_, _ = rw.Write(body)
 | 
						|
	}))
 | 
						|
	u, _ := url.Parse(s.URL)
 | 
						|
	return u, s
 | 
						|
}
 | 
						|
 | 
						|
func newTestCidaasSetup(pathToBodyMap map[string][]byte) (*httptest.Server, *CIDAASProvider) {
 | 
						|
	redeemURL, server := newCidaasServer(pathToBodyMap)
 | 
						|
	provider := newCidaasProvider(redeemURL)
 | 
						|
	return server, provider
 | 
						|
}
 | 
						|
 | 
						|
func TestCidaasProvider_EnrichSession(t *testing.T) {
 | 
						|
	testCases := map[string]struct {
 | 
						|
		ExistingSession *sessions.SessionState
 | 
						|
		EmailClaim      string
 | 
						|
		GroupsClaim     string
 | 
						|
		ProfileJSON     map[string]interface{}
 | 
						|
		ExpectedError   error
 | 
						|
		ExpectedSession *sessions.SessionState
 | 
						|
	}{
 | 
						|
		"Missing Email Only in Profile URL": {
 | 
						|
			ExistingSession: &sessions.SessionState{
 | 
						|
				User:         "missing.email",
 | 
						|
				IDToken:      idToken,
 | 
						|
				AccessToken:  accessToken,
 | 
						|
				RefreshToken: refreshToken,
 | 
						|
			},
 | 
						|
			EmailClaim:  "email",
 | 
						|
			GroupsClaim: "groups",
 | 
						|
			ProfileJSON: map[string]interface{}{
 | 
						|
				"email": "found@email.com",
 | 
						|
			},
 | 
						|
			ExpectedError: nil,
 | 
						|
			ExpectedSession: &sessions.SessionState{
 | 
						|
				User:         "missing.email",
 | 
						|
				Email:        "found@email.com",
 | 
						|
				IDToken:      idToken,
 | 
						|
				AccessToken:  accessToken,
 | 
						|
				RefreshToken: refreshToken,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		"Missing Email with Custom Claim": {
 | 
						|
			ExistingSession: &sessions.SessionState{
 | 
						|
				User:         "missing.email",
 | 
						|
				Groups:       []string{"already", "populated"},
 | 
						|
				IDToken:      idToken,
 | 
						|
				AccessToken:  accessToken,
 | 
						|
				RefreshToken: refreshToken,
 | 
						|
			},
 | 
						|
			EmailClaim:  "weird",
 | 
						|
			GroupsClaim: "groups",
 | 
						|
			ProfileJSON: map[string]interface{}{
 | 
						|
				"weird": "weird@claim.com",
 | 
						|
				"groups": []map[string]interface{}{
 | 
						|
					{
 | 
						|
						"groupId": "CIDAAS_USERS",
 | 
						|
						"roles":   []string{"USER"},
 | 
						|
					},
 | 
						|
				},
 | 
						|
				"roles": []string{"USER"},
 | 
						|
			},
 | 
						|
			ExpectedError: nil,
 | 
						|
			ExpectedSession: &sessions.SessionState{
 | 
						|
				User:         "missing.email",
 | 
						|
				Email:        "weird@claim.com",
 | 
						|
				Groups:       []string{"CIDAAS_USERS:USER", "cidaas:USER"},
 | 
						|
				IDToken:      idToken,
 | 
						|
				AccessToken:  accessToken,
 | 
						|
				RefreshToken: refreshToken,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		"Missing Email not in Profile URL": {
 | 
						|
			ExistingSession: &sessions.SessionState{
 | 
						|
				User:         "missing.email",
 | 
						|
				Groups:       []string{"already", "populated"},
 | 
						|
				IDToken:      idToken,
 | 
						|
				AccessToken:  accessToken,
 | 
						|
				RefreshToken: refreshToken,
 | 
						|
			},
 | 
						|
			EmailClaim:  "email",
 | 
						|
			GroupsClaim: "groups",
 | 
						|
			ProfileJSON: map[string]interface{}{
 | 
						|
				"groups": []map[string]interface{}{
 | 
						|
					{
 | 
						|
						"groupId": "CIDAAS_USERS",
 | 
						|
						"roles":   []string{"USER"},
 | 
						|
					},
 | 
						|
				},
 | 
						|
				"roles": []string{"USER"},
 | 
						|
			},
 | 
						|
			ExpectedError: errors.New("neither the id_token nor the profileURL set an email"),
 | 
						|
			ExpectedSession: &sessions.SessionState{
 | 
						|
				User:         "missing.email",
 | 
						|
				Groups:       []string{"CIDAAS_USERS:USER", "cidaas:USER"},
 | 
						|
				IDToken:      idToken,
 | 
						|
				AccessToken:  accessToken,
 | 
						|
				RefreshToken: refreshToken,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		"Missing Groups": {
 | 
						|
			ExistingSession: &sessions.SessionState{
 | 
						|
				User:         "already",
 | 
						|
				Email:        "already@populated.com",
 | 
						|
				Groups:       nil,
 | 
						|
				IDToken:      idToken,
 | 
						|
				AccessToken:  accessToken,
 | 
						|
				RefreshToken: refreshToken,
 | 
						|
			},
 | 
						|
			EmailClaim:  "email",
 | 
						|
			GroupsClaim: "groups",
 | 
						|
			ProfileJSON: map[string]interface{}{
 | 
						|
				"email": "new@thing.com",
 | 
						|
				"groups": []map[string]interface{}{
 | 
						|
					{
 | 
						|
						"groupId": "CIDAAS_USERS",
 | 
						|
						"roles":   []string{"USER"},
 | 
						|
					},
 | 
						|
				},
 | 
						|
				"roles": []string{"USER"},
 | 
						|
			},
 | 
						|
			ExpectedError: nil,
 | 
						|
			ExpectedSession: &sessions.SessionState{
 | 
						|
				User:         "already",
 | 
						|
				Email:        "already@populated.com",
 | 
						|
				Groups:       []string{"CIDAAS_USERS:USER", "cidaas:USER"},
 | 
						|
				IDToken:      idToken,
 | 
						|
				AccessToken:  accessToken,
 | 
						|
				RefreshToken: refreshToken,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		"Empty Groups Claims": {
 | 
						|
			ExistingSession: &sessions.SessionState{
 | 
						|
				User:         "already",
 | 
						|
				Email:        "already@populated.com",
 | 
						|
				Groups:       []string{},
 | 
						|
				IDToken:      idToken,
 | 
						|
				AccessToken:  accessToken,
 | 
						|
				RefreshToken: refreshToken,
 | 
						|
			},
 | 
						|
			EmailClaim:  "email",
 | 
						|
			GroupsClaim: "groups",
 | 
						|
			ProfileJSON: map[string]interface{}{
 | 
						|
				"email": "new@thing.com",
 | 
						|
				"groups": []map[string]interface{}{
 | 
						|
					{
 | 
						|
						"groupId": "CIDAAS_USERS",
 | 
						|
						"roles":   []string{"USER"},
 | 
						|
					},
 | 
						|
				},
 | 
						|
				"roles": []string{"USER"},
 | 
						|
			},
 | 
						|
			ExpectedError: nil,
 | 
						|
			ExpectedSession: &sessions.SessionState{
 | 
						|
				User:         "already",
 | 
						|
				Email:        "already@populated.com",
 | 
						|
				Groups:       []string{"CIDAAS_USERS:USER", "cidaas:USER"},
 | 
						|
				IDToken:      idToken,
 | 
						|
				AccessToken:  accessToken,
 | 
						|
				RefreshToken: refreshToken,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		"Missing Groups with Custom Claim": {
 | 
						|
			ExistingSession: &sessions.SessionState{
 | 
						|
				User:         "already",
 | 
						|
				Email:        "already@populated.com",
 | 
						|
				Groups:       nil,
 | 
						|
				IDToken:      idToken,
 | 
						|
				AccessToken:  accessToken,
 | 
						|
				RefreshToken: refreshToken,
 | 
						|
			},
 | 
						|
			EmailClaim:  "email",
 | 
						|
			GroupsClaim: "groups2",
 | 
						|
			ProfileJSON: map[string]interface{}{
 | 
						|
				"email": "already@populated.com",
 | 
						|
				"groups2": []map[string]interface{}{
 | 
						|
					{
 | 
						|
						"sub":     "aa5181ea-0841-4ea7-b67f-81882f153d40",
 | 
						|
						"groupId": "CIDAAS_ADMINS",
 | 
						|
						"path":    "/CIDAAS_ADMINS/",
 | 
						|
						"roles":   []string{"ADMIN"},
 | 
						|
					},
 | 
						|
					{
 | 
						|
						"sub":       "aa5181ea-0841-4ea7-b67f-81882f153d39",
 | 
						|
						"groupId":   "customers",
 | 
						|
						"groupType": "Customers",
 | 
						|
						"path":      "/customers/",
 | 
						|
						"roles": []string{
 | 
						|
							"CUSTOMER_ACCOUNT_LOGIN",
 | 
						|
							"GROUP_ADMIN",
 | 
						|
						},
 | 
						|
					},
 | 
						|
					{
 | 
						|
						"groupId": "CIDAAS_USERS",
 | 
						|
						"roles":   []string{"USER"},
 | 
						|
					},
 | 
						|
				},
 | 
						|
				"roles": []string{"USER"},
 | 
						|
			},
 | 
						|
			ExpectedError: nil,
 | 
						|
			ExpectedSession: &sessions.SessionState{
 | 
						|
				User:         "already",
 | 
						|
				Email:        "already@populated.com",
 | 
						|
				Groups:       []string{"CIDAAS_ADMINS:ADMIN", "customers:CUSTOMER_ACCOUNT_LOGIN", "customers:GROUP_ADMIN", "CIDAAS_USERS:USER", "cidaas:USER"},
 | 
						|
				IDToken:      idToken,
 | 
						|
				AccessToken:  accessToken,
 | 
						|
				RefreshToken: refreshToken,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		"Just format Groups": {
 | 
						|
			ExistingSession: &sessions.SessionState{
 | 
						|
				User:         "already",
 | 
						|
				Email:        "already@populated.com",
 | 
						|
				Groups:       nil,
 | 
						|
				IDToken:      idToken,
 | 
						|
				AccessToken:  accessToken,
 | 
						|
				RefreshToken: refreshToken,
 | 
						|
			},
 | 
						|
			EmailClaim:  "email",
 | 
						|
			GroupsClaim: "groups2",
 | 
						|
			ProfileJSON: map[string]interface{}{
 | 
						|
				"email": "already@populated.com",
 | 
						|
				"groups2": []map[string]interface{}{
 | 
						|
					{
 | 
						|
						"sub":       "aa5181ea-0841-4ea7-b67f-81882f153d39",
 | 
						|
						"groupId":   "customers",
 | 
						|
						"groupType": "Customers",
 | 
						|
						"path":      "/customers/",
 | 
						|
						"roles": []string{
 | 
						|
							"CUSTOMER_ACCOUNT_LOGIN",
 | 
						|
							"GROUP_ADMIN",
 | 
						|
						},
 | 
						|
					},
 | 
						|
					{
 | 
						|
						"groupId": "CIDAAS_USERS",
 | 
						|
						"roles":   []string{"USER"},
 | 
						|
					},
 | 
						|
				},
 | 
						|
				"roles": []string{"USER"},
 | 
						|
			},
 | 
						|
			ExpectedError: nil,
 | 
						|
			ExpectedSession: &sessions.SessionState{
 | 
						|
				User:         "already",
 | 
						|
				Email:        "already@populated.com",
 | 
						|
				Groups:       []string{"customers:CUSTOMER_ACCOUNT_LOGIN", "customers:GROUP_ADMIN", "CIDAAS_USERS:USER", "cidaas:USER"},
 | 
						|
				IDToken:      idToken,
 | 
						|
				AccessToken:  accessToken,
 | 
						|
				RefreshToken: refreshToken,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		"Missing Groups String Profile URL Response": {
 | 
						|
			ExistingSession: &sessions.SessionState{
 | 
						|
				User:         "already",
 | 
						|
				Email:        "already@populated.com",
 | 
						|
				Groups:       nil,
 | 
						|
				IDToken:      idToken,
 | 
						|
				AccessToken:  accessToken,
 | 
						|
				RefreshToken: refreshToken,
 | 
						|
			},
 | 
						|
			EmailClaim:  "email",
 | 
						|
			GroupsClaim: "groups",
 | 
						|
			ProfileJSON: map[string]interface{}{
 | 
						|
				"groups": []map[string]interface{}{
 | 
						|
					{
 | 
						|
						"sub":     "aa5181ea-0841-4ea7-b67f-81882f153d40",
 | 
						|
						"groupId": "CIDAAS_ADMINS",
 | 
						|
						"path":    "/CIDAAS_ADMINS/",
 | 
						|
						"roles":   []string{"ADMIN"},
 | 
						|
					},
 | 
						|
					{
 | 
						|
						"sub":       "aa5181ea-0841-4ea7-b67f-81882f153d39",
 | 
						|
						"groupId":   "customers",
 | 
						|
						"groupType": "Customers",
 | 
						|
						"path":      "/customers/",
 | 
						|
						"roles": []string{
 | 
						|
							"CUSTOMER_ACCOUNT_LOGIN",
 | 
						|
							"GROUP_ADMIN",
 | 
						|
						},
 | 
						|
					},
 | 
						|
					{
 | 
						|
						"groupId": "CIDAAS_USERS",
 | 
						|
						"roles":   []string{"USER"},
 | 
						|
					},
 | 
						|
				},
 | 
						|
				"roles": []string{"USER"},
 | 
						|
			},
 | 
						|
			ExpectedError: nil,
 | 
						|
			ExpectedSession: &sessions.SessionState{
 | 
						|
				User:         "already",
 | 
						|
				Email:        "already@populated.com",
 | 
						|
				Groups:       []string{"CIDAAS_ADMINS:ADMIN", "customers:CUSTOMER_ACCOUNT_LOGIN", "customers:GROUP_ADMIN", "CIDAAS_USERS:USER", "cidaas:USER"},
 | 
						|
				IDToken:      idToken,
 | 
						|
				AccessToken:  accessToken,
 | 
						|
				RefreshToken: refreshToken,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		"Missing Groups in both Claims and Profile URL": {
 | 
						|
			ExistingSession: &sessions.SessionState{
 | 
						|
				User:         "already",
 | 
						|
				Email:        "already@populated.com",
 | 
						|
				IDToken:      idToken,
 | 
						|
				AccessToken:  accessToken,
 | 
						|
				RefreshToken: refreshToken,
 | 
						|
			},
 | 
						|
			EmailClaim:  "email",
 | 
						|
			GroupsClaim: "groups",
 | 
						|
			ProfileJSON: map[string]interface{}{
 | 
						|
				"email": "new@thing.com",
 | 
						|
			},
 | 
						|
			ExpectedError: nil,
 | 
						|
			ExpectedSession: &sessions.SessionState{
 | 
						|
				User:         "already",
 | 
						|
				Email:        "already@populated.com",
 | 
						|
				IDToken:      idToken,
 | 
						|
				AccessToken:  accessToken,
 | 
						|
				RefreshToken: refreshToken,
 | 
						|
			},
 | 
						|
		},
 | 
						|
	}
 | 
						|
	for testName, tc := range testCases {
 | 
						|
		t.Run(testName, func(t *testing.T) {
 | 
						|
			path := "/userinfo/"
 | 
						|
			jsonResp, err := json.Marshal(tc.ProfileJSON)
 | 
						|
			assert.NoError(t, err)
 | 
						|
 | 
						|
			server, provider := newTestCidaasSetup(map[string][]byte{path: jsonResp})
 | 
						|
			provider.ProfileURL, err = url.Parse(fmt.Sprintf("%s%s", server.URL, path))
 | 
						|
			assert.NoError(t, err)
 | 
						|
 | 
						|
			provider.EmailClaim = tc.EmailClaim
 | 
						|
			provider.GroupsClaim = tc.GroupsClaim
 | 
						|
			defer server.Close()
 | 
						|
 | 
						|
			err = provider.EnrichSession(context.Background(), tc.ExistingSession)
 | 
						|
			if tc.ExpectedError != nil {
 | 
						|
				assert.EqualError(t, err, tc.ExpectedError.Error())
 | 
						|
			} else {
 | 
						|
				assert.NoError(t, err)
 | 
						|
			}
 | 
						|
			assert.Equal(t, *tc.ExpectedSession, *tc.ExistingSession)
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestCidaasProvider_RefreshSession(t *testing.T) {
 | 
						|
	testCases := map[string]struct {
 | 
						|
		ExistingSession   *sessions.SessionState
 | 
						|
		EmailClaim        string
 | 
						|
		GroupsClaim       string
 | 
						|
		ProfileJSON       map[string]interface{}
 | 
						|
		RedeemJSON        redeemTokenResponse
 | 
						|
		ExpectedRefreshed bool
 | 
						|
		ExpectedError     error
 | 
						|
		ExpectedEmail     string
 | 
						|
		ExpectedUser      string
 | 
						|
	}{
 | 
						|
		"Refresh session successfully": {
 | 
						|
			ExistingSession: &sessions.SessionState{
 | 
						|
				User:         "session.is.not.locked",
 | 
						|
				Email:        "found@email.com",
 | 
						|
				IDToken:      idToken,
 | 
						|
				AccessToken:  accessToken,
 | 
						|
				RefreshToken: refreshToken,
 | 
						|
			},
 | 
						|
			RedeemJSON: redeemTokenResponse{
 | 
						|
				AccessToken:  accessToken,
 | 
						|
				ExpiresIn:    10,
 | 
						|
				TokenType:    "Bearer",
 | 
						|
				RefreshToken: refreshToken,
 | 
						|
			},
 | 
						|
			ExpectedRefreshed: true,
 | 
						|
			ExpectedError:     nil,
 | 
						|
			ExpectedEmail:     defaultIDToken.Email,
 | 
						|
			ExpectedUser:      defaultIDToken.Subject,
 | 
						|
		},
 | 
						|
		"Unable to refresh session": {
 | 
						|
			ExistingSession: &sessions.SessionState{
 | 
						|
				User:         "session.is.unable.to.refresh",
 | 
						|
				Email:        "found@email.com",
 | 
						|
				IDToken:      idToken,
 | 
						|
				AccessToken:  accessToken,
 | 
						|
				RefreshToken: refreshToken,
 | 
						|
			},
 | 
						|
			ExpectedRefreshed: false,
 | 
						|
			ExpectedError:     fmt.Errorf("unable to redeem refresh token: failed to get token: oauth2: server response missing access_token"),
 | 
						|
			ExpectedUser:      "session.is.unable.to.refresh",
 | 
						|
			ExpectedEmail:     "found@email.com",
 | 
						|
		},
 | 
						|
	}
 | 
						|
	for testName, tc := range testCases {
 | 
						|
		t.Run(testName, func(t *testing.T) {
 | 
						|
			idToken, _ := newSignedTestIDToken(defaultIDToken)
 | 
						|
			tc.RedeemJSON.IDToken = idToken
 | 
						|
			redeemPath := "/token/"
 | 
						|
			redeemJSONResp, err := json.Marshal(tc.RedeemJSON)
 | 
						|
			assert.NoError(t, err)
 | 
						|
 | 
						|
			serverURL, server := newCidaasServer(
 | 
						|
				map[string][]byte{
 | 
						|
					redeemPath: redeemJSONResp,
 | 
						|
				})
 | 
						|
			provider := newCidaasProvider(serverURL)
 | 
						|
 | 
						|
			// Disable session enrichment, because we want to focus on refreshing logic
 | 
						|
			provider.ProfileURL, err = url.Parse("")
 | 
						|
			assert.NoError(t, err)
 | 
						|
			provider.RedeemURL, err = url.Parse(fmt.Sprintf("%s%s", server.URL, redeemPath))
 | 
						|
			assert.NoError(t, err)
 | 
						|
 | 
						|
			provider.GroupsClaim = tc.GroupsClaim
 | 
						|
			defer server.Close()
 | 
						|
 | 
						|
			var refreshed bool
 | 
						|
			refreshed, err = provider.RefreshSession(context.Background(), tc.ExistingSession)
 | 
						|
 | 
						|
			if tc.ExpectedError != nil {
 | 
						|
				assert.EqualError(t, err, tc.ExpectedError.Error())
 | 
						|
			} else {
 | 
						|
				assert.NoError(t, err)
 | 
						|
			}
 | 
						|
			assert.Equal(t, tc.ExpectedRefreshed, refreshed)
 | 
						|
			assert.Equal(t, tc.ExpectedEmail, tc.ExistingSession.Email)
 | 
						|
			assert.Equal(t, tc.ExpectedUser, tc.ExistingSession.User)
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 |