Skip to content

Commit

Permalink
Checks (#85)
Browse files Browse the repository at this point in the history
* added initial basculechecks files, moved checks.go

* renamed interfaces and function names for better package clarity

* updated changelog

* fixed lint issues
  • Loading branch information
kristinapathak authored Apr 26, 2021
1 parent 9a727c9 commit 8f512df
Show file tree
Hide file tree
Showing 19 changed files with 1,865 additions and 125 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Moved log.go to basculehttp and simplified code, with nothing exported. [#79](https://github.com/xmidt-org/bascule/pull/79)
- Added constructor option for letting users decide what gets written on the HTTP response on errors. [#84](https://github.com/xmidt-org/bascule/pull/84)
- Added metric listener for auth validation outcome. [#81](https://github.com/xmidt-org/bascule/pull/81)
- Moved checks to their own package and added capability checks. [#85](https://github.com/xmidt-org/bascule/pull/85)

## [v0.9.0]
- added helper function for building basic auth map [#59](https://github.com/xmidt-org/bascule/pull/59)
Expand Down
94 changes: 94 additions & 0 deletions basculechecks/capabilitiesmap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/**
* Copyright 2021 Comcast Cable Communications Management, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package basculechecks

import (
"errors"

"github.com/goph/emperror"
"github.com/xmidt-org/bascule"
)

var (
ErrNilDefaultChecker = errors.New("default checker cannot be nil")
ErrEmptyEndpoint = errors.New("endpoint provided is empty")
)

// CapabilitiesMap runs a capability check based on the value of the parsedURL,
// which is the key to the CapabilitiesMap's map. The parsedURL is expected to
// be some regex values, allowing for bucketing of urls that contain some kind
// of ID or otherwise variable portion of a URL.
type CapabilitiesMap struct {
Checkers map[string]EndpointChecker
DefaultChecker EndpointChecker
}

// Check uses the parsed endpoint value to determine which EndpointChecker to
// run against the capabilities in the auth provided. If there is no
// EndpointChecker for the endpoint, the default is used. As long as one
// capability is found to be authorized by the EndpointChecker, no error is
// returned.
func (c CapabilitiesMap) CheckAuthentication(auth bascule.Authentication, vs ParsedValues) (string, error) {
if auth.Token == nil {
return MissingValues, ErrNoToken
}

if auth.Request.URL == nil {
return MissingValues, ErrNoURL
}

if vs.Endpoint == "" {
return EmptyParsedURL, ErrEmptyEndpoint
}

capabilities, reason, err := getCapabilities(auth.Token.Attributes())
if err != nil {
return reason, err
}

// determine which EndpointChecker to use.
checker, ok := c.Checkers[vs.Endpoint]
if !ok || checker == nil {
checker = c.DefaultChecker
}
reqURL := auth.Request.URL.EscapedPath()
method := auth.Request.Method

// if the checker is nil, we treat it like a checker that always returns
// false.
if checker == nil {
return NoCapabilitiesMatch, emperror.With(ErrNoValidCapabilityFound,
"capabilitiesFound", capabilities, "request URL", reqURL,
"request method", method, "parsed URL", vs.Endpoint,
"checker", checker)
}

// if one of the capabilities is good, then the request is authorized
// for this endpoint.
for _, capability := range capabilities {
if checker.Authorized(capability, reqURL, method) {
return "", nil
}
}

return NoCapabilitiesMatch, emperror.With(ErrNoValidCapabilityFound,
"capabilitiesFound", capabilities, "request URL", reqURL,
"request method", method, "parsed URL", vs.Endpoint,
"checker", checker)

}
175 changes: 175 additions & 0 deletions basculechecks/capabilitiesmap_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
/**
* Copyright 2021 Comcast Cable Communications Management, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package basculechecks

import (
"net/url"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/xmidt-org/bascule"
)

func TestCapabilitiesMapCheck(t *testing.T) {
goodDefault := ConstEndpointCheck("default checker")
checkersMap := map[string]EndpointChecker{
"a": ConstEndpointCheck("meh"),
"bcedef": ConstEndpointCheck("yay"),
"all": ConstEndpointCheck("good"),
"fallback": nil,
}
cm := CapabilitiesMap{
Checkers: checkersMap,
DefaultChecker: goodDefault,
}
nilCM := CapabilitiesMap{}
goodCapabilities := []string{
"test",
"",
"yay",
"...",
}
goodToken := bascule.NewToken("test", "princ",
bascule.NewAttributes(map[string]interface{}{CapabilityKey: goodCapabilities}))
defaultCapabilities := []string{
"test",
"",
"default checker",
"...",
}
defaultToken := bascule.NewToken("test", "princ",
bascule.NewAttributes(map[string]interface{}{CapabilityKey: defaultCapabilities}))
badToken := bascule.NewToken("", "", nil)
tests := []struct {
description string
cm CapabilitiesMap
token bascule.Token
includeURL bool
endpoint string
expectedReason string
expectedErr error
}{
{
description: "Success",
cm: cm,
token: goodToken,
includeURL: true,
endpoint: "bcedef",
},
{
description: "Success Not in Map",
cm: cm,
token: defaultToken,
includeURL: true,
endpoint: "b",
},
{
description: "Success Nil Map Value",
cm: cm,
token: defaultToken,
includeURL: true,
endpoint: "fallback",
},
{
description: "No Match Error",
cm: cm,
token: goodToken,
includeURL: true,
endpoint: "b",
expectedReason: NoCapabilitiesMatch,
expectedErr: ErrNoValidCapabilityFound,
},
{
description: "No Match with Default Checker Error",
cm: cm,
token: defaultToken,
includeURL: true,
endpoint: "bcedef",
expectedReason: NoCapabilitiesMatch,
expectedErr: ErrNoValidCapabilityFound,
},
{
description: "No Match Nil Default Checker Error",
cm: nilCM,
token: defaultToken,
includeURL: true,
endpoint: "bcedef",
expectedReason: NoCapabilitiesMatch,
expectedErr: ErrNoValidCapabilityFound,
},
{
description: "No Token Error",
cm: cm,
token: nil,
includeURL: true,
expectedReason: MissingValues,
expectedErr: ErrNoToken,
},
{
description: "No Request URL Error",
cm: cm,
token: goodToken,
includeURL: false,
expectedReason: MissingValues,
expectedErr: ErrNoURL,
},
{
description: "Empty Endpoint Error",
cm: cm,
token: goodToken,
includeURL: true,
endpoint: "",
expectedReason: EmptyParsedURL,
expectedErr: ErrEmptyEndpoint,
},
{
description: "Get Capabilities Error",
cm: cm,
token: badToken,
includeURL: true,
endpoint: "b",
expectedReason: UndeterminedCapabilities,
expectedErr: ErrNilAttributes,
},
}
for _, tc := range tests {
t.Run(tc.description, func(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
auth := bascule.Authentication{
Token: tc.token,
}
if tc.includeURL {
goodURL, err := url.Parse("/test")
require.Nil(err)
auth.Request = bascule.Request{
URL: goodURL,
Method: "GET",
}
}
reason, err := tc.cm.CheckAuthentication(auth, ParsedValues{Endpoint: tc.endpoint})
assert.Equal(tc.expectedReason, reason)
if err == nil || tc.expectedErr == nil {
assert.Equal(tc.expectedErr, err)
return
}
assert.Contains(err.Error(), tc.expectedErr.Error())
})
}
}
Loading

0 comments on commit 8f512df

Please sign in to comment.