Skip to content
This repository has been archived by the owner on Dec 7, 2020. It is now read-only.

Commit

Permalink
Merge pull request #389 from gambol99/any_roles
Browse files Browse the repository at this point in the history
 Require Any Roles
  • Loading branch information
gambol99 authored Jul 3, 2018
2 parents ff204ed + 48e5faa commit d959f29
Show file tree
Hide file tree
Showing 7 changed files with 55 additions and 2 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@

#### **2.2.3 (Unreleased)**

FEATURES:
* Added the ability to use a "any" operation on the roles rather then just "and" with the inclusion of a `require-any-role` [#PR389](https://github.com/gambol99/keycloak-proxy/pull/389)

#### **2.2.2**

FEATURES:
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ resources:
roles:
- client:test1
- client:test2
require-any-role: true
groups:
- admins
- users
Expand Down Expand Up @@ -253,7 +254,9 @@ bin/keycloak-proxy \
--resources="uri=/public/*|white-listed=true"
```
Note from release 2.2.0 the `--enable-default-deny` is true by default and should explicityly allow what you want through.
Note from release 2.2.0 the `--enable-default-deny` is true by default and should explicitly allow what you want through.
By default the roles defined on a resource perform a logical `AND` so all roles specified must be present in the claims, this behavior can be altered by the `require-any-role` option however so as long as one role is present the permission is granted.
#### **HTTP Routing**
Expand Down
2 changes: 2 additions & 0 deletions doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ type Resource struct {
Methods []string `json:"methods" yaml:"methods"`
// WhiteListed permits the prefix through
WhiteListed bool `json:"white-listed" yaml:"white-listed"`
// RequireAnyRole indicates that ANY of the roles are required, the default is all
RequireAnyRole bool `json:"require-any-role" yaml:"require-any-role"`
// Roles the roles required to access this url
Roles []string `json:"roles" yaml:"roles"`
// Groups is a list of groups the user is in
Expand Down
2 changes: 1 addition & 1 deletion middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ func (r *oauthProxy) admissionMiddleware(resource *Resource) func(http.Handler)
user := scope.Identity

// @step: we need to check the roles
if !hasAccess(resource.Roles, user.roles, true) {
if !hasAccess(resource.Roles, user.roles, !resource.RequireAnyRole) {
r.log.Warn("access denied, invalid roles",
zap.String("access", "denied"),
zap.String("email", user.email),
Expand Down
32 changes: 32 additions & 0 deletions middleware_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,38 @@ func TestWhiteListedRequests(t *testing.T) {
newFakeProxy(cfg).RunTests(t, requests)
}

func TestRequireAnyRoles(t *testing.T) {
cfg := newFakeKeycloakConfig()
cfg.Resources = []*Resource{
{
URL: "/require_any_role/*",
Methods: allHTTPMethods,
RequireAnyRole: true,
Roles: []string{"admin", "guest"},
},
}
requests := []fakeRequest{
{
URI: "/require_any_role/test",
ExpectedCode: http.StatusUnauthorized,
},
{
URI: "/require_any_role/test",
HasToken: true,
Roles: []string{"guest"},
ExpectedCode: http.StatusOK,
ExpectedProxy: true,
},
{
URI: "/require_any_role/test",
HasToken: true,
Roles: []string{"guest1"},
ExpectedCode: http.StatusForbidden,
},
}
newFakeProxy(cfg).RunTests(t, requests)
}

func TestGroupPermissionsMiddleware(t *testing.T) {
cfg := newFakeKeycloakConfig()
cfg.Resources = []*Resource{
Expand Down
6 changes: 6 additions & 0 deletions resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ func (r *Resource) parse(resource string) (*Resource, error) {
r.Methods = allHTTPMethods
}
}
case "require-any-role":
v, err := strconv.ParseBool(kp[1])
if err != nil {
return nil, err
}
r.RequireAnyRole = v
case "roles":
r.Roles = strings.Split(kp[1], ",")
case "groups":
Expand Down
5 changes: 5 additions & 0 deletions resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ func TestDecodeResourceBad(t *testing.T) {
{Option: "uri"},
{Option: "uri=hello"},
{Option: "uri=/|white-listed=ERROR"},
{Option: "uri=/|require-any-role=BAD"},
}
for i, c := range cs {
if _, err := newResource().parse(c.Option); err == nil {
Expand Down Expand Up @@ -79,6 +80,10 @@ func TestResourceParseOk(t *testing.T) {
Option: "uri=/*|groups=admin",
Resource: &Resource{URL: "/*", Methods: allHTTPMethods, Groups: []string{"admin"}},
},
{
Option: "uri=/*|require-any-role=true",
Resource: &Resource{URL: "/*", Methods: allHTTPMethods, RequireAnyRole: true},
},
}
for i, x := range cs {
r, err := newResource().parse(x.Option)
Expand Down

0 comments on commit d959f29

Please sign in to comment.