Skip to content

Commit

Permalink
all: add ability to skip acp checks
Browse files Browse the repository at this point in the history
  • Loading branch information
arekkas authored and arekkas committed Oct 12, 2017
1 parent 13f9f81 commit 18facbb
Show file tree
Hide file tree
Showing 11 changed files with 165 additions and 58 deletions.
5 changes: 5 additions & 0 deletions docs/api.swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,11 @@
"type": "boolean",
"x-go-name": "AllowAnonymous"
},
"bypassAccessControlPolicies": {
"description": "BypassAccessControlPolicies if set true disables checking access control policies.",
"type": "boolean",
"x-go-name": "BypassAccessControlPolicies"
},
"bypassAuthorization": {
"description": "BypassAuthorization if set true disables firewall capabilities.",
"type": "boolean",
Expand Down
56 changes: 56 additions & 0 deletions evaluator/evaluator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func mustGenerateURL(t *testing.T, u string) *url.URL {
func TestEvaluator(t *testing.T) {
we := NewWardenEvaluator(nil, nil, nil)
publicRule := rule.Rule{MatchesMethods: []string{"GET"}, MatchesPath: mustCompileRegex(t, "/users/[0-9]+"), AllowAnonymous: true}
bypassACPRule := rule.Rule{MatchesMethods: []string{"GET"}, MatchesPath: mustCompileRegex(t, "/users/[0-9]+"), BypassAccessControlPolicies: true}
privateRule := rule.Rule{
MatchesMethods: []string{"POST"},
MatchesPath: mustCompileRegex(t, "/users/([0-9]+)"),
Expand Down Expand Up @@ -157,6 +158,61 @@ func TestEvaluator(t *testing.T) {
return s
},
},
{
d: "request is not allowed because it matches a rule without access control policies, but token introspection fails",
rules: []rule.Rule{bypassACPRule},
r: &http.Request{Method: "GET", Header: http.Header{"Authorization": []string{"bEaReR token"}}, URL: mustGenerateURL(t, "https://localhost/users/1234")},
e: func(t *testing.T, s *Session, err error) {
require.Error(t, err)
},
mock: func(c *gomock.Controller) hydra.SDK {
s := NewMockSDK(c)
s.EXPECT().IntrospectOAuth2Token(gomock.Eq("token"), gomock.Eq("")).Return(&swagger.OAuth2TokenIntrospection{Active: false}, &swagger.APIResponse{Response: &http.Response{StatusCode: http.StatusOK}}, nil)
return s
},
},
{
d: "request is not allowed because it matches a rule without access control policies, but token introspection fails with network error",
rules: []rule.Rule{bypassACPRule},
r: &http.Request{Method: "GET", Header: http.Header{"Authorization": []string{"bEaReR token"}}, URL: mustGenerateURL(t, "https://localhost/users/1234")},
e: func(t *testing.T, s *Session, err error) {
require.Error(t, err)
},
mock: func(c *gomock.Controller) hydra.SDK {
s := NewMockSDK(c)
s.EXPECT().IntrospectOAuth2Token(gomock.Eq("token"), gomock.Eq("")).Return(nil, nil, errors.New("Some error"))
return s
},
},
{
d: "request is not allowed because it matches a rule without access control policies, but token introspection fails with wrong status code",
rules: []rule.Rule{bypassACPRule},
r: &http.Request{Method: "GET", Header: http.Header{"Authorization": []string{"bEaReR token"}}, URL: mustGenerateURL(t, "https://localhost/users/1234")},
e: func(t *testing.T, s *Session, err error) {
require.Error(t, err)
},
mock: func(c *gomock.Controller) hydra.SDK {
s := NewMockSDK(c)
s.EXPECT().IntrospectOAuth2Token(gomock.Eq("token"), gomock.Eq("")).Return(nil, &swagger.APIResponse{Response: &http.Response{StatusCode: http.StatusUnauthorized}}, nil)
return s
},
},
{
d: "request is allowed because it matches a rule without access control policies, and token introspection succeeds",
rules: []rule.Rule{bypassACPRule},
r: &http.Request{Method: "GET", Header: http.Header{"Authorization": []string{"bEaReR token"}}, URL: mustGenerateURL(t, "https://localhost/users/1234")},
e: func(t *testing.T, s *Session, err error) {
require.NoError(t, err)
assert.Equal(t, "client", s.ClientID)
assert.Equal(t, "user", s.User)
assert.False(t, s.Anonymous)
},
mock: func(c *gomock.Controller) hydra.SDK {
s := NewMockSDK(c)
s.EXPECT().IntrospectOAuth2Token(gomock.Eq("token"), gomock.Eq("")).Return(&swagger.OAuth2TokenIntrospection{Active: true, Sub: "user", ClientId: "client"}, &swagger.APIResponse{Response: &http.Response{StatusCode: http.StatusOK}}, nil)
return s
},
},
{
d: "request is denied because token is missing and endpoint is not public",
rules: []rule.Rule{privateRule},
Expand Down
27 changes: 27 additions & 0 deletions evaluator/evaluator_warden.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/tomasen/realip"
"strings"
)

type WardenEvaluator struct {
Expand Down Expand Up @@ -76,6 +77,32 @@ func (d *WardenEvaluator) EvaluateAccessRequest(r *http.Request) (*Session, erro
return nil, errors.WithStack(helper.ErrMissingBearerToken)
}

if rl.BypassAccessControlPolicies {
introspection, response, err := d.Hydra.IntrospectOAuth2Token(token, strings.Join(rl.RequiredScopes, " "))
if err != nil {
d.Logger.WithError(err).
WithField("access_url", r.URL.String()).
WithField("token", token[:5]).
Errorf("Unable to connect to warden endpoint.")
return nil, errors.WithStack(err)
} else if response.StatusCode != http.StatusOK {
d.Logger.
WithField("status_code", response.StatusCode).
WithField("token", token[:5]).
WithField("access_url", r.URL.String()).
Errorf("Expected warden response to return status code 200.")
return nil, errors.Errorf("Token introspection expects status code %d but got %d", http.StatusOK, response.StatusCode)
} else if !introspection.Active {
return nil, errors.WithStack(helper.ErrForbidden)
}

return &Session{
User: introspection.Sub,
ClientID: introspection.ClientId,
Anonymous: false,
}, nil
}

introspection, response, err := d.Hydra.DoesWardenAllowTokenAccessRequest(d.prepareAccessRequests(r, token, rl))
if err != nil {
d.Logger.WithError(err).
Expand Down
3 changes: 3 additions & 0 deletions rule/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,7 @@ type jsonRule struct {

// BypassAuthorization if set true disables firewall capabilities.
BypassAuthorization bool `json:"bypassAuthorization"`

// BypassAccessControlPolicies if set true disables checking access control policies.
BypassAccessControlPolicies bool `json:"bypassAccessControlPolicies"`
}
38 changes: 20 additions & 18 deletions rule/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,28 +201,30 @@ func toRule(rule *jsonRule) (*Rule, error) {
}

return &Rule{
ID: rule.ID,
MatchesPath: exp,
MatchesMethods: rule.MatchesMethods,
RequiredScopes: rule.RequiredScopes,
RequiredAction: rule.RequiredAction,
RequiredResource: rule.RequiredResource,
AllowAnonymous: rule.AllowAnonymous,
BypassAuthorization: rule.BypassAuthorization,
Description: rule.Description,
ID: rule.ID,
MatchesPath: exp,
MatchesMethods: rule.MatchesMethods,
RequiredScopes: rule.RequiredScopes,
RequiredAction: rule.RequiredAction,
RequiredResource: rule.RequiredResource,
AllowAnonymous: rule.AllowAnonymous,
BypassAuthorization: rule.BypassAuthorization,
BypassAccessControlPolicies: rule.BypassAccessControlPolicies,
Description: rule.Description,
}, nil
}

func encodeRule(r *Rule) *jsonRule {
return &jsonRule{
ID: r.ID,
MatchesPath: r.MatchesPath.String(),
MatchesMethods: r.MatchesMethods,
RequiredScopes: r.RequiredScopes,
RequiredAction: r.RequiredAction,
RequiredResource: r.RequiredResource,
BypassAuthorization: r.BypassAuthorization,
AllowAnonymous: r.AllowAnonymous,
Description: r.Description,
ID: r.ID,
MatchesPath: r.MatchesPath.String(),
MatchesMethods: r.MatchesMethods,
RequiredScopes: r.RequiredScopes,
RequiredAction: r.RequiredAction,
RequiredResource: r.RequiredResource,
BypassAuthorization: r.BypassAuthorization,
BypassAccessControlPolicies: r.BypassAccessControlPolicies,
AllowAnonymous: r.AllowAnonymous,
Description: r.Description,
}
}
11 changes: 6 additions & 5 deletions rule/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,12 @@ func TestHandler(t *testing.T) {
RequiredScopes: []string{"users.create"},
}
r2 := swagger.Rule{
Description: "Get users rule",
MatchesPath: "/users/([0-9]+)",
MatchesMethods: []string{"GET"},
AllowAnonymous: true,
BypassAuthorization: true,
Description: "Get users rule",
MatchesPath: "/users/([0-9]+)",
MatchesMethods: []string{"GET"},
AllowAnonymous: true,
BypassAuthorization: true,
BypassAccessControlPolicies: true,
}

t.Run("case=create a new rule", func(t *testing.T) {
Expand Down
61 changes: 33 additions & 28 deletions rule/manager_sql.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,16 @@ import (
)

type sqlRule struct {
ID string `db:"id"`
MatchesMethods string `db:"matches_methods"`
MatchesPath string `db:"matches_path"`
RequiredScopes string `db:"required_scopes"`
RequiredAction string `db:"required_action"`
RequiredResource string `db:"required_resource"`
AllowAnonymous bool `db:"allow_anonymous"`
BypassAuthorization bool `db:"disable_firewall"`
Description string `db:"description"`
ID string `db:"id"`
MatchesMethods string `db:"matches_methods"`
MatchesPath string `db:"matches_path"`
RequiredScopes string `db:"required_scopes"`
RequiredAction string `db:"required_action"`
RequiredResource string `db:"required_resource"`
AllowAnonymous bool `db:"allow_anonymous"`
BypassAuthorization bool `db:"disable_firewall"`
BypassAccessControlPolicies bool `db:"disable_acp"`
Description string `db:"description"`
}

func (r *sqlRule) toRule() (*Rule, error) {
Expand All @@ -41,29 +42,31 @@ func (r *sqlRule) toRule() (*Rule, error) {
}

return &Rule{
ID: r.ID,
MatchesMethods: methods,
MatchesPath: exp,
RequiredScopes: scopes,
RequiredAction: r.RequiredAction,
RequiredResource: r.RequiredResource,
AllowAnonymous: r.AllowAnonymous,
BypassAuthorization: r.BypassAuthorization,
Description: r.Description,
ID: r.ID,
MatchesMethods: methods,
MatchesPath: exp,
RequiredScopes: scopes,
RequiredAction: r.RequiredAction,
RequiredResource: r.RequiredResource,
AllowAnonymous: r.AllowAnonymous,
BypassAuthorization: r.BypassAuthorization,
BypassAccessControlPolicies: r.BypassAccessControlPolicies,
Description: r.Description,
}, nil
}

func toSqlRule(r *Rule) *sqlRule {
return &sqlRule{
ID: r.ID,
MatchesMethods: strings.Join(r.MatchesMethods, " "),
MatchesPath: r.MatchesPath.String(),
RequiredScopes: strings.Join(r.RequiredScopes, " "),
RequiredAction: r.RequiredAction,
RequiredResource: r.RequiredResource,
AllowAnonymous: r.AllowAnonymous,
BypassAuthorization: r.BypassAuthorization,
Description: r.Description,
ID: r.ID,
MatchesMethods: strings.Join(r.MatchesMethods, " "),
MatchesPath: r.MatchesPath.String(),
RequiredScopes: strings.Join(r.RequiredScopes, " "),
RequiredAction: r.RequiredAction,
RequiredResource: r.RequiredResource,
AllowAnonymous: r.AllowAnonymous,
BypassAuthorization: r.BypassAuthorization,
BypassAccessControlPolicies: r.BypassAccessControlPolicies,
Description: r.Description,
}
}

Expand All @@ -80,7 +83,8 @@ var migrations = &migrate.MemoryMigrationSource{
required_resource text NOT NULL,
allow_anonymous bool NOT NULL,
description text NOT NULL,
disable_firewall text NOT NULL
disable_firewall bool NOT NULL,
disable_acp bool NOT NULL
)`},
Down: []string{
"DROP TABLE hydra_client",
Expand All @@ -99,6 +103,7 @@ var sqlParams = []string{
"allow_anonymous",
"description",
"disable_firewall",
"disable_acp",
}

func NewSQLManager(db *sqlx.DB) *SQLManager {
Expand Down
15 changes: 8 additions & 7 deletions rule/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,14 @@ func TestManagers(t *testing.T) {
RequiredScopes: []string{"users.create"},
}
r2 := Rule{
ID: "foo2",
Description: "Get users rule",
MatchesPath: mustCompileRegex(t, "/users/([0-9]+)"),
MatchesMethods: []string{"GET"},
AllowAnonymous: true,
RequiredScopes: []string{},
BypassAuthorization: true,
ID: "foo2",
Description: "Get users rule",
MatchesPath: mustCompileRegex(t, "/users/([0-9]+)"),
MatchesMethods: []string{"GET"},
AllowAnonymous: true,
RequiredScopes: []string{},
BypassAuthorization: true,
BypassAccessControlPolicies: true,
}

t.Run("case="+k, func(t *testing.T) {
Expand Down
3 changes: 3 additions & 0 deletions rule/rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ type Rule struct {
// BypassAuthorization if set true disables firewall capabilities.
BypassAuthorization bool

// BypassAccessControlPolicies if set true disables checking access control policies.
BypassAccessControlPolicies bool

// Description describes the rule.
Description string
}
Expand Down
1 change: 1 addition & 0 deletions sdk/swagger/docs/Rule.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**AllowAnonymous** | **bool** | AllowAnonymous sets if the endpoint is public, thus not needing any authorization at all. | [optional] [default to null]
**BypassAccessControlPolicies** | **bool** | BypassAccessControlPolicies if set true disables checking access control policies. | [optional] [default to null]
**BypassAuthorization** | **bool** | BypassAuthorization if set true disables firewall capabilities. | [optional] [default to null]
**Description** | **string** | Description describes the rule. | [optional] [default to null]
**Id** | **string** | ID the a unique id of a rule. | [optional] [default to null]
Expand Down
3 changes: 3 additions & 0 deletions sdk/swagger/rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ type Rule struct {
// AllowAnonymous sets if the endpoint is public, thus not needing any authorization at all.
AllowAnonymous bool `json:"allowAnonymous,omitempty"`

// BypassAccessControlPolicies if set true disables checking access control policies.
BypassAccessControlPolicies bool `json:"bypassAccessControlPolicies,omitempty"`

// BypassAuthorization if set true disables firewall capabilities.
BypassAuthorization bool `json:"bypassAuthorization,omitempty"`

Expand Down

0 comments on commit 18facbb

Please sign in to comment.