You've already forked oauth2-proxy
mirror of
https://github.com/oauth2-proxy/oauth2-proxy.git
synced 2025-11-27 22:38:39 +02:00
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>
This commit is contained in:
493
providers/cidaas_test.go
Normal file
493
providers/cidaas_test.go
Normal file
@@ -0,0 +1,493 @@
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user