-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* added initial basculechecks files, moved checks.go * renamed interfaces and function names for better package clarity * updated changelog * fixed lint issues
- Loading branch information
1 parent
9a727c9
commit 8f512df
Showing
19 changed files
with
1,865 additions
and
125 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()) | ||
}) | ||
} | ||
} |
Oops, something went wrong.