1
0
mirror of https://github.com/oauth2-proxy/oauth2-proxy.git synced 2025-10-08 22:51:59 +02:00
Files
oauth2-proxy/providers/cidaas_test.go
Kevin Kreitner 4c86a4d574 feat: add Cidaas provider (#2273)
* Add sensible logging flag to default setup for logger

* Fix default value flag for sensitive logging

* Remove sensitive logging changes

* Add Cidaas provider

* Update CHANGELOG.md

* Add required groups scope to defaults

* Fix tests

* Remove if block with protected resource

* Fix linting

* Adjust provider sorting, fixes

* Directly handle error return

Co-authored-by: Jan Larwig <jan@larwig.com>

* Use less deep nesting

Co-authored-by: Jan Larwig <jan@larwig.com>

* Directly handle returned error

Co-authored-by: Jan Larwig <jan@larwig.com>

* Pass provider options to Cidaas provider

Co-authored-by: Jan Larwig <jan@larwig.com>

* Add import for provider options

* Fix tests

* Fix linting

* Add Cidaas doc page

* Add Cidaas provider doc page to overview

* Fix link in docs

* Fix link in docs

* Add link to Cidaas

* fix provider order in docs and changelog position

Signed-off-by: Jan Larwig <jan@larwig.com>

---------

Signed-off-by: Jan Larwig <jan@larwig.com>
Co-authored-by: Teko012 <112829523+Teko012@users.noreply.github.com>
Co-authored-by: Jan Larwig <jan@larwig.com>
Co-authored-by: Kevin Kreitner <kevinkreitner@gmail.com>
2025-08-12 17:41:45 +02:00

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)
})
}
}