add the IDP display name to the downstream ID token's sub
claim
To make the subject of the downstream ID token more unique when there are multiple IDPs. It is possible to define two IDPs in a FederationDomain using the same identity provider CR, in which case the only thing that would make the subject claim different is adding the IDP display name into the values of the subject claim.
This commit is contained in:
parent
28210ab14d
commit
e2bdab9e2d
@ -246,7 +246,7 @@ if [[ "$use_oidc_upstream" == "yes" ]]; then
|
||||
fd_idps="${fd_idps}$(
|
||||
cat <<EOF
|
||||
|
||||
- displayName: "My OIDC IDP"
|
||||
- displayName: "My OIDC IDP 🚀"
|
||||
objectRef:
|
||||
apiGroup: idp.supervisor.pinniped.dev
|
||||
kind: OIDCIdentityProvider
|
||||
@ -272,7 +272,7 @@ if [[ "$use_ldap_upstream" == "yes" ]]; then
|
||||
fd_idps="${fd_idps}$(
|
||||
cat <<EOF
|
||||
|
||||
- displayName: "My LDAP IDP"
|
||||
- displayName: "My LDAP IDP 🚀"
|
||||
objectRef:
|
||||
apiGroup: idp.supervisor.pinniped.dev
|
||||
kind: LDAPIdentityProvider
|
||||
|
@ -41,6 +41,8 @@ const (
|
||||
emailVerifiedClaimInvalidFormatErr = constable.Error("email_verified claim in upstream ID token has invalid format")
|
||||
emailVerifiedClaimFalseErr = constable.Error("email_verified claim in upstream ID token has false value")
|
||||
idTransformUnexpectedErr = constable.Error("configured identity transformation or policy resulted in unexpected error")
|
||||
|
||||
idpNameSubjectQueryParam = "idpName"
|
||||
)
|
||||
|
||||
// MakeDownstreamSession creates a downstream OIDC session.
|
||||
@ -213,8 +215,9 @@ func AutoApproveScopes(authorizeRequester fosite.AuthorizeRequester) {
|
||||
func GetDownstreamIdentityFromUpstreamIDToken(
|
||||
upstreamIDPConfig upstreamprovider.UpstreamOIDCIdentityProviderI,
|
||||
idTokenClaims map[string]interface{},
|
||||
idpDisplayName string,
|
||||
) (string, string, []string, error) {
|
||||
subject, username, err := getSubjectAndUsernameFromUpstreamIDToken(upstreamIDPConfig, idTokenClaims)
|
||||
subject, username, err := getSubjectAndUsernameFromUpstreamIDToken(upstreamIDPConfig, idTokenClaims, idpDisplayName)
|
||||
if err != nil {
|
||||
return "", "", nil, err
|
||||
}
|
||||
@ -275,6 +278,7 @@ func ApplyIdentityTransformations(
|
||||
func getSubjectAndUsernameFromUpstreamIDToken(
|
||||
upstreamIDPConfig upstreamprovider.UpstreamOIDCIdentityProviderI,
|
||||
idTokenClaims map[string]interface{},
|
||||
idpDisplayName string,
|
||||
) (string, string, error) {
|
||||
// The spec says the "sub" claim is only unique per issuer,
|
||||
// so we will prepend the issuer string to make it globally unique.
|
||||
@ -286,11 +290,11 @@ func getSubjectAndUsernameFromUpstreamIDToken(
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
subject := downstreamSubjectFromUpstreamOIDC(upstreamIssuer, upstreamSubject)
|
||||
subject := downstreamSubjectFromUpstreamOIDC(upstreamIssuer, upstreamSubject, idpDisplayName)
|
||||
|
||||
usernameClaimName := upstreamIDPConfig.GetUsernameClaim()
|
||||
if usernameClaimName == "" {
|
||||
return subject, subject, nil
|
||||
return subject, downstreamUsernameFromUpstreamOIDCSubject(upstreamIssuer, upstreamSubject), nil
|
||||
}
|
||||
|
||||
// If the upstream username claim is configured to be the special "email" claim and the upstream "email_verified"
|
||||
@ -358,20 +362,34 @@ func ExtractStringClaimValue(claimName string, upstreamIDPName string, idTokenCl
|
||||
return valueAsString, nil
|
||||
}
|
||||
|
||||
func DownstreamSubjectFromUpstreamLDAP(ldapUpstream upstreamprovider.UpstreamLDAPIdentityProviderI, authenticateResponse *authenticators.Response) string {
|
||||
func DownstreamSubjectFromUpstreamLDAP(
|
||||
ldapUpstream upstreamprovider.UpstreamLDAPIdentityProviderI,
|
||||
authenticateResponse *authenticators.Response,
|
||||
idpDisplayName string,
|
||||
) string {
|
||||
ldapURL := *ldapUpstream.GetURL()
|
||||
return DownstreamLDAPSubject(authenticateResponse.User.GetUID(), ldapURL)
|
||||
return DownstreamLDAPSubject(authenticateResponse.User.GetUID(), ldapURL, idpDisplayName)
|
||||
}
|
||||
|
||||
func DownstreamLDAPSubject(uid string, ldapURL url.URL) string {
|
||||
func DownstreamLDAPSubject(uid string, ldapURL url.URL, idpDisplayName string) string {
|
||||
q := ldapURL.Query()
|
||||
q.Set(idpNameSubjectQueryParam, idpDisplayName)
|
||||
q.Set(oidcapi.IDTokenClaimSubject, uid)
|
||||
ldapURL.RawQuery = q.Encode()
|
||||
return ldapURL.String()
|
||||
}
|
||||
|
||||
func downstreamSubjectFromUpstreamOIDC(upstreamIssuerAsString string, upstreamSubject string) string {
|
||||
return fmt.Sprintf("%s?%s=%s", upstreamIssuerAsString, oidcapi.IDTokenClaimSubject, url.QueryEscape(upstreamSubject))
|
||||
func downstreamSubjectFromUpstreamOIDC(upstreamIssuerAsString string, upstreamSubject string, idpDisplayName string) string {
|
||||
return fmt.Sprintf("%s?%s=%s&%s=%s", upstreamIssuerAsString,
|
||||
idpNameSubjectQueryParam, url.QueryEscape(idpDisplayName),
|
||||
oidcapi.IDTokenClaimSubject, url.QueryEscape(upstreamSubject),
|
||||
)
|
||||
}
|
||||
|
||||
func downstreamUsernameFromUpstreamOIDCSubject(upstreamIssuerAsString string, upstreamSubject string) string {
|
||||
return fmt.Sprintf("%s?%s=%s", upstreamIssuerAsString,
|
||||
oidcapi.IDTokenClaimSubject, url.QueryEscape(upstreamSubject),
|
||||
)
|
||||
}
|
||||
|
||||
// GetGroupsFromUpstreamIDToken returns mapped group names coerced into a slice of strings.
|
||||
|
@ -5,6 +5,7 @@ package downstreamsession
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -156,3 +157,118 @@ func TestApplyIdentityTransformations(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDownstreamLDAPSubject(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
uid string
|
||||
ldapURL string
|
||||
idpDisplayName string
|
||||
wantSubject string
|
||||
}{
|
||||
{
|
||||
name: "simple display name",
|
||||
uid: "some uid",
|
||||
ldapURL: "ldaps://server.example.com:1234",
|
||||
idpDisplayName: "simpleName",
|
||||
wantSubject: "ldaps://server.example.com:1234?idpName=simpleName&sub=some+uid",
|
||||
},
|
||||
{
|
||||
name: "interesting display name",
|
||||
uid: "some uid",
|
||||
ldapURL: "ldaps://server.example.com:1234",
|
||||
idpDisplayName: "this is a 👍 display name that 🦭 can handle",
|
||||
wantSubject: "ldaps://server.example.com:1234?idpName=this+is+a+%F0%9F%91%8D+display+name+that+%F0%9F%A6%AD+can+handle&sub=some+uid",
|
||||
},
|
||||
{
|
||||
name: "url already has query",
|
||||
uid: "some uid",
|
||||
ldapURL: "ldaps://server.example.com:1234?a=1&b=%F0%9F%A6%AD",
|
||||
idpDisplayName: "some name",
|
||||
wantSubject: "ldaps://server.example.com:1234?a=1&b=%F0%9F%A6%AD&idpName=some+name&sub=some+uid",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
url, err := url.Parse(test.ldapURL)
|
||||
require.NoError(t, err)
|
||||
|
||||
actual := DownstreamLDAPSubject(test.uid, *url, test.idpDisplayName)
|
||||
|
||||
require.Equal(t, test.wantSubject, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDownstreamSubjectFromUpstreamOIDC(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
upstreamIssuerAsString string
|
||||
upstreamSubject string
|
||||
idpDisplayName string
|
||||
wantSubject string
|
||||
}{
|
||||
{
|
||||
name: "simple display name",
|
||||
upstreamIssuerAsString: "https://server.example.com:1234/path",
|
||||
upstreamSubject: "some subject",
|
||||
idpDisplayName: "simpleName",
|
||||
wantSubject: "https://server.example.com:1234/path?idpName=simpleName&sub=some+subject",
|
||||
},
|
||||
{
|
||||
name: "interesting display name",
|
||||
upstreamIssuerAsString: "https://server.example.com:1234/path",
|
||||
upstreamSubject: "some subject",
|
||||
idpDisplayName: "this is a 👍 display name that 🦭 can handle",
|
||||
wantSubject: "https://server.example.com:1234/path?idpName=this+is+a+%F0%9F%91%8D+display+name+that+%F0%9F%A6%AD+can+handle&sub=some+subject",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := downstreamSubjectFromUpstreamOIDC(test.upstreamIssuerAsString, test.upstreamSubject, test.idpDisplayName)
|
||||
|
||||
require.Equal(t, test.wantSubject, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDownstreamUsernameFromUpstreamOIDCSubject(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
upstreamIssuerAsString string
|
||||
upstreamSubject string
|
||||
wantSubject string
|
||||
}{
|
||||
{
|
||||
name: "simple upstreamSubject",
|
||||
upstreamIssuerAsString: "https://server.example.com:1234/path",
|
||||
upstreamSubject: "some subject",
|
||||
wantSubject: "https://server.example.com:1234/path?sub=some+subject",
|
||||
},
|
||||
{
|
||||
name: "interesting upstreamSubject",
|
||||
upstreamIssuerAsString: "https://server.example.com:1234/path",
|
||||
upstreamSubject: "this is a 👍 subject that 🦭 can handle",
|
||||
wantSubject: "https://server.example.com:1234/path?sub=this+is+a+%F0%9F%91%8D+subject+that+%F0%9F%A6%AD+can+handle",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := downstreamUsernameFromUpstreamOIDCSubject(test.upstreamIssuerAsString, test.upstreamSubject)
|
||||
|
||||
require.Equal(t, test.wantSubject, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -108,6 +108,7 @@ func NewHandler(
|
||||
oauthHelperWithStorage,
|
||||
oidcUpstream.Provider,
|
||||
oidcUpstream.Transforms,
|
||||
oidcUpstream.DisplayName,
|
||||
idpNameQueryParamValue,
|
||||
)
|
||||
}
|
||||
@ -130,6 +131,7 @@ func NewHandler(
|
||||
ldapUpstream.Provider,
|
||||
ldapUpstream.SessionProviderType,
|
||||
ldapUpstream.Transforms,
|
||||
ldapUpstream.DisplayName,
|
||||
idpNameQueryParamValue,
|
||||
)
|
||||
}
|
||||
@ -158,6 +160,7 @@ func handleAuthRequestForLDAPUpstreamCLIFlow(
|
||||
ldapUpstream upstreamprovider.UpstreamLDAPIdentityProviderI,
|
||||
idpType psession.ProviderType,
|
||||
identityTransforms *idtransform.TransformationPipeline,
|
||||
idpDisplayName string,
|
||||
idpNameQueryParamValue string,
|
||||
) error {
|
||||
authorizeRequester, created := newAuthorizeRequest(r, w, oauthHelper, true)
|
||||
@ -187,7 +190,7 @@ func handleAuthRequestForLDAPUpstreamCLIFlow(
|
||||
return nil
|
||||
}
|
||||
|
||||
subject := downstreamsession.DownstreamSubjectFromUpstreamLDAP(ldapUpstream, authenticateResponse)
|
||||
subject := downstreamsession.DownstreamSubjectFromUpstreamLDAP(ldapUpstream, authenticateResponse, idpDisplayName)
|
||||
upstreamUsername := authenticateResponse.User.GetName()
|
||||
upstreamGroups := authenticateResponse.User.GetGroups()
|
||||
|
||||
@ -251,6 +254,7 @@ func handleAuthRequestForOIDCUpstreamPasswordGrant(
|
||||
oauthHelper fosite.OAuth2Provider,
|
||||
oidcUpstream upstreamprovider.UpstreamOIDCIdentityProviderI,
|
||||
identityTransforms *idtransform.TransformationPipeline,
|
||||
idpDisplayName string,
|
||||
idpNameQueryParamValue string,
|
||||
) error {
|
||||
authorizeRequester, created := newAuthorizeRequest(r, w, oauthHelper, true)
|
||||
@ -291,7 +295,9 @@ func handleAuthRequestForOIDCUpstreamPasswordGrant(
|
||||
return nil
|
||||
}
|
||||
|
||||
subject, upstreamUsername, upstreamGroups, err := downstreamsession.GetDownstreamIdentityFromUpstreamIDToken(oidcUpstream, token.IDToken.Claims)
|
||||
subject, upstreamUsername, upstreamGroups, err := downstreamsession.GetDownstreamIdentityFromUpstreamIDToken(
|
||||
oidcUpstream, token.IDToken.Claims, idpDisplayName,
|
||||
)
|
||||
if err != nil {
|
||||
// Return a user-friendly error for this case which is entirely within our control.
|
||||
oidc.WriteAuthorizeError(r, w, oauthHelper, authorizeRequester,
|
||||
|
@ -833,7 +833,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
|
||||
wantStatus: http.StatusFound,
|
||||
wantContentType: htmlContentType,
|
||||
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + oidcPasswordGrantUpstreamName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: oidcUpstreamUsername,
|
||||
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -856,7 +856,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
|
||||
wantStatus: http.StatusFound,
|
||||
wantContentType: htmlContentType,
|
||||
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + oidcPasswordGrantUpstreamName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: transformationUsernamePrefix + oidcUpstreamUsername,
|
||||
wantDownstreamIDTokenGroups: testutil.AddPrefixToEach(transformationGroupsPrefix, oidcUpstreamGroupMembership),
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -905,7 +905,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
|
||||
wantStatus: http.StatusFound,
|
||||
wantContentType: htmlContentType,
|
||||
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + oidcPasswordGrantUpstreamName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: oidcUpstreamUsername,
|
||||
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -936,7 +936,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
|
||||
wantStatus: http.StatusFound,
|
||||
wantContentType: htmlContentType,
|
||||
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + oidcPasswordGrantUpstreamName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: oidcUpstreamUsername,
|
||||
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -958,7 +958,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
|
||||
wantStatus: http.StatusFound,
|
||||
wantContentType: htmlContentType,
|
||||
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + ldapUpstreamName + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator,
|
||||
wantDownstreamIDTokenGroups: happyLDAPGroups,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -980,7 +980,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
|
||||
wantStatus: http.StatusFound,
|
||||
wantContentType: htmlContentType,
|
||||
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + ldapUpstreamName + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenUsername: transformationUsernamePrefix + happyLDAPUsernameFromAuthenticator,
|
||||
wantDownstreamIDTokenGroups: testutil.AddPrefixToEach(transformationGroupsPrefix, happyLDAPGroups),
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -1019,7 +1019,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
|
||||
wantStatus: http.StatusFound,
|
||||
wantContentType: htmlContentType,
|
||||
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + activeDirectoryUpstreamName + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator,
|
||||
wantDownstreamIDTokenGroups: happyLDAPGroups,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -1211,7 +1211,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
|
||||
wantStatus: http.StatusFound,
|
||||
wantContentType: htmlContentType,
|
||||
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + oidcPasswordGrantUpstreamName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: oidcUpstreamUsername,
|
||||
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -1234,7 +1234,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
|
||||
wantStatus: http.StatusFound,
|
||||
wantContentType: htmlContentType,
|
||||
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + ldapUpstreamName + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator,
|
||||
wantDownstreamIDTokenGroups: happyLDAPGroups,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -1257,7 +1257,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
|
||||
wantStatus: http.StatusFound,
|
||||
wantContentType: htmlContentType,
|
||||
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + activeDirectoryUpstreamName + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator,
|
||||
wantDownstreamIDTokenGroups: happyLDAPGroups,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -1414,7 +1414,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
|
||||
wantStatus: http.StatusFound,
|
||||
wantContentType: htmlContentType,
|
||||
wantRedirectLocationRegexp: downstreamRedirectURIWithDifferentPort + `\?code=([^&]+)&scope=openid\+username\+groups&state=` + happyState,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + oidcPasswordGrantUpstreamName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: oidcUpstreamUsername,
|
||||
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -1437,7 +1437,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
|
||||
wantStatus: http.StatusFound,
|
||||
wantContentType: htmlContentType,
|
||||
wantRedirectLocationRegexp: downstreamRedirectURIWithDifferentPort + `\?code=([^&]+)&scope=openid\+username\+groups&state=` + happyState,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + ldapUpstreamName + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator,
|
||||
wantDownstreamIDTokenGroups: happyLDAPGroups,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -1478,7 +1478,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
|
||||
wantStatus: http.StatusFound,
|
||||
wantContentType: htmlContentType,
|
||||
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + oidcPasswordGrantUpstreamName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: oidcUpstreamUsername,
|
||||
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -1500,7 +1500,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
|
||||
wantStatus: http.StatusFound,
|
||||
wantContentType: htmlContentType,
|
||||
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + oidcPasswordGrantUpstreamName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: oidcUpstreamUsername,
|
||||
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -1535,7 +1535,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
|
||||
wantStatus: http.StatusFound,
|
||||
wantContentType: htmlContentType,
|
||||
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + oidcPasswordGrantUpstreamName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: oidcUpstreamUsername,
|
||||
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -2668,7 +2668,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
|
||||
wantStatus: http.StatusFound,
|
||||
wantContentType: htmlContentType,
|
||||
wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=username\+groups&state=` + happyState, // username and groups scopes were not requested, but are granted anyway for backwards compatibility
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + oidcPasswordGrantUpstreamName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: oidcUpstreamUsername, // username scope was not requested, but is granted anyway for backwards compatibility
|
||||
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership, // groups scope was not requested, but is granted anyway for backwards compatibility
|
||||
wantDownstreamRequestedScopes: []string{"email"}, // only email was requested
|
||||
@ -2690,7 +2690,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
|
||||
wantStatus: http.StatusFound,
|
||||
wantContentType: htmlContentType,
|
||||
wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=username\+groups&state=` + happyState, // username and groups scopes were not requested, but are granted anyway for backwards compatibility
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + ldapUpstreamName + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator, // username scope was not requested, but is granted anyway for backwards compatibility
|
||||
wantDownstreamIDTokenGroups: happyLDAPGroups, // groups scope was not requested, but is granted anyway for backwards compatibility
|
||||
wantDownstreamRequestedScopes: []string{"email"}, // only email was requested
|
||||
@ -2714,7 +2714,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
|
||||
wantStatus: http.StatusFound,
|
||||
wantContentType: htmlContentType,
|
||||
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + oidcPasswordGrantUpstreamName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenGroups: []string{},
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -2745,7 +2745,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
|
||||
wantStatus: http.StatusFound,
|
||||
wantContentType: htmlContentType,
|
||||
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + oidcPasswordGrantUpstreamName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: "joe@whitehouse.gov",
|
||||
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -2777,7 +2777,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
|
||||
wantStatus: http.StatusFound,
|
||||
wantContentType: htmlContentType,
|
||||
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + oidcPasswordGrantUpstreamName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: "joe@whitehouse.gov",
|
||||
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -2810,7 +2810,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
|
||||
wantStatus: http.StatusFound,
|
||||
wantContentType: htmlContentType,
|
||||
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + oidcPasswordGrantUpstreamName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: "joe",
|
||||
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -2875,7 +2875,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
|
||||
wantStatus: http.StatusFound,
|
||||
wantContentType: htmlContentType,
|
||||
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + oidcPasswordGrantUpstreamName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: oidcUpstreamSubject,
|
||||
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -2905,7 +2905,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
|
||||
wantStatus: http.StatusFound,
|
||||
wantContentType: htmlContentType,
|
||||
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + oidcPasswordGrantUpstreamName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: oidcUpstreamUsername,
|
||||
wantDownstreamIDTokenGroups: []string{"notAnArrayGroup1 notAnArrayGroup2"},
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -2935,7 +2935,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
|
||||
wantStatus: http.StatusFound,
|
||||
wantContentType: htmlContentType,
|
||||
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + oidcPasswordGrantUpstreamName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: oidcUpstreamUsername,
|
||||
wantDownstreamIDTokenGroups: []string{"group1", "group2"},
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -2979,7 +2979,7 @@ func TestAuthorizationEndpoint(t *testing.T) { //nolint:gocyclo
|
||||
wantStatus: http.StatusFound,
|
||||
wantContentType: htmlContentType,
|
||||
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + oidcPasswordGrantUpstreamName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: oidcUpstreamUsername,
|
||||
wantDownstreamIDTokenGroups: []string{},
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
|
@ -70,19 +70,25 @@ func NewHandler(
|
||||
return httperr.New(http.StatusBadGateway, "error exchanging and validating upstream tokens")
|
||||
}
|
||||
|
||||
subject, upstreamUsername, upstreamGroups, err := downstreamsession.GetDownstreamIdentityFromUpstreamIDToken(upstreamIDPConfig, token.IDToken.Claims)
|
||||
subject, upstreamUsername, upstreamGroups, err := downstreamsession.GetDownstreamIdentityFromUpstreamIDToken(
|
||||
upstreamIDPConfig, token.IDToken.Claims, resolvedOIDCIdentityProvider.DisplayName,
|
||||
)
|
||||
if err != nil {
|
||||
return httperr.Wrap(http.StatusUnprocessableEntity, err.Error(), err)
|
||||
}
|
||||
|
||||
username, groups, err := downstreamsession.ApplyIdentityTransformations(r.Context(), resolvedOIDCIdentityProvider.Transforms, upstreamUsername, upstreamGroups)
|
||||
username, groups, err := downstreamsession.ApplyIdentityTransformations(
|
||||
r.Context(), resolvedOIDCIdentityProvider.Transforms, upstreamUsername, upstreamGroups,
|
||||
)
|
||||
if err != nil {
|
||||
return httperr.Wrap(http.StatusUnprocessableEntity, err.Error(), err)
|
||||
}
|
||||
|
||||
additionalClaims := downstreamsession.MapAdditionalClaimsFromUpstreamIDToken(upstreamIDPConfig, token.IDToken.Claims)
|
||||
|
||||
customSessionData, err := downstreamsession.MakeDownstreamOIDCCustomSessionData(upstreamIDPConfig, token, username, upstreamUsername, upstreamGroups)
|
||||
customSessionData, err := downstreamsession.MakeDownstreamOIDCCustomSessionData(
|
||||
upstreamIDPConfig, token, username, upstreamUsername, upstreamGroups,
|
||||
)
|
||||
if err != nil {
|
||||
return httperr.Wrap(http.StatusUnprocessableEntity, err.Error(), err)
|
||||
}
|
||||
|
@ -223,7 +223,7 @@ func TestCallbackEndpoint(t *testing.T) {
|
||||
wantStatus: http.StatusOK,
|
||||
wantContentType: "text/html;charset=UTF-8",
|
||||
wantBodyFormResponseRegexp: `<code id="manual-auth-code">(.+)</code>`,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: oidcUpstreamUsername,
|
||||
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -262,7 +262,7 @@ func TestCallbackEndpoint(t *testing.T) {
|
||||
wantStatus: http.StatusOK,
|
||||
wantContentType: "text/html;charset=UTF-8",
|
||||
wantBodyFormResponseRegexp: `<code id="manual-auth-code">(.+)</code>`,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: oidcUpstreamUsername,
|
||||
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -290,7 +290,7 @@ func TestCallbackEndpoint(t *testing.T) {
|
||||
wantStatus: http.StatusSeeOther,
|
||||
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
|
||||
wantBody: "",
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: oidcUpstreamUsername,
|
||||
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -315,7 +315,7 @@ func TestCallbackEndpoint(t *testing.T) {
|
||||
wantStatus: http.StatusSeeOther,
|
||||
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
|
||||
wantBody: "",
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: oidcUpstreamUsername,
|
||||
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -339,7 +339,7 @@ func TestCallbackEndpoint(t *testing.T) {
|
||||
wantStatus: http.StatusSeeOther,
|
||||
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
|
||||
wantBody: "",
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: oidcUpstreamUsername,
|
||||
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -373,7 +373,7 @@ func TestCallbackEndpoint(t *testing.T) {
|
||||
wantStatus: http.StatusOK,
|
||||
wantContentType: "text/html;charset=UTF-8",
|
||||
wantBodyFormResponseRegexp: `<code id="manual-auth-code">(.+)</code>`,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: oidcUpstreamUsername,
|
||||
wantDownstreamRequestedScopes: []string{"openid"},
|
||||
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
|
||||
@ -398,7 +398,7 @@ func TestCallbackEndpoint(t *testing.T) {
|
||||
wantStatus: http.StatusSeeOther,
|
||||
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
|
||||
wantBody: "",
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: oidcUpstreamUsername,
|
||||
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -437,7 +437,7 @@ func TestCallbackEndpoint(t *testing.T) {
|
||||
wantStatus: http.StatusSeeOther,
|
||||
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
|
||||
wantBody: "",
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenGroups: []string{},
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -467,7 +467,7 @@ func TestCallbackEndpoint(t *testing.T) {
|
||||
wantStatus: http.StatusSeeOther,
|
||||
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
|
||||
wantBody: "",
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: "joe@whitehouse.gov",
|
||||
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -499,7 +499,7 @@ func TestCallbackEndpoint(t *testing.T) {
|
||||
wantStatus: http.StatusSeeOther,
|
||||
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
|
||||
wantBody: "",
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: "joe@whitehouse.gov",
|
||||
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -532,7 +532,7 @@ func TestCallbackEndpoint(t *testing.T) {
|
||||
wantStatus: http.StatusSeeOther, // succeed despite `email_verified=false` because we're not using the email claim for anything
|
||||
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
|
||||
wantBody: "",
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: "joe",
|
||||
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -667,7 +667,7 @@ func TestCallbackEndpoint(t *testing.T) {
|
||||
wantStatus: http.StatusSeeOther,
|
||||
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
|
||||
wantBody: "",
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: oidcUpstreamSubject,
|
||||
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -697,7 +697,7 @@ func TestCallbackEndpoint(t *testing.T) {
|
||||
wantStatus: http.StatusSeeOther,
|
||||
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
|
||||
wantBody: "",
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: oidcUpstreamUsername,
|
||||
wantDownstreamIDTokenGroups: []string{"notAnArrayGroup1 notAnArrayGroup2"},
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -727,7 +727,7 @@ func TestCallbackEndpoint(t *testing.T) {
|
||||
wantStatus: http.StatusSeeOther,
|
||||
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
|
||||
wantBody: "",
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: oidcUpstreamUsername,
|
||||
wantDownstreamIDTokenGroups: []string{"group1", "group2"},
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -761,7 +761,7 @@ func TestCallbackEndpoint(t *testing.T) {
|
||||
wantStatus: http.StatusSeeOther,
|
||||
wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid\+offline_access\+groups&state=` + happyDownstreamState,
|
||||
wantBody: "",
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: "", // username scope was not requested
|
||||
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
|
||||
wantDownstreamRequestedScopes: []string{"openid", "groups", "offline_access"},
|
||||
@ -791,7 +791,7 @@ func TestCallbackEndpoint(t *testing.T) {
|
||||
wantStatus: http.StatusSeeOther,
|
||||
wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid\+offline_access\+username&state=` + happyDownstreamState,
|
||||
wantBody: "",
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: oidcUpstreamUsername,
|
||||
wantDownstreamIDTokenGroups: nil, // groups scope was not requested
|
||||
wantDownstreamRequestedScopes: []string{"openid", "username", "offline_access"},
|
||||
@ -834,7 +834,7 @@ func TestCallbackEndpoint(t *testing.T) {
|
||||
wantStatus: http.StatusSeeOther,
|
||||
wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid\+offline_access\+groups&state=` + happyDownstreamState,
|
||||
wantBody: "",
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: "", // username scope was not requested
|
||||
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
|
||||
wantDownstreamRequestedScopes: []string{"openid", "groups", "offline_access"},
|
||||
@ -877,7 +877,7 @@ func TestCallbackEndpoint(t *testing.T) {
|
||||
wantStatus: http.StatusSeeOther,
|
||||
wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid\+offline_access\+username&state=` + happyDownstreamState,
|
||||
wantBody: "",
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: oidcUpstreamUsername,
|
||||
wantDownstreamIDTokenGroups: nil, // groups scope was not requested
|
||||
wantDownstreamRequestedScopes: []string{"openid", "username", "offline_access"},
|
||||
@ -902,7 +902,7 @@ func TestCallbackEndpoint(t *testing.T) {
|
||||
wantStatus: http.StatusSeeOther,
|
||||
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
|
||||
wantBody: "",
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: transformationUsernamePrefix + oidcUpstreamUsername,
|
||||
wantDownstreamIDTokenGroups: testutil.AddPrefixToEach(transformationGroupsPrefix, oidcUpstreamGroupMembership),
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -1158,7 +1158,7 @@ func TestCallbackEndpoint(t *testing.T) {
|
||||
wantStatus: http.StatusSeeOther,
|
||||
wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=username\+groups&state=` + happyDownstreamState,
|
||||
wantDownstreamIDTokenUsername: oidcUpstreamUsername,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamRequestedScopes: []string{"profile", "email", "username", "groups"},
|
||||
wantDownstreamGrantedScopes: []string{"username", "groups"},
|
||||
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
|
||||
@ -1188,7 +1188,7 @@ func TestCallbackEndpoint(t *testing.T) {
|
||||
wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=username\+groups&state=` + happyDownstreamState,
|
||||
wantDownstreamIDTokenUsername: oidcUpstreamUsername,
|
||||
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamRequestedScopes: []string{"profile", "email"},
|
||||
// username and groups scopes were not requested but are granted anyway for the pinniped-cli client for backwards compatibility
|
||||
wantDownstreamGrantedScopes: []string{"username", "groups"},
|
||||
@ -1217,7 +1217,7 @@ func TestCallbackEndpoint(t *testing.T) {
|
||||
wantStatus: http.StatusSeeOther,
|
||||
wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid\+offline_access\+username\+groups&state=` + happyDownstreamState,
|
||||
wantDownstreamIDTokenUsername: oidcUpstreamUsername,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamRequestedScopes: []string{"openid", "offline_access", "username", "groups"},
|
||||
wantDownstreamGrantedScopes: []string{"openid", "offline_access", "username", "groups"},
|
||||
wantDownstreamIDTokenGroups: oidcUpstreamGroupMembership,
|
||||
@ -1315,7 +1315,7 @@ func TestCallbackEndpoint(t *testing.T) {
|
||||
wantStatus: http.StatusSeeOther,
|
||||
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
|
||||
wantBody: "",
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?idpName=" + happyUpstreamIDPName + "&sub=" + oidcUpstreamSubjectQueryEscaped,
|
||||
wantDownstreamIDTokenUsername: oidcUpstreamUsername,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
wantDownstreamGrantedScopes: happyDownstreamScopesGranted,
|
||||
|
@ -63,7 +63,9 @@ func NewPostHandler(issuerURL string, upstreamIDPs federationdomainproviders.Fed
|
||||
}
|
||||
|
||||
// Attempt to authenticate the user with the upstream IDP.
|
||||
authenticateResponse, authenticated, err := ldapUpstream.Provider.AuthenticateUser(r.Context(), submittedUsername, submittedPassword, authorizeRequester.GetGrantedScopes())
|
||||
authenticateResponse, authenticated, err := ldapUpstream.Provider.AuthenticateUser(
|
||||
r.Context(), submittedUsername, submittedPassword, authorizeRequester.GetGrantedScopes(),
|
||||
)
|
||||
if err != nil {
|
||||
plog.WarningErr("unexpected error during upstream LDAP authentication", err, "upstreamName", ldapUpstream.Provider.GetName())
|
||||
// There was some problem during authentication with the upstream, aside from bad username/password.
|
||||
@ -80,11 +82,15 @@ func NewPostHandler(issuerURL string, upstreamIDPs federationdomainproviders.Fed
|
||||
// Now the upstream IDP has authenticated the user, so now we're back into the regular OIDC authcode flow steps.
|
||||
// Both success and error responses from this point onwards should look like the usual fosite redirect
|
||||
// responses, and a happy redirect response will include a downstream authcode.
|
||||
subject := downstreamsession.DownstreamSubjectFromUpstreamLDAP(ldapUpstream.Provider, authenticateResponse)
|
||||
subject := downstreamsession.DownstreamSubjectFromUpstreamLDAP(
|
||||
ldapUpstream.Provider, authenticateResponse, ldapUpstream.DisplayName,
|
||||
)
|
||||
upstreamUsername := authenticateResponse.User.GetName()
|
||||
upstreamGroups := authenticateResponse.User.GetGroups()
|
||||
|
||||
username, groups, err := downstreamsession.ApplyIdentityTransformations(r.Context(), ldapUpstream.Transforms, upstreamUsername, upstreamGroups)
|
||||
username, groups, err := downstreamsession.ApplyIdentityTransformations(
|
||||
r.Context(), ldapUpstream.Transforms, upstreamUsername, upstreamGroups,
|
||||
)
|
||||
if err != nil {
|
||||
oidc.WriteAuthorizeError(r, w, oauthHelper, authorizeRequester,
|
||||
fosite.ErrAccessDenied.WithHintf("Reason: %s.", err.Error()), false,
|
||||
@ -92,7 +98,8 @@ func NewPostHandler(issuerURL string, upstreamIDPs federationdomainproviders.Fed
|
||||
return nil
|
||||
}
|
||||
|
||||
customSessionData := downstreamsession.MakeDownstreamLDAPOrADCustomSessionData(ldapUpstream.Provider, ldapUpstream.SessionProviderType, authenticateResponse, username, upstreamUsername, upstreamGroups)
|
||||
customSessionData := downstreamsession.MakeDownstreamLDAPOrADCustomSessionData(
|
||||
ldapUpstream.Provider, ldapUpstream.SessionProviderType, authenticateResponse, username, upstreamUsername, upstreamGroups)
|
||||
openIDSession := downstreamsession.MakeDownstreamSession(subject, username, groups,
|
||||
authorizeRequester.GetGrantedScopes(), authorizeRequester.GetClient().GetID(), customSessionData, map[string]interface{}{})
|
||||
oidc.PerformAuthcodeRedirect(r, w, oauthHelper, authorizeRequester, openIDSession, false)
|
||||
|
@ -338,7 +338,7 @@ func TestPostLoginEndpoint(t *testing.T) {
|
||||
wantContentType: htmlContentType,
|
||||
wantBodyString: "",
|
||||
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + ldapUpstreamName + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator,
|
||||
wantDownstreamIDTokenGroups: happyLDAPGroups,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -361,7 +361,7 @@ func TestPostLoginEndpoint(t *testing.T) {
|
||||
wantContentType: htmlContentType,
|
||||
wantBodyString: "",
|
||||
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + ldapUpstreamName + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenUsername: transformationUsernamePrefix + happyLDAPUsernameFromAuthenticator,
|
||||
wantDownstreamIDTokenGroups: testutil.AddPrefixToEach(transformationGroupsPrefix, happyLDAPGroups),
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -390,7 +390,7 @@ func TestPostLoginEndpoint(t *testing.T) {
|
||||
wantContentType: htmlContentType,
|
||||
wantBodyString: "",
|
||||
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + ldapUpstreamName + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator,
|
||||
wantDownstreamIDTokenGroups: happyLDAPGroups,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -413,7 +413,7 @@ func TestPostLoginEndpoint(t *testing.T) {
|
||||
wantContentType: htmlContentType,
|
||||
wantBodyString: "",
|
||||
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + activeDirectoryUpstreamName + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator,
|
||||
wantDownstreamIDTokenGroups: happyLDAPGroups,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -436,7 +436,7 @@ func TestPostLoginEndpoint(t *testing.T) {
|
||||
wantContentType: htmlContentType,
|
||||
wantBodyString: "",
|
||||
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + activeDirectoryUpstreamName + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenUsername: transformationUsernamePrefix + happyLDAPUsernameFromAuthenticator,
|
||||
wantDownstreamIDTokenGroups: testutil.AddPrefixToEach(transformationGroupsPrefix, happyLDAPGroups),
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -465,7 +465,7 @@ func TestPostLoginEndpoint(t *testing.T) {
|
||||
wantContentType: htmlContentType,
|
||||
wantBodyString: "",
|
||||
wantRedirectLocationRegexp: happyAuthcodeDownstreamRedirectLocationRegexp,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + activeDirectoryUpstreamName + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator,
|
||||
wantDownstreamIDTokenGroups: happyLDAPGroups,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -490,7 +490,7 @@ func TestPostLoginEndpoint(t *testing.T) {
|
||||
wantContentType: htmlContentType,
|
||||
wantBodyFormResponseRegexp: `(?s)<html.*<script>.*To finish logging in, paste this authorization code` +
|
||||
`.*<form>.*<code id="manual-auth-code">(.+)</code>.*</html>`, // "(?s)" means match "." across newlines
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + ldapUpstreamName + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator,
|
||||
wantDownstreamIDTokenGroups: happyLDAPGroups,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -515,7 +515,7 @@ func TestPostLoginEndpoint(t *testing.T) {
|
||||
wantContentType: htmlContentType,
|
||||
wantBodyString: "",
|
||||
wantRedirectLocationRegexp: "http://127.0.0.1:4242/callback" + `\?code=([^&]+)&scope=openid\+username\+groups&state=` + happyDownstreamState,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + ldapUpstreamName + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator,
|
||||
wantDownstreamIDTokenGroups: happyLDAPGroups,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -541,7 +541,7 @@ func TestPostLoginEndpoint(t *testing.T) {
|
||||
wantContentType: htmlContentType,
|
||||
wantBodyString: "",
|
||||
wantRedirectLocationRegexp: "http://127.0.0.1:4242/callback" + `\?code=([^&]+)&scope=openid\+username\+groups&state=` + happyDownstreamState,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + ldapUpstreamName + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator,
|
||||
wantDownstreamIDTokenGroups: happyLDAPGroups,
|
||||
wantDownstreamRequestedScopes: happyDownstreamScopesRequested,
|
||||
@ -567,7 +567,7 @@ func TestPostLoginEndpoint(t *testing.T) {
|
||||
wantBodyString: "",
|
||||
// username and groups scopes were not requested but are granted anyway for the pinniped-cli client for backwards compatibility
|
||||
wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid\+offline_access\+pinniped%3Arequest-audience\+username\+groups&state=` + happyDownstreamState,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + ldapUpstreamName + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator,
|
||||
wantDownstreamIDTokenGroups: happyLDAPGroups,
|
||||
wantDownstreamRequestedScopes: []string{"openid", "offline_access", "pinniped:request-audience"},
|
||||
@ -593,7 +593,7 @@ func TestPostLoginEndpoint(t *testing.T) {
|
||||
wantContentType: htmlContentType,
|
||||
wantBodyString: "",
|
||||
wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid\+offline_access\+pinniped%3Arequest-audience&state=` + happyDownstreamState,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + ldapUpstreamName + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenUsername: "", // username scope was not requested, so there should be no username in the ID token
|
||||
wantDownstreamIDTokenGroups: []string{}, // groups scope was not requested, so there should be no groups in the ID token
|
||||
wantDownstreamRequestedScopes: []string{"openid", "offline_access", "pinniped:request-audience"},
|
||||
@ -627,7 +627,7 @@ func TestPostLoginEndpoint(t *testing.T) {
|
||||
wantContentType: htmlContentType,
|
||||
wantBodyString: "",
|
||||
wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid\+offline_access\+groups&state=` + happyDownstreamState,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + ldapUpstreamName + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenUsername: "", // username scope was not requested, so there should be no username in the ID token
|
||||
wantDownstreamIDTokenGroups: happyLDAPGroups,
|
||||
wantDownstreamRequestedScopes: []string{"openid", "offline_access", "groups"},
|
||||
@ -661,7 +661,7 @@ func TestPostLoginEndpoint(t *testing.T) {
|
||||
wantContentType: htmlContentType,
|
||||
wantBodyString: "",
|
||||
wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid\+offline_access\+username&state=` + happyDownstreamState,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + ldapUpstreamName + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator,
|
||||
wantDownstreamIDTokenGroups: []string{}, // groups scope was not requested, so there should be no groups in the ID token
|
||||
wantDownstreamRequestedScopes: []string{"openid", "offline_access", "username"},
|
||||
@ -691,7 +691,7 @@ func TestPostLoginEndpoint(t *testing.T) {
|
||||
wantBodyString: "",
|
||||
// username and groups scopes were not requested but are granted anyway for the pinniped-cli client for backwards compatibility
|
||||
wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=username\+groups&state=` + happyDownstreamState,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + ldapUpstreamName + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator,
|
||||
wantDownstreamIDTokenGroups: happyLDAPGroups,
|
||||
wantDownstreamRequestedScopes: []string{"email"}, // only email was requested
|
||||
@ -719,7 +719,7 @@ func TestPostLoginEndpoint(t *testing.T) {
|
||||
wantBodyString: "",
|
||||
// username and groups scopes were not requested but are granted anyway for the pinniped-cli client for backwards compatibility
|
||||
wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid\+username\+groups&state=` + happyDownstreamState,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenSubject: upstreamLDAPURL + "&idpName=" + ldapUpstreamName + "&sub=" + happyLDAPUID,
|
||||
wantDownstreamIDTokenUsername: happyLDAPUsernameFromAuthenticator,
|
||||
wantDownstreamIDTokenGroups: happyLDAPGroups,
|
||||
wantDownstreamRequestedScopes: []string{"openid"},
|
||||
|
@ -394,7 +394,7 @@ func upstreamLDAPRefresh(
|
||||
Groups: oldUntransformedGroups,
|
||||
AdditionalAttributes: additionalAttributes,
|
||||
GrantedScopes: grantedScopes,
|
||||
})
|
||||
}, p.DisplayName)
|
||||
if err != nil {
|
||||
return errUpstreamRefreshError().WithHint(
|
||||
"Upstream refresh failed.").WithTrace(err).
|
||||
|
@ -122,5 +122,5 @@ type UpstreamLDAPIdentityProviderI interface {
|
||||
authenticators.UserAuthenticator
|
||||
|
||||
// PerformRefresh performs a refresh against the upstream LDAP identity provider
|
||||
PerformRefresh(ctx context.Context, storedRefreshAttributes RefreshAttributes) (groups []string, err error)
|
||||
PerformRefresh(ctx context.Context, storedRefreshAttributes RefreshAttributes, idpDisplayName string) (groups []string, err error)
|
||||
}
|
||||
|
@ -220,7 +220,7 @@ func (u *TestUpstreamLDAPIdentityProvider) GetURL() *url.URL {
|
||||
return u.URL
|
||||
}
|
||||
|
||||
func (u *TestUpstreamLDAPIdentityProvider) PerformRefresh(ctx context.Context, storedRefreshAttributes upstreamprovider.RefreshAttributes) ([]string, error) {
|
||||
func (u *TestUpstreamLDAPIdentityProvider) PerformRefresh(ctx context.Context, storedRefreshAttributes upstreamprovider.RefreshAttributes, idpDisplayName string) ([]string, error) {
|
||||
if u.performRefreshArgs == nil {
|
||||
u.performRefreshArgs = make([]*PerformRefreshArgs, 0)
|
||||
}
|
||||
|
@ -188,7 +188,7 @@ func closeAndLogError(conn Conn, doingWhat string) {
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Provider) PerformRefresh(ctx context.Context, storedRefreshAttributes upstreamprovider.RefreshAttributes) ([]string, error) {
|
||||
func (p *Provider) PerformRefresh(ctx context.Context, storedRefreshAttributes upstreamprovider.RefreshAttributes, idpDisplayName string) ([]string, error) {
|
||||
t := trace.FromContext(ctx).Nest("slow ldap refresh attempt", trace.Field{Key: "providerName", Value: p.GetName()})
|
||||
defer t.LogIfLong(500 * time.Millisecond) // to help users debug slow LDAP searches
|
||||
userDN := storedRefreshAttributes.DN
|
||||
@ -237,7 +237,7 @@ func (p *Provider) PerformRefresh(ctx context.Context, storedRefreshAttributes u
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newSubject := downstreamsession.DownstreamLDAPSubject(newUID, *p.GetURL())
|
||||
newSubject := downstreamsession.DownstreamLDAPSubject(newUID, *p.GetURL(), idpDisplayName)
|
||||
if newSubject != storedRefreshAttributes.Subject {
|
||||
return nil, fmt.Errorf(`searching for user %q produced a different subject than the previous value. expected: %q, actual: %q`, userDN, storedRefreshAttributes.Subject, newSubject)
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ const (
|
||||
testHost = "ldap.example.com:8443"
|
||||
testBindUsername = "cn=some-bind-username,dc=pinniped,dc=dev"
|
||||
testBindPassword = "some-bind-password"
|
||||
testUpstreamName = "some-upstream-idp-name"
|
||||
testUpstreamUsername = "some-upstream-username"
|
||||
testUpstreamPassword = "some-upstream-password"
|
||||
testUserSearchBase = "some-upstream-user-base-dn"
|
||||
@ -2046,7 +2047,7 @@ func TestUpstreamRefresh(t *testing.T) {
|
||||
}, nil).Times(1)
|
||||
conn.EXPECT().Close().Times(1)
|
||||
},
|
||||
wantErr: "searching for user \"some-upstream-user-dn\" produced a different subject than the previous value. expected: \"ldaps://ldap.example.com:8443?base=some-upstream-user-base-dn&sub=c29tZS11cHN0cmVhbS11aWQtdmFsdWU\", actual: \"ldaps://ldap.example.com:8443?base=some-upstream-user-base-dn&sub=d3JvbmctdWlk\"",
|
||||
wantErr: "searching for user \"some-upstream-user-dn\" produced a different subject than the previous value. expected: \"ldaps://ldap.example.com:8443?base=some-upstream-user-base-dn&idpName=some-upstream-idp-name&sub=c29tZS11cHN0cmVhbS11aWQtdmFsdWU\", actual: \"ldaps://ldap.example.com:8443?base=some-upstream-user-base-dn&idpName=some-upstream-idp-name&sub=d3JvbmctdWlk\"",
|
||||
},
|
||||
{
|
||||
name: "search result has wrong username",
|
||||
@ -2279,14 +2280,17 @@ func TestUpstreamRefresh(t *testing.T) {
|
||||
}
|
||||
initialPwdLastSetEncoded := base64.RawURLEncoding.EncodeToString([]byte("132801740800000000"))
|
||||
ldapProvider := New(*tt.providerConfig)
|
||||
subject := "ldaps://ldap.example.com:8443?base=some-upstream-user-base-dn&sub=c29tZS11cHN0cmVhbS11aWQtdmFsdWU"
|
||||
subject := fmt.Sprintf(
|
||||
"ldaps://ldap.example.com:8443?base=some-upstream-user-base-dn&idpName=%s&sub=c29tZS11cHN0cmVhbS11aWQtdmFsdWU",
|
||||
testUpstreamName,
|
||||
)
|
||||
groups, err := ldapProvider.PerformRefresh(context.Background(), upstreamprovider.RefreshAttributes{
|
||||
Username: testUserSearchResultUsernameAttributeValue,
|
||||
Subject: subject,
|
||||
DN: tt.refreshUserDN,
|
||||
AdditionalAttributes: map[string]string{pwdLastSetAttribute: initialPwdLastSetEncoded},
|
||||
GrantedScopes: tt.grantedScopes,
|
||||
})
|
||||
}, testUpstreamName)
|
||||
if tt.wantErr != "" {
|
||||
require.Error(t, err)
|
||||
require.Equal(t, tt.wantErr, err.Error())
|
||||
|
@ -162,6 +162,49 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
return ldapIDP, secret
|
||||
}
|
||||
|
||||
// The downstream ID token Subject should include the upstream user ID after the upstream issuer name
|
||||
// and IDP display name.
|
||||
expectedIDTokenSubjectRegexForUpstreamOIDC := "^" +
|
||||
regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?idpName=test-upstream-oidc-idp-") + `[\w]+` +
|
||||
regexp.QuoteMeta("&sub=") + ".+" +
|
||||
"$"
|
||||
|
||||
// The downstream ID token Subject should be the Host URL, plus the user search base, plus the IDP display name,
|
||||
// plus value pulled from the requested UserSearch.Attributes.UID attribute.
|
||||
expectedIDTokenSubjectRegexForUpstreamLDAP := "^" +
|
||||
regexp.QuoteMeta("ldaps://"+env.SupervisorUpstreamLDAP.Host+"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)) +
|
||||
regexp.QuoteMeta("&idpName=test-upstream-ldap-idp-") + `[\w]+` +
|
||||
regexp.QuoteMeta("&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue))) +
|
||||
"$"
|
||||
|
||||
// Some of the LDAP tests below use the StartTLS server and port.
|
||||
expectedIDTokenSubjectRegexForUpstreamLDAPStartTLSHost := "^" +
|
||||
regexp.QuoteMeta("ldaps://"+env.SupervisorUpstreamLDAP.StartTLSOnlyHost+"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)) +
|
||||
regexp.QuoteMeta("&idpName=test-upstream-ldap-idp-") + `[\w]+` +
|
||||
regexp.QuoteMeta("&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue))) +
|
||||
"$"
|
||||
|
||||
// The downstream ID token Subject should be in the the same format as LDAP above, but with AD-specific values.
|
||||
expectedIDTokenSubjectRegexForUpstreamAD := "^" +
|
||||
regexp.QuoteMeta("ldaps://"+env.SupervisorUpstreamActiveDirectory.Host+"?base="+url.QueryEscape(env.SupervisorUpstreamActiveDirectory.DefaultNamingContextSearchBase)) +
|
||||
regexp.QuoteMeta("&idpName=test-upstream-ad-idp-") + `[\w]+` +
|
||||
regexp.QuoteMeta("&sub="+env.SupervisorUpstreamActiveDirectory.TestUserUniqueIDAttributeValue) +
|
||||
"$"
|
||||
|
||||
// Sometimes the AD tests below use a different search base.
|
||||
expectedIDTokenSubjectRegexForUpstreamADWithCustomUserSearchBase := "^" +
|
||||
regexp.QuoteMeta("ldaps://"+env.SupervisorUpstreamActiveDirectory.Host+"?base="+url.QueryEscape(env.SupervisorUpstreamActiveDirectory.UserSearchBase)) +
|
||||
regexp.QuoteMeta("&idpName=test-upstream-ad-idp-") + `[\w]+` +
|
||||
regexp.QuoteMeta("&sub="+env.SupervisorUpstreamActiveDirectory.TestUserUniqueIDAttributeValue) +
|
||||
"$"
|
||||
|
||||
// Sometimes the AD tests below create a user dynamically so we can't know the UID up front.
|
||||
expectedIDTokenSubjectRegexForUpstreamADWithUnkownUID := "^" +
|
||||
regexp.QuoteMeta("ldaps://"+env.SupervisorUpstreamActiveDirectory.Host+"?base="+url.QueryEscape(env.SupervisorUpstreamActiveDirectory.DefaultNamingContextSearchBase)) +
|
||||
regexp.QuoteMeta("&idpName=test-upstream-ad-idp-") + `[\w]+` +
|
||||
regexp.QuoteMeta("&sub=") + ".+" +
|
||||
"$"
|
||||
|
||||
// These tests attempt to exercise the entire login and refresh flow of the Supervisor for various cases.
|
||||
// They do not use the Pinniped CLI as the client, which allows them to exercise the Supervisor as an
|
||||
// OIDC provider in ways that the CLI might not use. Similar tests exist using the CLI in e2e_test.go.
|
||||
@ -275,8 +318,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
pinnipedSessionData := pinnipedSession.Custom
|
||||
pinnipedSessionData.OIDC.UpstreamIssuer = "wrong-issuer"
|
||||
},
|
||||
// the ID token Subject should include the upstream user ID after the upstream issuer name
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+",
|
||||
wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamOIDC,
|
||||
// the ID token Username should include the upstream user ID after the upstream issuer name
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+" },
|
||||
},
|
||||
@ -299,7 +341,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
customSessionData := pinnipedSession.Custom
|
||||
customSessionData.Username = "some-incorrect-username"
|
||||
},
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+",
|
||||
wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamOIDC,
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Username) + "$" },
|
||||
wantDownstreamIDTokenGroups: env.SupervisorUpstreamOIDC.ExpectedGroups,
|
||||
editRefreshSessionDataWithoutBreaking: func(t *testing.T, sessionData *psession.PinnipedSession, _, _ string) []string {
|
||||
@ -346,7 +388,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
customSessionData := pinnipedSession.Custom
|
||||
customSessionData.Username = "some-incorrect-username"
|
||||
},
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+",
|
||||
wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamOIDC,
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Username) + "$" },
|
||||
wantDownstreamIDTokenGroups: env.SupervisorUpstreamOIDC.ExpectedGroups,
|
||||
},
|
||||
@ -375,8 +417,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
require.NotEmpty(t, customSessionData.OIDC.UpstreamRefreshToken)
|
||||
customSessionData.OIDC.UpstreamRefreshToken = "invalid-updated-refresh-token"
|
||||
},
|
||||
// the ID token Subject should include the upstream user ID after the upstream issuer name
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+",
|
||||
wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamOIDC,
|
||||
// the ID token Username should include the upstream user ID after the upstream issuer name
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+" },
|
||||
},
|
||||
@ -406,8 +447,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
false,
|
||||
)
|
||||
},
|
||||
// the ID token Subject should include the upstream user ID after the upstream issuer name
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+",
|
||||
wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamOIDC,
|
||||
// the ID token Username should include the upstream user ID after the upstream issuer name
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+" },
|
||||
wantDownstreamIDTokenAdditionalClaims: wantGroupsInAdditionalClaimsIfGroupsExist(map[string]interface{}{
|
||||
@ -432,8 +472,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
return testlib.CreateTestOIDCIdentityProvider(t, spec, idpv1alpha1.PhaseReady).Name
|
||||
},
|
||||
requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowOIDC,
|
||||
// the ID token Subject should include the upstream user ID after the upstream issuer name
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+",
|
||||
wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamOIDC,
|
||||
// the ID token Username should include the upstream user ID after the upstream issuer name
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+" },
|
||||
wantDownstreamIDTokenAdditionalClaims: wantGroupsInAdditionalClaimsIfGroupsExist(map[string]interface{}{
|
||||
@ -472,12 +511,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
fositeSessionData := pinnipedSession.Fosite
|
||||
fositeSessionData.Claims.Subject = "not-right"
|
||||
},
|
||||
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
|
||||
"ldaps://"+env.SupervisorUpstreamLDAP.Host+
|
||||
"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+
|
||||
"&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)),
|
||||
) + "$",
|
||||
wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamLDAP,
|
||||
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
|
||||
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$"
|
||||
@ -504,12 +538,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
false,
|
||||
)
|
||||
},
|
||||
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
|
||||
"ldaps://"+env.SupervisorUpstreamLDAP.Host+
|
||||
"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+
|
||||
"&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)),
|
||||
) + "$",
|
||||
wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamLDAP,
|
||||
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
|
||||
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$"
|
||||
@ -534,12 +563,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
false,
|
||||
)
|
||||
},
|
||||
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
|
||||
"ldaps://"+env.SupervisorUpstreamLDAP.Host+
|
||||
"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+
|
||||
"&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)),
|
||||
) + "$",
|
||||
wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamLDAP,
|
||||
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
|
||||
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$"
|
||||
@ -563,7 +587,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
downstreamScopes: []string{"openid", "pinniped:request-audience", "offline_access"},
|
||||
wantDownstreamScopes: []string{"openid", "pinniped:request-audience", "offline_access", "username", "groups"},
|
||||
requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowOIDC,
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+",
|
||||
wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamOIDC,
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Username) + "$" },
|
||||
wantDownstreamIDTokenGroups: env.SupervisorUpstreamOIDC.ExpectedGroups,
|
||||
},
|
||||
@ -580,12 +604,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
env.SupervisorUpstreamLDAP.TestUserPassword // password to present to server during login
|
||||
},
|
||||
requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowLDAP,
|
||||
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
|
||||
"ldaps://"+env.SupervisorUpstreamLDAP.Host+
|
||||
"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+
|
||||
"&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)),
|
||||
) + "$",
|
||||
wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamLDAP,
|
||||
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
|
||||
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$"
|
||||
@ -635,12 +654,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
env.SupervisorUpstreamLDAP.TestUserPassword // password to present to server during login
|
||||
},
|
||||
requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowLDAPWithBadCredentialsAndThenGoodCredentials,
|
||||
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
|
||||
"ldaps://"+env.SupervisorUpstreamLDAP.Host+
|
||||
"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+
|
||||
"&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)),
|
||||
) + "$",
|
||||
wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamLDAP,
|
||||
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
|
||||
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$"
|
||||
@ -684,12 +698,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
fositeSessionData := pinnipedSession.Fosite
|
||||
fositeSessionData.Claims.Subject = "not-right"
|
||||
},
|
||||
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
|
||||
"ldaps://"+env.SupervisorUpstreamLDAP.Host+
|
||||
"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+
|
||||
"&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)),
|
||||
) + "$",
|
||||
wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamLDAP,
|
||||
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
|
||||
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$"
|
||||
@ -739,12 +748,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
fositeSessionData := pinnipedSession.Fosite
|
||||
fositeSessionData.Claims.Subject = "not-right"
|
||||
},
|
||||
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
|
||||
"ldaps://"+env.SupervisorUpstreamLDAP.Host+
|
||||
"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+
|
||||
"&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)),
|
||||
) + "$",
|
||||
wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamLDAP,
|
||||
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
|
||||
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$"
|
||||
@ -778,12 +782,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
require.NotEmpty(t, customSessionData.LDAP.UserDN)
|
||||
customSessionData.Username = "not-the-same"
|
||||
},
|
||||
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
|
||||
"ldaps://"+env.SupervisorUpstreamLDAP.StartTLSOnlyHost+
|
||||
"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+
|
||||
"&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)),
|
||||
) + "$",
|
||||
wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamLDAPStartTLSHost,
|
||||
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserDN) + "$" },
|
||||
wantDownstreamIDTokenGroups: env.SupervisorUpstreamLDAP.TestUserDirectGroupsCNs,
|
||||
@ -852,12 +851,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
require.NotEmpty(t, customSessionData.LDAP.UserDN)
|
||||
customSessionData.LDAP.UserDN = "cn=not-a-user,dc=pinniped,dc=dev"
|
||||
},
|
||||
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
|
||||
"ldaps://"+env.SupervisorUpstreamLDAP.Host+
|
||||
"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+
|
||||
"&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)),
|
||||
) + "$",
|
||||
wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamLDAP,
|
||||
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
|
||||
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$"
|
||||
@ -923,12 +917,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
require.NotEmpty(t, customSessionData.LDAP.UserDN)
|
||||
customSessionData.LDAP.UserDN = "cn=not-a-user,dc=pinniped,dc=dev"
|
||||
},
|
||||
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
|
||||
"ldaps://"+env.SupervisorUpstreamLDAP.Host+
|
||||
"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+
|
||||
"&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)),
|
||||
) + "$",
|
||||
wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamLDAP,
|
||||
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
|
||||
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$"
|
||||
@ -957,12 +946,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
require.NotEmpty(t, customSessionData.ActiveDirectory.UserDN)
|
||||
customSessionData.Username = "not-the-same"
|
||||
},
|
||||
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
|
||||
"ldaps://"+env.SupervisorUpstreamActiveDirectory.Host+
|
||||
"?base="+url.QueryEscape(env.SupervisorUpstreamActiveDirectory.DefaultNamingContextSearchBase)+
|
||||
"&sub="+env.SupervisorUpstreamActiveDirectory.TestUserUniqueIDAttributeValue,
|
||||
) + "$",
|
||||
wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamAD,
|
||||
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
|
||||
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamActiveDirectory.TestUserPrincipalNameValue) + "$"
|
||||
@ -1007,12 +991,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
fositeSessionData := pinnipedSession.Fosite
|
||||
fositeSessionData.Claims.Subject = "not-right"
|
||||
},
|
||||
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
|
||||
"ldaps://"+env.SupervisorUpstreamActiveDirectory.Host+
|
||||
"?base="+url.QueryEscape(env.SupervisorUpstreamActiveDirectory.UserSearchBase)+
|
||||
"&sub="+env.SupervisorUpstreamActiveDirectory.TestUserUniqueIDAttributeValue,
|
||||
) + "$",
|
||||
wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamADWithCustomUserSearchBase,
|
||||
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
|
||||
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamActiveDirectory.TestUserMailAttributeValue) + "$"
|
||||
@ -1055,12 +1034,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
false,
|
||||
)
|
||||
},
|
||||
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
|
||||
"ldaps://"+env.SupervisorUpstreamActiveDirectory.Host+
|
||||
"?base="+url.QueryEscape(env.SupervisorUpstreamActiveDirectory.UserSearchBase)+
|
||||
"&sub="+env.SupervisorUpstreamActiveDirectory.TestUserUniqueIDAttributeValue,
|
||||
) + "$",
|
||||
wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamADWithCustomUserSearchBase,
|
||||
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
|
||||
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamActiveDirectory.TestUserMailAttributeValue) + "$"
|
||||
@ -1112,12 +1086,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
require.NotEmpty(t, customSessionData.ActiveDirectory.UserDN)
|
||||
customSessionData.ActiveDirectory.UserDN = "cn=not-a-user,dc=pinniped,dc=dev"
|
||||
},
|
||||
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
|
||||
"ldaps://"+env.SupervisorUpstreamActiveDirectory.Host+
|
||||
"?base="+url.QueryEscape(env.SupervisorUpstreamActiveDirectory.DefaultNamingContextSearchBase)+
|
||||
"&sub="+env.SupervisorUpstreamActiveDirectory.TestUserUniqueIDAttributeValue,
|
||||
) + "$",
|
||||
wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamAD,
|
||||
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
|
||||
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamActiveDirectory.TestUserPrincipalNameValue) + "$"
|
||||
@ -1184,12 +1153,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
require.NotEmpty(t, customSessionData.ActiveDirectory.UserDN)
|
||||
customSessionData.ActiveDirectory.UserDN = "cn=not-a-user,dc=pinniped,dc=dev"
|
||||
},
|
||||
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
|
||||
"ldaps://"+env.SupervisorUpstreamActiveDirectory.Host+
|
||||
"?base="+url.QueryEscape(env.SupervisorUpstreamActiveDirectory.DefaultNamingContextSearchBase)+
|
||||
"&sub="+env.SupervisorUpstreamActiveDirectory.TestUserUniqueIDAttributeValue,
|
||||
) + "$",
|
||||
wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamAD,
|
||||
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
|
||||
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamActiveDirectory.TestUserPrincipalNameValue) + "$"
|
||||
@ -1218,8 +1182,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
breakRefreshSessionData: func(t *testing.T, sessionData *psession.PinnipedSession, _, username string) {
|
||||
testlib.ChangeADTestUserPassword(t, env, username)
|
||||
},
|
||||
// we can't know the subject ahead of time because we created a new user and don't know their uid,
|
||||
// so skip wantDownstreamIDTokenSubjectToMatch
|
||||
wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamADWithUnkownUID,
|
||||
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
|
||||
wantDownstreamIDTokenUsernameToMatch: func(username string) string {
|
||||
return "^" + regexp.QuoteMeta(username+"@"+env.SupervisorUpstreamActiveDirectory.Domain) + "$"
|
||||
@ -1248,8 +1211,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
breakRefreshSessionData: func(t *testing.T, sessionData *psession.PinnipedSession, _, username string) {
|
||||
testlib.DeactivateADTestUser(t, env, username)
|
||||
},
|
||||
// we can't know the subject ahead of time because we created a new user and don't know their uid,
|
||||
// so skip wantDownstreamIDTokenSubjectToMatch
|
||||
wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamADWithUnkownUID,
|
||||
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
|
||||
wantDownstreamIDTokenUsernameToMatch: func(username string) string {
|
||||
return "^" + regexp.QuoteMeta(username+"@"+env.SupervisorUpstreamActiveDirectory.Domain) + "$"
|
||||
@ -1278,8 +1240,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
breakRefreshSessionData: func(t *testing.T, sessionData *psession.PinnipedSession, _, username string) {
|
||||
testlib.LockADTestUser(t, env, username)
|
||||
},
|
||||
// we can't know the subject ahead of time because we created a new user and don't know their uid,
|
||||
// so skip wantDownstreamIDTokenSubjectToMatch
|
||||
wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamADWithUnkownUID,
|
||||
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
|
||||
wantDownstreamIDTokenUsernameToMatch: func(username string) string {
|
||||
return "^" + regexp.QuoteMeta(username+"@"+env.SupervisorUpstreamActiveDirectory.Domain) + "$"
|
||||
@ -1337,12 +1298,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
time.Sleep(10 * time.Second) // wait for controllers to pick up the change
|
||||
},
|
||||
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
|
||||
"ldaps://"+env.SupervisorUpstreamLDAP.Host+
|
||||
"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+
|
||||
"&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)),
|
||||
) + "$",
|
||||
wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamLDAP,
|
||||
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
|
||||
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$"
|
||||
@ -1381,12 +1337,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
time.Sleep(10 * time.Second) // wait for controllers to pick up the change
|
||||
return []string{}
|
||||
},
|
||||
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
|
||||
"ldaps://"+env.SupervisorUpstreamLDAP.Host+
|
||||
"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+
|
||||
"&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)),
|
||||
) + "$",
|
||||
wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamLDAP,
|
||||
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
|
||||
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$"
|
||||
@ -1401,8 +1352,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
},
|
||||
requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowOIDC,
|
||||
requestTokenExchangeAud: "contains-disallowed-substring.pinniped.dev-something", // .pinniped.dev substring is not allowed
|
||||
// the ID token Subject should include the upstream user ID after the upstream issuer name
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+",
|
||||
wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamOIDC,
|
||||
// the ID token Username should include the upstream user ID after the upstream issuer name
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+" },
|
||||
wantTokenExchangeResponse: func(t *testing.T, status int, body string) {
|
||||
@ -1422,8 +1372,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
},
|
||||
requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowOIDC,
|
||||
requestTokenExchangeAud: "client.oauth.pinniped.dev-client-name", // OIDC dynamic client name is not allowed
|
||||
// the ID token Subject should include the upstream user ID after the upstream issuer name
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+",
|
||||
wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamOIDC,
|
||||
// the ID token Username should include the upstream user ID after the upstream issuer name
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+" },
|
||||
wantTokenExchangeResponse: func(t *testing.T, status int, body string) {
|
||||
@ -1443,8 +1392,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
},
|
||||
requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowOIDC,
|
||||
requestTokenExchangeAud: "pinniped-cli", // pinniped-cli is not allowed
|
||||
// the ID token Subject should include the upstream user ID after the upstream issuer name
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+",
|
||||
wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamOIDC,
|
||||
// the ID token Username should include the upstream user ID after the upstream issuer name
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+" },
|
||||
wantTokenExchangeResponse: func(t *testing.T, status int, body string) {
|
||||
@ -1478,8 +1426,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
}, configv1alpha1.OIDCClientPhaseReady)
|
||||
},
|
||||
requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowOIDC,
|
||||
// the ID token Subject should include the upstream user ID after the upstream issuer name
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+",
|
||||
wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamOIDC,
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Username) + "$" },
|
||||
wantDownstreamIDTokenGroups: env.SupervisorUpstreamOIDC.ExpectedGroups,
|
||||
},
|
||||
@ -1511,8 +1458,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
}, configv1alpha1.OIDCClientPhaseReady)
|
||||
},
|
||||
requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowOIDC,
|
||||
// the ID token Subject should include the upstream user ID after the upstream issuer name
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+",
|
||||
wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamOIDC,
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string { return "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Username) + "$" },
|
||||
wantDownstreamIDTokenGroups: env.SupervisorUpstreamOIDC.ExpectedGroups,
|
||||
wantDownstreamIDTokenAdditionalClaims: wantGroupsInAdditionalClaimsIfGroupsExist(map[string]interface{}{
|
||||
@ -1540,12 +1486,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
env.SupervisorUpstreamLDAP.TestUserPassword // password to present to server during login
|
||||
},
|
||||
requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowLDAP,
|
||||
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
|
||||
"ldaps://"+env.SupervisorUpstreamLDAP.Host+
|
||||
"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+
|
||||
"&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)),
|
||||
) + "$",
|
||||
wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamLDAP,
|
||||
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
|
||||
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$"
|
||||
@ -1693,12 +1634,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
},
|
||||
downstreamScopes: []string{"openid", "pinniped:request-audience", "offline_access", "username"}, // do not request (or expect) groups
|
||||
requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowLDAP,
|
||||
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
|
||||
"ldaps://"+env.SupervisorUpstreamLDAP.Host+
|
||||
"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+
|
||||
"&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)),
|
||||
) + "$",
|
||||
wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamLDAP,
|
||||
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
|
||||
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$"
|
||||
@ -1726,12 +1662,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
},
|
||||
downstreamScopes: []string{"openid", "pinniped:request-audience", "offline_access", "groups"}, // do not request (or expect) username
|
||||
requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowLDAP,
|
||||
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
|
||||
"ldaps://"+env.SupervisorUpstreamLDAP.Host+
|
||||
"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+
|
||||
"&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)),
|
||||
) + "$",
|
||||
wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamLDAP,
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
|
||||
return "" // username should not exist as a claim since we did not request it
|
||||
},
|
||||
@ -1765,12 +1696,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
},
|
||||
downstreamScopes: []string{"openid", "offline_access"}, // do not request (or expect) pinniped:request-audience or username or groups
|
||||
requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowLDAP,
|
||||
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
|
||||
"ldaps://"+env.SupervisorUpstreamLDAP.Host+
|
||||
"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+
|
||||
"&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)),
|
||||
) + "$",
|
||||
wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamLDAP,
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
|
||||
return "" // username should not exist as a claim since we did not request it
|
||||
},
|
||||
@ -1803,12 +1729,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
env.SupervisorUpstreamActiveDirectory.TestUserPassword // password to present to server during login
|
||||
},
|
||||
requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowLDAP,
|
||||
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
|
||||
"ldaps://"+env.SupervisorUpstreamActiveDirectory.Host+
|
||||
"?base="+url.QueryEscape(env.SupervisorUpstreamActiveDirectory.DefaultNamingContextSearchBase)+
|
||||
"&sub="+env.SupervisorUpstreamActiveDirectory.TestUserUniqueIDAttributeValue,
|
||||
) + "$",
|
||||
wantDownstreamIDTokenSubjectToMatch: expectedIDTokenSubjectRegexForUpstreamAD,
|
||||
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
|
||||
return "^" + regexp.QuoteMeta(env.SupervisorUpstreamActiveDirectory.TestUserPrincipalNameValue) + "$"
|
||||
@ -1879,7 +1800,11 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
}, displayName
|
||||
},
|
||||
requestAuthorization: requestAuthorizationUsingBrowserAuthcodeFlowOIDC,
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+",
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" +
|
||||
regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer) +
|
||||
regexp.QuoteMeta("?idpName="+url.QueryEscape("my oidc idp")) +
|
||||
regexp.QuoteMeta("&sub=") + ".+" +
|
||||
"$",
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
|
||||
return "^" + regexp.QuoteMeta("username-prefix:"+env.SupervisorUpstreamOIDC.Username) + "$"
|
||||
},
|
||||
@ -1958,12 +1883,11 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
// compared to what they chose during the initial login, by changing what they decided during the initial login.
|
||||
customSessionData.Username = "some-different-downstream-username"
|
||||
},
|
||||
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
|
||||
"ldaps://"+env.SupervisorUpstreamLDAP.Host+
|
||||
"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+
|
||||
"&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)),
|
||||
) + "$",
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" +
|
||||
regexp.QuoteMeta("ldaps://"+env.SupervisorUpstreamLDAP.Host+"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)) +
|
||||
regexp.QuoteMeta("&idpName="+url.QueryEscape("my ldap idp")) +
|
||||
regexp.QuoteMeta("&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue))) +
|
||||
"$",
|
||||
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute and then transformed
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
|
||||
return "^" + regexp.QuoteMeta("username-prefix:"+env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$"
|
||||
@ -2023,12 +1947,11 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
// compared to what they chose during the initial login, by changing what they decided during the initial login.
|
||||
customSessionData.Username = "some-different-downstream-username"
|
||||
},
|
||||
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
|
||||
"ldaps://"+env.SupervisorUpstreamLDAP.Host+
|
||||
"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)+
|
||||
"&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue)),
|
||||
) + "$",
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" +
|
||||
regexp.QuoteMeta("ldaps://"+env.SupervisorUpstreamLDAP.Host+"?base="+url.QueryEscape(env.SupervisorUpstreamLDAP.UserSearchBase)) +
|
||||
regexp.QuoteMeta("&idpName="+url.QueryEscape("my ldap idp")) +
|
||||
regexp.QuoteMeta("&sub="+base64.RawURLEncoding.EncodeToString([]byte(env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue))) +
|
||||
"$",
|
||||
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute and then transformed
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
|
||||
return "^" + regexp.QuoteMeta("username-prefix:"+env.SupervisorUpstreamLDAP.TestUserMailAttributeValue) + "$"
|
||||
@ -2093,12 +2016,11 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
// compared to what they chose during the initial login, by changing what they decided during the initial login.
|
||||
customSessionData.Username = "some-different-downstream-username"
|
||||
},
|
||||
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
|
||||
"ldaps://"+env.SupervisorUpstreamActiveDirectory.Host+
|
||||
"?base="+url.QueryEscape(env.SupervisorUpstreamActiveDirectory.DefaultNamingContextSearchBase)+
|
||||
"&sub="+env.SupervisorUpstreamActiveDirectory.TestUserUniqueIDAttributeValue,
|
||||
) + "$",
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" +
|
||||
regexp.QuoteMeta("ldaps://"+env.SupervisorUpstreamActiveDirectory.Host+"?base="+url.QueryEscape(env.SupervisorUpstreamActiveDirectory.DefaultNamingContextSearchBase)) +
|
||||
regexp.QuoteMeta("&idpName="+url.QueryEscape("my ad idp")) +
|
||||
regexp.QuoteMeta("&sub="+env.SupervisorUpstreamActiveDirectory.TestUserUniqueIDAttributeValue) +
|
||||
"$",
|
||||
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
|
||||
return "^" + regexp.QuoteMeta("username-prefix:"+env.SupervisorUpstreamActiveDirectory.TestUserPrincipalNameValue) + "$"
|
||||
@ -2160,12 +2082,11 @@ func TestSupervisorLogin_Browser(t *testing.T) {
|
||||
// compared to what they chose during the initial login, by changing what they decided during the initial login.
|
||||
customSessionData.Username = "some-different-downstream-username"
|
||||
},
|
||||
// the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" + regexp.QuoteMeta(
|
||||
"ldaps://"+env.SupervisorUpstreamActiveDirectory.Host+
|
||||
"?base="+url.QueryEscape(env.SupervisorUpstreamActiveDirectory.DefaultNamingContextSearchBase)+
|
||||
"&sub="+env.SupervisorUpstreamActiveDirectory.TestUserUniqueIDAttributeValue,
|
||||
) + "$",
|
||||
wantDownstreamIDTokenSubjectToMatch: "^" +
|
||||
regexp.QuoteMeta("ldaps://"+env.SupervisorUpstreamActiveDirectory.Host+"?base="+url.QueryEscape(env.SupervisorUpstreamActiveDirectory.DefaultNamingContextSearchBase)) +
|
||||
regexp.QuoteMeta("&idpName="+url.QueryEscape("my ad idp")) +
|
||||
regexp.QuoteMeta("&sub="+env.SupervisorUpstreamActiveDirectory.TestUserUniqueIDAttributeValue) +
|
||||
"$",
|
||||
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
|
||||
wantDownstreamIDTokenUsernameToMatch: func(_ string) string {
|
||||
return "^" + regexp.QuoteMeta("username-prefix:"+env.SupervisorUpstreamActiveDirectory.TestUserPrincipalNameValue) + "$"
|
||||
|
Loading…
Reference in New Issue
Block a user