mirror of
https://github.com/oauth2-proxy/oauth2-proxy.git
synced 2026-06-19 22:58:30 +02:00
Merge branch 'master' into ap-gh-pagination-with-lastpage
This commit is contained in:
+4
-3
@@ -195,9 +195,10 @@ func userInGroup(service *admin.Service, groups []string, email string) bool {
|
||||
r, err := req.Do()
|
||||
if err != nil {
|
||||
err, ok := err.(*googleapi.Error)
|
||||
if ok && err.Code == 404 {
|
||||
switch {
|
||||
case ok && err.Code == 404:
|
||||
logger.Printf("error checking membership in group %s: group does not exist", group)
|
||||
} else if ok && err.Code == 400 {
|
||||
case ok && err.Code == 400:
|
||||
// It is possible for Members.HasMember to return false even if the email is a group member.
|
||||
// One case that can cause this is if the user email is from a different domain than the group,
|
||||
// e.g. "member@otherdomain.com" in the group "group@mydomain.com" will result in a 400 error
|
||||
@@ -215,7 +216,7 @@ func userInGroup(service *admin.Service, groups []string, email string) bool {
|
||||
if r.Status == "ACTIVE" {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
default:
|
||||
logger.Printf("error checking group membership: %v", err)
|
||||
}
|
||||
continue
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
package providers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/pusher/oauth2_proxy/pkg/apis/sessions"
|
||||
"github.com/pusher/oauth2_proxy/pkg/logger"
|
||||
"github.com/pusher/oauth2_proxy/pkg/requests"
|
||||
)
|
||||
|
||||
type KeycloakProvider struct {
|
||||
*ProviderData
|
||||
Group string
|
||||
}
|
||||
|
||||
func NewKeycloakProvider(p *ProviderData) *KeycloakProvider {
|
||||
p.ProviderName = "Keycloak"
|
||||
if p.LoginURL == nil || p.LoginURL.String() == "" {
|
||||
p.LoginURL = &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "keycloak.org",
|
||||
Path: "/oauth/authorize",
|
||||
}
|
||||
}
|
||||
if p.RedeemURL == nil || p.RedeemURL.String() == "" {
|
||||
p.RedeemURL = &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "keycloak.org",
|
||||
Path: "/oauth/token",
|
||||
}
|
||||
}
|
||||
if p.ValidateURL == nil || p.ValidateURL.String() == "" {
|
||||
p.ValidateURL = &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "keycloak.org",
|
||||
Path: "/api/v3/user",
|
||||
}
|
||||
}
|
||||
if p.Scope == "" {
|
||||
p.Scope = "api"
|
||||
}
|
||||
return &KeycloakProvider{ProviderData: p}
|
||||
}
|
||||
|
||||
func (p *KeycloakProvider) SetGroup(group string) {
|
||||
p.Group = group
|
||||
}
|
||||
|
||||
func (p *KeycloakProvider) GetEmailAddress(s *sessions.SessionState) (string, error) {
|
||||
|
||||
req, err := http.NewRequest("GET", p.ValidateURL.String(), nil)
|
||||
req.Header.Set("Authorization", "Bearer "+s.AccessToken)
|
||||
if err != nil {
|
||||
logger.Printf("failed building request %s", err)
|
||||
return "", err
|
||||
}
|
||||
json, err := requests.Request(req)
|
||||
if err != nil {
|
||||
logger.Printf("failed making request %s", err)
|
||||
return "", err
|
||||
}
|
||||
|
||||
if p.Group != "" {
|
||||
var groups, err = json.Get("groups").Array()
|
||||
if err != nil {
|
||||
logger.Printf("groups not found %s", err)
|
||||
return "", err
|
||||
}
|
||||
|
||||
var found = false
|
||||
for i := range groups {
|
||||
if groups[i].(string) == p.Group {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if found != true {
|
||||
logger.Printf("group not found, access denied")
|
||||
return "", nil
|
||||
}
|
||||
}
|
||||
|
||||
return json.Get("email").String()
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
package providers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/bmizerany/assert"
|
||||
"github.com/pusher/oauth2_proxy/pkg/apis/sessions"
|
||||
)
|
||||
|
||||
const imaginaryAccessToken = "imaginary_access_token"
|
||||
const bearerAccessToken = "Bearer " + imaginaryAccessToken
|
||||
|
||||
func testKeycloakProvider(hostname, group string) *KeycloakProvider {
|
||||
p := NewKeycloakProvider(
|
||||
&ProviderData{
|
||||
ProviderName: "",
|
||||
LoginURL: &url.URL{},
|
||||
RedeemURL: &url.URL{},
|
||||
ProfileURL: &url.URL{},
|
||||
ValidateURL: &url.URL{},
|
||||
Scope: ""})
|
||||
|
||||
if group != "" {
|
||||
p.SetGroup(group)
|
||||
}
|
||||
|
||||
if hostname != "" {
|
||||
updateURL(p.Data().LoginURL, hostname)
|
||||
updateURL(p.Data().RedeemURL, hostname)
|
||||
updateURL(p.Data().ProfileURL, hostname)
|
||||
updateURL(p.Data().ValidateURL, hostname)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func testKeycloakBackend(payload string) *httptest.Server {
|
||||
path := "/api/v3/user"
|
||||
|
||||
return httptest.NewServer(http.HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
url := r.URL
|
||||
if url.Path != path {
|
||||
w.WriteHeader(404)
|
||||
} else if r.Header.Get("Authorization") != bearerAccessToken {
|
||||
w.WriteHeader(403)
|
||||
} else {
|
||||
w.WriteHeader(200)
|
||||
w.Write([]byte(payload))
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
func TestKeycloakProviderDefaults(t *testing.T) {
|
||||
p := testKeycloakProvider("", "")
|
||||
assert.NotEqual(t, nil, p)
|
||||
assert.Equal(t, "Keycloak", p.Data().ProviderName)
|
||||
assert.Equal(t, "https://keycloak.org/oauth/authorize",
|
||||
p.Data().LoginURL.String())
|
||||
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 TestKeycloakProviderOverrides(t *testing.T) {
|
||||
p := NewKeycloakProvider(
|
||||
&ProviderData{
|
||||
LoginURL: &url.URL{
|
||||
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) {
|
||||
b := testKeycloakBackend("{\"email\": \"michael.bland@gsa.gov\"}")
|
||||
defer b.Close()
|
||||
|
||||
bURL, _ := url.Parse(b.URL)
|
||||
p := testKeycloakProvider(bURL.Host, "")
|
||||
|
||||
session := &sessions.SessionState{AccessToken: imaginaryAccessToken}
|
||||
email, err := p.GetEmailAddress(session)
|
||||
assert.Equal(t, nil, err)
|
||||
assert.Equal(t, "michael.bland@gsa.gov", email)
|
||||
}
|
||||
|
||||
func TestKeycloakProviderGetEmailAddressAndGroup(t *testing.T) {
|
||||
b := testKeycloakBackend("{\"email\": \"michael.bland@gsa.gov\", \"groups\": [\"test-grp1\", \"test-grp2\"]}")
|
||||
defer b.Close()
|
||||
|
||||
bURL, _ := url.Parse(b.URL)
|
||||
p := testKeycloakProvider(bURL.Host, "test-grp1")
|
||||
|
||||
session := &sessions.SessionState{AccessToken: imaginaryAccessToken}
|
||||
email, err := p.GetEmailAddress(session)
|
||||
assert.Equal(t, nil, err)
|
||||
assert.Equal(t, "michael.bland@gsa.gov", email)
|
||||
}
|
||||
|
||||
// Note that trying to trigger the "failed building request" case is not
|
||||
// practical, since the only way it can fail is if the URL fails to parse.
|
||||
func TestKeycloakProviderGetEmailAddressFailedRequest(t *testing.T) {
|
||||
b := testKeycloakBackend("unused payload")
|
||||
defer b.Close()
|
||||
|
||||
bURL, _ := url.Parse(b.URL)
|
||||
p := testKeycloakProvider(bURL.Host, "")
|
||||
|
||||
// We'll trigger a request failure by using an unexpected access
|
||||
// token. Alternatively, we could allow the parsing of the payload as
|
||||
// JSON to fail.
|
||||
session := &sessions.SessionState{AccessToken: "unexpected_access_token"}
|
||||
email, err := p.GetEmailAddress(session)
|
||||
assert.NotEqual(t, nil, err)
|
||||
assert.Equal(t, "", email)
|
||||
}
|
||||
|
||||
func TestKeycloakProviderGetEmailAddressEmailNotPresentInPayload(t *testing.T) {
|
||||
b := testKeycloakBackend("{\"foo\": \"bar\"}")
|
||||
defer b.Close()
|
||||
|
||||
bURL, _ := url.Parse(b.URL)
|
||||
p := testKeycloakProvider(bURL.Host, "")
|
||||
|
||||
session := &sessions.SessionState{AccessToken: imaginaryAccessToken}
|
||||
email, err := p.GetEmailAddress(session)
|
||||
assert.NotEqual(t, nil, err)
|
||||
assert.Equal(t, "", email)
|
||||
}
|
||||
@@ -28,6 +28,8 @@ func New(provider string, p *ProviderData) Provider {
|
||||
return NewFacebookProvider(p)
|
||||
case "github":
|
||||
return NewGitHubProvider(p)
|
||||
case "keycloak":
|
||||
return NewKeycloakProvider(p)
|
||||
case "azure":
|
||||
return NewAzureProvider(p)
|
||||
case "gitlab":
|
||||
|
||||
Reference in New Issue
Block a user