mirror of
https://github.com/oauth2-proxy/oauth2-proxy.git
synced 2025-05-15 22:16:45 +02:00
Merge pull request #953 from grnhse/keycloak-refactor-provider-methods
Refactor Keycloak Provider Methods
This commit is contained in:
commit
91b3f5973e
@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
## Important Notes
|
## Important Notes
|
||||||
|
|
||||||
|
- [#953](https://github.com/oauth2-proxy/oauth2-proxy/pull/953) Keycloak will now use `--profile-url` if set for the userinfo endpoint
|
||||||
|
instead of `--validate-url`. `--validate-url` will still work for backwards compatibility.
|
||||||
- [#936](https://github.com/oauth2-proxy/oauth2-proxy/pull/936) `--user-id-claim` option is deprecated and replaced by `--oidc-email-claim`
|
- [#936](https://github.com/oauth2-proxy/oauth2-proxy/pull/936) `--user-id-claim` option is deprecated and replaced by `--oidc-email-claim`
|
||||||
- [#630](https://github.com/oauth2-proxy/oauth2-proxy/pull/630) Gitlab projects needs a Gitlab application with the extra `read_api` enabled
|
- [#630](https://github.com/oauth2-proxy/oauth2-proxy/pull/630) Gitlab projects needs a Gitlab application with the extra `read_api` enabled
|
||||||
- [#849](https://github.com/oauth2-proxy/oauth2-proxy/pull/849) `/oauth2/auth` `allowed_groups` querystring parameter can be paired with the `allowed-groups` configuration option.
|
- [#849](https://github.com/oauth2-proxy/oauth2-proxy/pull/849) `/oauth2/auth` `allowed_groups` querystring parameter can be paired with the `allowed-groups` configuration option.
|
||||||
@ -33,6 +35,8 @@
|
|||||||
|
|
||||||
## Breaking Changes
|
## Breaking Changes
|
||||||
|
|
||||||
|
- [#953](https://github.com/oauth2-proxy/oauth2-proxy/pull/953) In config files & envvar configs, `keycloak_group` is now the plural `keycloak_groups`.
|
||||||
|
Flag configs are still `--keycloak-group` but it can be passed multiple times.
|
||||||
- [#911](https://github.com/oauth2-proxy/oauth2-proxy/pull/911) Specifying a non-existent provider will cause OAuth2-Proxy to fail on startup instead of defaulting to "google".
|
- [#911](https://github.com/oauth2-proxy/oauth2-proxy/pull/911) Specifying a non-existent provider will cause OAuth2-Proxy to fail on startup instead of defaulting to "google".
|
||||||
- [#797](https://github.com/oauth2-proxy/oauth2-proxy/pull/797) Security changes to Google provider group authorization flow
|
- [#797](https://github.com/oauth2-proxy/oauth2-proxy/pull/797) Security changes to Google provider group authorization flow
|
||||||
- If you change the list of allowed groups, existing sessions that now don't have a valid group will be logged out immediately.
|
- If you change the list of allowed groups, existing sessions that now don't have a valid group will be logged out immediately.
|
||||||
@ -54,6 +58,7 @@
|
|||||||
|
|
||||||
## Changes since v6.1.1
|
## Changes since v6.1.1
|
||||||
|
|
||||||
|
- [#953](https://github.com/oauth2-proxy/oauth2-proxy/pull/953) Migrate Keycloak to EnrichSession & support multiple groups for authorization (@NickMeves)
|
||||||
- [#630](https://github.com/oauth2-proxy/oauth2-proxy/pull/630) Add support for Gitlab project based authentication (@factorysh)
|
- [#630](https://github.com/oauth2-proxy/oauth2-proxy/pull/630) Add support for Gitlab project based authentication (@factorysh)
|
||||||
- [#907](https://github.com/oauth2-proxy/oauth2-proxy/pull/907) Introduce alpha configuration option to enable testing of structured configuration (@JoelSpeed)
|
- [#907](https://github.com/oauth2-proxy/oauth2-proxy/pull/907) Introduce alpha configuration option to enable testing of structured configuration (@JoelSpeed)
|
||||||
- [#938](https://github.com/oauth2-proxy/oauth2-proxy/pull/938) Cleanup missed provider renaming refactor methods (@NickMeves)
|
- [#938](https://github.com/oauth2-proxy/oauth2-proxy/pull/938) Cleanup missed provider renaming refactor methods (@NickMeves)
|
||||||
|
@ -135,15 +135,25 @@ If you are using GitHub enterprise, make sure you set the following to the appro
|
|||||||
|
|
||||||
Make sure you set the following to the appropriate url:
|
Make sure you set the following to the appropriate url:
|
||||||
|
|
||||||
-provider=keycloak
|
--provider=keycloak
|
||||||
-client-id=<client you have created>
|
--client-id=<client you have created>
|
||||||
-client-secret=<your client's secret>
|
--client-secret=<your client's secret>
|
||||||
-login-url="http(s)://<keycloak host>/auth/realms/<your realm>/protocol/openid-connect/auth"
|
--login-url="http(s)://<keycloak host>/auth/realms/<your realm>/protocol/openid-connect/auth"
|
||||||
-redeem-url="http(s)://<keycloak host>/auth/realms/<your realm>/protocol/openid-connect/token"
|
--redeem-url="http(s)://<keycloak host>/auth/realms/<your realm>/protocol/openid-connect/token"
|
||||||
-validate-url="http(s)://<keycloak host>/auth/realms/<your realm>/protocol/openid-connect/userinfo"
|
--profile-url="http(s)://<keycloak host>/auth/realms/<your realm>/protocol/openid-connect/userinfo"
|
||||||
-keycloak-group=<user_group>
|
--validate-url="http(s)://<keycloak host>/auth/realms/<your realm>/protocol/openid-connect/userinfo"
|
||||||
|
--keycloak-group=<first_allowed_user_group>
|
||||||
|
--keycloak-group=<second_allowed_user_group>
|
||||||
|
|
||||||
The group management in keycloak is using a tree. If you create a group named admin in keycloak you should define the 'keycloak-group' value to /admin.
|
For group based authorization, the optional `--keycloak-group` (legacy) or `--allowed-group` (global standard)
|
||||||
|
flags can be used to specify which groups to limit access to.
|
||||||
|
|
||||||
|
If these are unset but a `groups` mapper is set up above in step (3), the provider will still
|
||||||
|
populate the `X-Forwarded-Groups` header to your upstream server with the `groups` data in the
|
||||||
|
Keycloak userinfo endpoint response.
|
||||||
|
|
||||||
|
The group management in keycloak is using a tree. If you create a group named admin in keycloak
|
||||||
|
you should define the 'keycloak-group' value to /admin.
|
||||||
|
|
||||||
### GitLab Auth Provider
|
### GitLab Auth Provider
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ type Options struct {
|
|||||||
TLSKeyFile string `flag:"tls-key-file" cfg:"tls_key_file"`
|
TLSKeyFile string `flag:"tls-key-file" cfg:"tls_key_file"`
|
||||||
|
|
||||||
AuthenticatedEmailsFile string `flag:"authenticated-emails-file" cfg:"authenticated_emails_file"`
|
AuthenticatedEmailsFile string `flag:"authenticated-emails-file" cfg:"authenticated_emails_file"`
|
||||||
KeycloakGroup string `flag:"keycloak-group" cfg:"keycloak_group"`
|
KeycloakGroups []string `flag:"keycloak-group" cfg:"keycloak_groups"`
|
||||||
AzureTenant string `flag:"azure-tenant" cfg:"azure_tenant"`
|
AzureTenant string `flag:"azure-tenant" cfg:"azure_tenant"`
|
||||||
BitbucketTeam string `flag:"bitbucket-team" cfg:"bitbucket_team"`
|
BitbucketTeam string `flag:"bitbucket-team" cfg:"bitbucket_team"`
|
||||||
BitbucketRepository string `flag:"bitbucket-repository" cfg:"bitbucket_repository"`
|
BitbucketRepository string `flag:"bitbucket-repository" cfg:"bitbucket_repository"`
|
||||||
@ -181,7 +181,7 @@ func NewFlagSet() *pflag.FlagSet {
|
|||||||
|
|
||||||
flagSet.StringSlice("email-domain", []string{}, "authenticate emails with the specified domain (may be given multiple times). Use * to authenticate any email")
|
flagSet.StringSlice("email-domain", []string{}, "authenticate emails with the specified domain (may be given multiple times). Use * to authenticate any email")
|
||||||
flagSet.StringSlice("whitelist-domain", []string{}, "allowed domains for redirection after authentication. Prefix domain with a . to allow subdomains (eg .example.com)")
|
flagSet.StringSlice("whitelist-domain", []string{}, "allowed domains for redirection after authentication. Prefix domain with a . to allow subdomains (eg .example.com)")
|
||||||
flagSet.String("keycloak-group", "", "restrict login to members of this group.")
|
flagSet.StringSlice("keycloak-group", []string{}, "restrict logins to members of these groups (may be given multiple times)")
|
||||||
flagSet.String("azure-tenant", "common", "go to a tenant-specific or common (tenant-independent) endpoint.")
|
flagSet.String("azure-tenant", "common", "go to a tenant-specific or common (tenant-independent) endpoint.")
|
||||||
flagSet.String("bitbucket-team", "", "restrict logins to members of this team")
|
flagSet.String("bitbucket-team", "", "restrict logins to members of this team")
|
||||||
flagSet.String("bitbucket-repository", "", "restrict logins to user with access to this repository")
|
flagSet.String("bitbucket-repository", "", "restrict logins to user with access to this repository")
|
||||||
|
@ -263,7 +263,10 @@ func parseProviderInfo(o *options.Options, msgs []string) []string {
|
|||||||
p.SetRepo(o.GitHubRepo, o.GitHubToken)
|
p.SetRepo(o.GitHubRepo, o.GitHubToken)
|
||||||
p.SetUsers(o.GitHubUsers)
|
p.SetUsers(o.GitHubUsers)
|
||||||
case *providers.KeycloakProvider:
|
case *providers.KeycloakProvider:
|
||||||
p.SetGroup(o.KeycloakGroup)
|
// Backwards compatibility with `--keycloak-group` option
|
||||||
|
if len(o.KeycloakGroups) > 0 {
|
||||||
|
p.SetAllowedGroups(o.KeycloakGroups)
|
||||||
|
}
|
||||||
case *providers.GoogleProvider:
|
case *providers.GoogleProvider:
|
||||||
if o.GoogleServiceAccountJSON != "" {
|
if o.GoogleServiceAccountJSON != "" {
|
||||||
file, err := os.Open(o.GoogleServiceAccountJSON)
|
file, err := os.Open(o.GoogleServiceAccountJSON)
|
||||||
|
@ -2,6 +2,7 @@ package providers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
|
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
|
||||||
@ -11,7 +12,6 @@ import (
|
|||||||
|
|
||||||
type KeycloakProvider struct {
|
type KeycloakProvider struct {
|
||||||
*ProviderData
|
*ProviderData
|
||||||
Group string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Provider = (*KeycloakProvider)(nil)
|
var _ Provider = (*KeycloakProvider)(nil)
|
||||||
@ -47,6 +47,7 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// NewKeycloakProvider creates a KeyCloakProvider using the passed ProviderData
|
||||||
func NewKeycloakProvider(p *ProviderData) *KeycloakProvider {
|
func NewKeycloakProvider(p *ProviderData) *KeycloakProvider {
|
||||||
p.setProviderDefaults(providerDefaults{
|
p.setProviderDefaults(providerDefaults{
|
||||||
name: keycloakProviderName,
|
name: keycloakProviderName,
|
||||||
@ -59,41 +60,39 @@ func NewKeycloakProvider(p *ProviderData) *KeycloakProvider {
|
|||||||
return &KeycloakProvider{ProviderData: p}
|
return &KeycloakProvider{ProviderData: p}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *KeycloakProvider) SetGroup(group string) {
|
// EnrichSession uses the Keycloak userinfo endpoint to populate the session's
|
||||||
p.Group = group
|
// email and groups.
|
||||||
}
|
func (p *KeycloakProvider) EnrichSession(ctx context.Context, s *sessions.SessionState) error {
|
||||||
|
// Fallback to ValidateURL if ProfileURL not set for legacy compatibility
|
||||||
|
profileURL := p.ValidateURL.String()
|
||||||
|
if p.ProfileURL.String() != "" {
|
||||||
|
profileURL = p.ProfileURL.String()
|
||||||
|
}
|
||||||
|
|
||||||
func (p *KeycloakProvider) GetEmailAddress(ctx context.Context, s *sessions.SessionState) (string, error) {
|
json, err := requests.New(profileURL).
|
||||||
json, err := requests.New(p.ValidateURL.String()).
|
|
||||||
WithContext(ctx).
|
WithContext(ctx).
|
||||||
SetHeader("Authorization", "Bearer "+s.AccessToken).
|
SetHeader("Authorization", "Bearer "+s.AccessToken).
|
||||||
Do().
|
Do().
|
||||||
UnmarshalJSON()
|
UnmarshalJSON()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("failed making request %s", err)
|
logger.Errorf("failed making request %v", err)
|
||||||
return "", err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.Group != "" {
|
groups, err := json.Get("groups").StringArray()
|
||||||
var groups, err = json.Get("groups").Array()
|
if err == nil {
|
||||||
if err != nil {
|
for _, group := range groups {
|
||||||
logger.Printf("groups not found %s", err)
|
if group != "" {
|
||||||
return "", err
|
s.Groups = append(s.Groups, group)
|
||||||
}
|
|
||||||
|
|
||||||
var found = false
|
|
||||||
for i := range groups {
|
|
||||||
if groups[i].(string) == p.Group {
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !found {
|
|
||||||
logger.Printf("group not found, access denied")
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return json.Get("email").String()
|
email, err := json.Get("email").String()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to extract email from userinfo endpoint: %v", err)
|
||||||
|
}
|
||||||
|
s.Email = email
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -2,17 +2,24 @@ package providers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/url"
|
"net/url"
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
|
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/ginkgo/extensions/table"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func testKeycloakProvider(hostname, group string) *KeycloakProvider {
|
const (
|
||||||
|
keycloakAccessToken = "eyJKeycloak.eyJAccess.Token"
|
||||||
|
keycloakUserinfoPath = "/api/v3/user"
|
||||||
|
)
|
||||||
|
|
||||||
|
func testKeycloakProvider(backend *httptest.Server) (*KeycloakProvider, error) {
|
||||||
p := NewKeycloakProvider(
|
p := NewKeycloakProvider(
|
||||||
&ProviderData{
|
&ProviderData{
|
||||||
ProviderName: "",
|
ProviderName: "",
|
||||||
@ -22,142 +29,182 @@ func testKeycloakProvider(hostname, group string) *KeycloakProvider {
|
|||||||
ValidateURL: &url.URL{},
|
ValidateURL: &url.URL{},
|
||||||
Scope: ""})
|
Scope: ""})
|
||||||
|
|
||||||
if group != "" {
|
if backend != nil {
|
||||||
p.SetGroup(group)
|
bURL, err := url.Parse(backend.URL)
|
||||||
}
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
hostname := bURL.Host
|
||||||
|
|
||||||
if hostname != "" {
|
|
||||||
updateURL(p.Data().LoginURL, hostname)
|
updateURL(p.Data().LoginURL, hostname)
|
||||||
updateURL(p.Data().RedeemURL, hostname)
|
updateURL(p.Data().RedeemURL, hostname)
|
||||||
updateURL(p.Data().ProfileURL, hostname)
|
updateURL(p.Data().ProfileURL, hostname)
|
||||||
updateURL(p.Data().ValidateURL, hostname)
|
updateURL(p.Data().ValidateURL, hostname)
|
||||||
}
|
}
|
||||||
return p
|
|
||||||
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func testKeycloakBackend(payload string) *httptest.Server {
|
var _ = Describe("Keycloak Provider Tests", func() {
|
||||||
path := "/api/v3/user"
|
Context("New Provider Init", func() {
|
||||||
|
It("uses defaults", func() {
|
||||||
|
providerData := NewKeycloakProvider(&ProviderData{}).Data()
|
||||||
|
Expect(providerData.ProviderName).To(Equal("Keycloak"))
|
||||||
|
Expect(providerData.LoginURL.String()).To(Equal("https://keycloak.org/oauth/authorize"))
|
||||||
|
Expect(providerData.RedeemURL.String()).To(Equal("https://keycloak.org/oauth/token"))
|
||||||
|
Expect(providerData.ProfileURL.String()).To(Equal(""))
|
||||||
|
Expect(providerData.ValidateURL.String()).To(Equal("https://keycloak.org/api/v3/user"))
|
||||||
|
Expect(providerData.Scope).To(Equal("api"))
|
||||||
|
})
|
||||||
|
|
||||||
return httptest.NewServer(http.HandlerFunc(
|
It("overrides defaults", func() {
|
||||||
func(w http.ResponseWriter, r *http.Request) {
|
p := NewKeycloakProvider(
|
||||||
url := r.URL
|
&ProviderData{
|
||||||
if url.Path != path {
|
LoginURL: &url.URL{
|
||||||
w.WriteHeader(404)
|
Scheme: "https",
|
||||||
} else if !IsAuthorizedInHeader(r.Header) {
|
Host: "example.com",
|
||||||
w.WriteHeader(403)
|
Path: "/oauth/auth"},
|
||||||
} else {
|
RedeemURL: &url.URL{
|
||||||
w.WriteHeader(200)
|
Scheme: "https",
|
||||||
w.Write([]byte(payload))
|
Host: "example.com",
|
||||||
}
|
Path: "/oauth/token"},
|
||||||
}))
|
ProfileURL: &url.URL{
|
||||||
}
|
Scheme: "https",
|
||||||
|
Host: "example.com",
|
||||||
|
Path: "/api/v3/user"},
|
||||||
|
ValidateURL: &url.URL{
|
||||||
|
Scheme: "https",
|
||||||
|
Host: "example.com",
|
||||||
|
Path: "/api/v3/user"},
|
||||||
|
Scope: "profile"})
|
||||||
|
providerData := p.Data()
|
||||||
|
|
||||||
func TestKeycloakProviderDefaults(t *testing.T) {
|
Expect(providerData.ProviderName).To(Equal("Keycloak"))
|
||||||
p := testKeycloakProvider("", "")
|
Expect(providerData.LoginURL.String()).To(Equal("https://example.com/oauth/auth"))
|
||||||
assert.NotEqual(t, nil, p)
|
Expect(providerData.RedeemURL.String()).To(Equal("https://example.com/oauth/token"))
|
||||||
assert.Equal(t, "Keycloak", p.Data().ProviderName)
|
Expect(providerData.ProfileURL.String()).To(Equal("https://example.com/api/v3/user"))
|
||||||
assert.Equal(t, "https://keycloak.org/oauth/authorize",
|
Expect(providerData.ValidateURL.String()).To(Equal("https://example.com/api/v3/user"))
|
||||||
p.Data().LoginURL.String())
|
Expect(providerData.Scope).To(Equal("profile"))
|
||||||
assert.Equal(t, "https://keycloak.org/oauth/token",
|
})
|
||||||
p.Data().RedeemURL.String())
|
})
|
||||||
assert.Equal(t, "https://keycloak.org/api/v3/user",
|
|
||||||
p.Data().ValidateURL.String())
|
|
||||||
assert.Equal(t, "api", p.Data().Scope)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewKeycloakProvider(t *testing.T) {
|
Context("EnrichSession", func() {
|
||||||
g := NewWithT(t)
|
type enrichSessionTableInput struct {
|
||||||
|
backendHandler http.HandlerFunc
|
||||||
|
expectedError error
|
||||||
|
expectedEmail string
|
||||||
|
expectedGroups []string
|
||||||
|
}
|
||||||
|
|
||||||
// Test that defaults are set when calling for a new provider with nothing set
|
DescribeTable("should return expected results",
|
||||||
providerData := NewKeycloakProvider(&ProviderData{}).Data()
|
func(in enrichSessionTableInput) {
|
||||||
g.Expect(providerData.ProviderName).To(Equal("Keycloak"))
|
backend := httptest.NewServer(in.backendHandler)
|
||||||
g.Expect(providerData.LoginURL.String()).To(Equal("https://keycloak.org/oauth/authorize"))
|
p, err := testKeycloakProvider(backend)
|
||||||
g.Expect(providerData.RedeemURL.String()).To(Equal("https://keycloak.org/oauth/token"))
|
Expect(err).To(BeNil())
|
||||||
g.Expect(providerData.ProfileURL.String()).To(Equal(""))
|
|
||||||
g.Expect(providerData.ValidateURL.String()).To(Equal("https://keycloak.org/api/v3/user"))
|
|
||||||
g.Expect(providerData.Scope).To(Equal("api"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestKeycloakProviderOverrides(t *testing.T) {
|
p.ProfileURL, err = url.Parse(
|
||||||
p := NewKeycloakProvider(
|
fmt.Sprintf("%s%s", backend.URL, keycloakUserinfoPath),
|
||||||
&ProviderData{
|
)
|
||||||
LoginURL: &url.URL{
|
Expect(err).To(BeNil())
|
||||||
Scheme: "https",
|
|
||||||
Host: "example.com",
|
|
||||||
Path: "/oauth/auth"},
|
|
||||||
RedeemURL: &url.URL{
|
|
||||||
Scheme: "https",
|
|
||||||
Host: "example.com",
|
|
||||||
Path: "/oauth/token"},
|
|
||||||
ValidateURL: &url.URL{
|
|
||||||
Scheme: "https",
|
|
||||||
Host: "example.com",
|
|
||||||
Path: "/api/v3/user"},
|
|
||||||
Scope: "profile"})
|
|
||||||
assert.NotEqual(t, nil, p)
|
|
||||||
assert.Equal(t, "Keycloak", p.Data().ProviderName)
|
|
||||||
assert.Equal(t, "https://example.com/oauth/auth",
|
|
||||||
p.Data().LoginURL.String())
|
|
||||||
assert.Equal(t, "https://example.com/oauth/token",
|
|
||||||
p.Data().RedeemURL.String())
|
|
||||||
assert.Equal(t, "https://example.com/api/v3/user",
|
|
||||||
p.Data().ValidateURL.String())
|
|
||||||
assert.Equal(t, "profile", p.Data().Scope)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestKeycloakProviderGetEmailAddress(t *testing.T) {
|
session := &sessions.SessionState{AccessToken: keycloakAccessToken}
|
||||||
b := testKeycloakBackend("{\"email\": \"michael.bland@gsa.gov\"}")
|
err = p.EnrichSession(context.Background(), session)
|
||||||
defer b.Close()
|
|
||||||
|
|
||||||
bURL, _ := url.Parse(b.URL)
|
if in.expectedError != nil {
|
||||||
p := testKeycloakProvider(bURL.Host, "")
|
Expect(err).To(Equal(in.expectedError))
|
||||||
|
} else {
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
}
|
||||||
|
|
||||||
session := CreateAuthorizedSession()
|
Expect(session.Email).To(Equal(in.expectedEmail))
|
||||||
email, err := p.GetEmailAddress(context.Background(), session)
|
|
||||||
assert.Equal(t, nil, err)
|
|
||||||
assert.Equal(t, "michael.bland@gsa.gov", email)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestKeycloakProviderGetEmailAddressAndGroup(t *testing.T) {
|
if in.expectedGroups != nil {
|
||||||
b := testKeycloakBackend("{\"email\": \"michael.bland@gsa.gov\", \"groups\": [\"test-grp1\", \"test-grp2\"]}")
|
Expect(session.Groups).To(Equal(in.expectedGroups))
|
||||||
defer b.Close()
|
} else {
|
||||||
|
Expect(session.Groups).To(BeNil())
|
||||||
bURL, _ := url.Parse(b.URL)
|
}
|
||||||
p := testKeycloakProvider(bURL.Host, "test-grp1")
|
},
|
||||||
|
Entry("email and multiple groups", enrichSessionTableInput{
|
||||||
session := CreateAuthorizedSession()
|
backendHandler: func(w http.ResponseWriter, _ *http.Request) {
|
||||||
email, err := p.GetEmailAddress(context.Background(), session)
|
w.WriteHeader(200)
|
||||||
assert.Equal(t, nil, err)
|
_, err := w.Write([]byte(`
|
||||||
assert.Equal(t, "michael.bland@gsa.gov", email)
|
{
|
||||||
}
|
"email": "michael.bland@gsa.gov",
|
||||||
|
"groups": [
|
||||||
// Note that trying to trigger the "failed building request" case is not
|
"test-grp1",
|
||||||
// practical, since the only way it can fail is if the URL fails to parse.
|
"test-grp2"
|
||||||
func TestKeycloakProviderGetEmailAddressFailedRequest(t *testing.T) {
|
]
|
||||||
b := testKeycloakBackend("unused payload")
|
}
|
||||||
defer b.Close()
|
`))
|
||||||
|
if err != nil {
|
||||||
bURL, _ := url.Parse(b.URL)
|
panic(err)
|
||||||
p := testKeycloakProvider(bURL.Host, "")
|
}
|
||||||
|
},
|
||||||
// We'll trigger a request failure by using an unexpected access
|
expectedError: nil,
|
||||||
// token. Alternatively, we could allow the parsing of the payload as
|
expectedEmail: "michael.bland@gsa.gov",
|
||||||
// JSON to fail.
|
expectedGroups: []string{"test-grp1", "test-grp2"},
|
||||||
session := &sessions.SessionState{AccessToken: "unexpected_access_token"}
|
}),
|
||||||
email, err := p.GetEmailAddress(context.Background(), session)
|
Entry("email and single group", enrichSessionTableInput{
|
||||||
assert.NotEqual(t, nil, err)
|
backendHandler: func(w http.ResponseWriter, _ *http.Request) {
|
||||||
assert.Equal(t, "", email)
|
w.WriteHeader(200)
|
||||||
}
|
_, err := w.Write([]byte(`
|
||||||
|
{
|
||||||
func TestKeycloakProviderGetEmailAddressEmailNotPresentInPayload(t *testing.T) {
|
"email": "michael.bland@gsa.gov",
|
||||||
b := testKeycloakBackend("{\"foo\": \"bar\"}")
|
"groups": ["test-grp1"]
|
||||||
defer b.Close()
|
}
|
||||||
|
`))
|
||||||
bURL, _ := url.Parse(b.URL)
|
if err != nil {
|
||||||
p := testKeycloakProvider(bURL.Host, "")
|
panic(err)
|
||||||
|
}
|
||||||
session := CreateAuthorizedSession()
|
},
|
||||||
email, err := p.GetEmailAddress(context.Background(), session)
|
expectedError: nil,
|
||||||
assert.NotEqual(t, nil, err)
|
expectedEmail: "michael.bland@gsa.gov",
|
||||||
assert.Equal(t, "", email)
|
expectedGroups: []string{"test-grp1"},
|
||||||
}
|
}),
|
||||||
|
Entry("email and no groups", enrichSessionTableInput{
|
||||||
|
backendHandler: func(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
w.WriteHeader(200)
|
||||||
|
_, err := w.Write([]byte(`
|
||||||
|
{
|
||||||
|
"email": "michael.bland@gsa.gov"
|
||||||
|
}
|
||||||
|
`))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
expectedError: nil,
|
||||||
|
expectedEmail: "michael.bland@gsa.gov",
|
||||||
|
expectedGroups: nil,
|
||||||
|
}),
|
||||||
|
Entry("missing email", enrichSessionTableInput{
|
||||||
|
backendHandler: func(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
w.WriteHeader(200)
|
||||||
|
_, err := w.Write([]byte(`
|
||||||
|
{
|
||||||
|
"groups": [
|
||||||
|
"test-grp1",
|
||||||
|
"test-grp2"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
`))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
expectedError: errors.New(
|
||||||
|
"unable to extract email from userinfo endpoint: type assertion to string failed"),
|
||||||
|
expectedEmail: "",
|
||||||
|
expectedGroups: []string{"test-grp1", "test-grp2"},
|
||||||
|
}),
|
||||||
|
Entry("request failure", enrichSessionTableInput{
|
||||||
|
backendHandler: func(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
w.WriteHeader(500)
|
||||||
|
},
|
||||||
|
expectedError: errors.New(`unexpected status "500": `),
|
||||||
|
expectedEmail: "",
|
||||||
|
expectedGroups: nil,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user