Skip to content

Commit

Permalink
simplified attributes (#64)
Browse files Browse the repository at this point in the history
* simplified attributes

* fixed/added tests, fixed func calls to NewAttributes()

* fixed functions
  • Loading branch information
kristinapathak authored Oct 6, 2020
1 parent 97ae895 commit fa01ecb
Show file tree
Hide file tree
Showing 14 changed files with 380 additions and 414 deletions.
54 changes: 54 additions & 0 deletions attributes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package bascule

import (
"time"

"github.com/xmidt-org/arrange"
)

var nilTime = time.Time{}

type BasicAttributes map[string]interface{}

func (a BasicAttributes) Get(key string) (interface{}, bool) {
v, ok := a[key]
return v, ok
}

//NewAttributes builds an Attributes instance with
//the given map as datasource.
func NewAttributes(m map[string]interface{}) Attributes {
return BasicAttributes(m)
}

// GetNestedAttribute uses multiple keys in order to obtain an attribute.
func GetNestedAttribute(attributes Attributes, keys ...string) (interface{}, bool) {
// need at least one key.
if keys == nil || len(keys) == 0 {
return nil, false
}

var (
result interface{}
ok bool
)
result = attributes
for _, k := range keys {
var a Attributes
if result == nil {
return nil, false
}
ok = arrange.TryConvert(result,
func(attr Attributes) { a = attr },
func(m map[string]interface{}) { a = BasicAttributes(m) },
)
if !ok {
return nil, false
}
result, ok = a.Get(k)
if !ok {
return nil, false
}
}
return result, ok
}
87 changes: 87 additions & 0 deletions attributes_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package bascule

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestGet(t *testing.T) {
assert := assert.New(t)
attributes := Attributes(attrs)

val, ok := attributes.Get("testkey")
assert.Equal("testval", val)
assert.True(ok)

val, ok = attributes.Get("noval")
assert.Empty(val)
assert.False(ok)

emptyAttributes := NewAttributes(map[string]interface{}{})
val, ok = emptyAttributes.Get("test")
assert.Nil(val)
assert.False(ok)
}

func TestGetNestedAttribute(t *testing.T) {
attributes := NewAttributes(map[string]interface{}{
"a": map[string]interface{}{"b": map[string]interface{}{"c": "answer"}},
"one level": "yay",
"bad": nil,
})
tests := []struct {
description string
keys []string
expectedResult interface{}
expectedOK bool
}{
// Success test is failing. ): getting nil, false
{
description: "Success",
keys: []string{"a", "b", "c"},
expectedResult: "answer",
expectedOK: true,
},
{
description: "Success single key",
keys: []string{"one level"},
expectedResult: "yay",
expectedOK: true,
},
{
description: "Success nil",
keys: []string{"bad"},
expectedResult: nil,
expectedOK: true,
},
{
description: "Nil Keys Error",
keys: nil,
},
{
description: "No Keys Error",
keys: []string{},
},
{
description: "Non Attribute Value Error",
keys: []string{"one level", "test"},
},
{
description: "Nil Attributes Error",
keys: []string{"bad", "more bad"},
},
{
description: "Missing Key Error",
keys: []string{"c", "b", "a"},
},
}
for _, tc := range tests {
t.Run(tc.description, func(t *testing.T) {
assert := assert.New(t)
val, ok := GetNestedAttribute(attributes, tc.keys...)
assert.Equal(tc.expectedResult, val)
assert.Equal(tc.expectedOK, ok)
})
}
}
5 changes: 3 additions & 2 deletions basculehttp/enforcer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ func TestEnforcer(t *testing.T) {
}),
WithEErrorResponseFunc(DefaultOnErrorResponse),
)
emptyAttributes := bascule.NewAttributes(map[string]interface{}{})
tests := []struct {
description string
enforcer func(http.Handler) http.Handler
Expand All @@ -36,7 +37,7 @@ func TestEnforcer(t *testing.T) {
enforcer: e2,
auth: bascule.Authentication{
Authorization: "jwt",
Token: bascule.NewToken("test", "", bascule.NewAttributes()),
Token: bascule.NewToken("test", "", emptyAttributes),
},
expectedStatusCode: http.StatusOK,
},
Expand All @@ -63,7 +64,7 @@ func TestEnforcer(t *testing.T) {
enforcer: e2,
auth: bascule.Authentication{
Authorization: "jwt",
Token: bascule.NewToken("", "", bascule.NewAttributes()),
Token: bascule.NewToken("", "", emptyAttributes),
},
expectedStatusCode: http.StatusForbidden,
},
Expand Down
2 changes: 1 addition & 1 deletion basculehttp/listener_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func TestListenerDecorator(t *testing.T) {

ctx := bascule.WithAuthentication(context.Background(), bascule.Authentication{
Authorization: "jwt",
Token: bascule.NewToken("", "", bascule.NewAttributes()),
Token: bascule.NewToken("", "", bascule.NewAttributes(map[string]interface{}{})),
Request: bascule.Request{
URL: u,
Method: "get",
Expand Down
13 changes: 8 additions & 5 deletions basculehttp/tokenFactory.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func (btf BasicTokenFactory) ParseAndValidate(ctx context.Context, _ *http.Reque
}
// "basic" is a placeholder here ... token types won't always map to the
// Authorization header. For example, a JWT should have a type of "jwt" or some such, not "bearer"
return bascule.NewToken("basic", principal, bascule.NewAttributes()), nil
return bascule.NewToken("basic", principal, bascule.NewAttributes(map[string]interface{}{})), nil
}

// NewBasicTokenFactoryFromList takes a list of base64 encoded basic auth keys,
Expand Down Expand Up @@ -161,12 +161,15 @@ func (btf BearerTokenFactory) ParseAndValidate(ctx context.Context, _ *http.Requ
return nil, emperror.WrapWith(err, "failed to get map of claims", "claims struct", claims)
}

jwtClaims := bascule.NewAttributesFromMap(claimsMap)

principal, ok := jwtClaims.GetString(jwtPrincipalKey)
jwtClaims := bascule.NewAttributes(claimsMap)

principalVal, ok := jwtClaims.Get(jwtPrincipalKey)
if !ok {
return nil, emperror.WrapWith(ErrorInvalidPrincipal, "principal value not found", "principal key", jwtPrincipalKey, "jwtClaims", claimsMap)
}
principal, ok := principalVal.(string)
if !ok {
return nil, emperror.WrapWith(ErrorInvalidPrincipal, "principal value of proper type not found", "principal", principal, "jwtClaims", claimsMap)
return nil, emperror.WrapWith(ErrorInvalidPrincipal, "principal value not a string", "principal", principalVal)
}

return bascule.NewToken("jwt", principal, jwtClaims), nil
Expand Down
2 changes: 1 addition & 1 deletion basculehttp/tokenFactory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func TestBasicTokenFactory(t *testing.T) {
{
description: "Sucess",
value: base64.StdEncoding.EncodeToString([]byte("user:pass")),
expectedToken: bascule.NewToken("basic", "user", bascule.NewAttributes()),
expectedToken: bascule.NewToken("basic", "user", bascule.NewAttributes(map[string]interface{}{})),
},
{
description: "Can't Decode Error",
Expand Down
8 changes: 4 additions & 4 deletions checks.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,11 @@ func CreateNonEmptyPrincipalCheck() ValidatorFunc {
// CreateListAttributeCheck returns a Validator that runs checks against the
// content found in the key given. It runs every check and returns all errors
// it finds.
func CreateListAttributeCheck(key string, checks ...func(context.Context, []interface{}) error) ValidatorFunc {
func CreateListAttributeCheck(keys []string, checks ...func(context.Context, []interface{}) error) ValidatorFunc {
return func(ctx context.Context, token Token) error {
val, ok := token.Attributes().Get(key)
val, ok := GetNestedAttribute(token.Attributes(), keys...)
if !ok {
return fmt.Errorf("couldn't find attribute with key %v", key)
return fmt.Errorf("couldn't find attribute with keys %v", keys)
}
strVal, ok := val.([]interface{})
if !ok {
Expand All @@ -80,7 +80,7 @@ func CreateListAttributeCheck(key string, checks ...func(context.Context, []inte
if len(errs) == 0 {
return nil
}
return emperror.Wrap(errs, fmt.Sprintf("attribute checks of key %v failed", key))
return emperror.Wrap(errs, fmt.Sprintf("attribute checks of keys %v failed", keys))
}
}

Expand Down
35 changes: 21 additions & 14 deletions checks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,60 +10,67 @@ import (
func TestCreateAllowAllCheck(t *testing.T) {
assert := assert.New(t)
f := CreateAllowAllCheck()
err := f(context.Background(), NewToken("", "", NewAttributes()))
err := f(context.Background(), NewToken("", "", NewAttributes(map[string]interface{}{})))
assert.Nil(err)
}

func TestCreateValidTypeCheck(t *testing.T) {
emptyAttributes := NewAttributes(map[string]interface{}{})
assert := assert.New(t)
f := CreateValidTypeCheck([]string{"valid", "type"})
err := f(context.Background(), NewToken("valid", "", NewAttributes()))
err := f(context.Background(), NewToken("valid", "", emptyAttributes))
assert.Nil(err)
err = f(context.Background(), NewToken("invalid", "", NewAttributes()))
err = f(context.Background(), NewToken("invalid", "", emptyAttributes))
assert.NotNil(err)
}

func TestCreateNonEmptyTypeCheck(t *testing.T) {
emptyAttributes := NewAttributes(map[string]interface{}{})
assert := assert.New(t)
f := CreateNonEmptyTypeCheck()
err := f(context.Background(), NewToken("type", "", NewAttributes()))
err := f(context.Background(), NewToken("type", "", emptyAttributes))
assert.Nil(err)
err = f(context.Background(), NewToken("", "", NewAttributes()))
err = f(context.Background(), NewToken("", "", emptyAttributes))
assert.NotNil(err)
}

func TestCreateNonEmptyPrincipalCheck(t *testing.T) {
emptyAttributes := NewAttributes(map[string]interface{}{})
assert := assert.New(t)
f := CreateNonEmptyPrincipalCheck()
err := f(context.Background(), NewToken("", "principal", NewAttributes()))
err := f(context.Background(), NewToken("", "principal", emptyAttributes))
assert.Nil(err)
err = f(context.Background(), NewToken("", "", NewAttributes()))
err = f(context.Background(), NewToken("", "", emptyAttributes))
assert.NotNil(err)
}

func TestCreateListAttributeCheck(t *testing.T) {
assert := assert.New(t)
f := CreateListAttributeCheck("testkey.subkey", NonEmptyStringListCheck)
f := CreateListAttributeCheck([]string{"testkey", "subkey"}, NonEmptyStringListCheck)

err := f(context.Background(), NewToken("", "", NewAttributesFromMap(map[string]interface{}{
err := f(context.Background(), NewToken("", "", NewAttributes(map[string]interface{}{
"testkey": map[string]interface{}{"subkey": []interface{}{"a", "b", "c"}}})))
assert.Nil(err)

err = f(context.Background(), NewToken("", "", NewAttributes()))
err = f(context.Background(), NewToken("", "", NewAttributes(map[string]interface{}{})))
assert.NotNil(err)

err = f(context.Background(), NewToken("", "", NewAttributesFromMap(map[string]interface{}{"testkey": ""})))
err = f(context.Background(), NewToken("", "", NewAttributes(map[string]interface{}{"testkey": ""})))
assert.NotNil(err)

err = f(context.Background(), NewToken("", "", NewAttributesFromMap(map[string]interface{}{"testkey": map[string]interface{}{
err = f(context.Background(), NewToken("", "", NewAttributes(map[string]interface{}{"testkey": map[string]interface{}{
"subkey": 5555}})))
assert.NotNil(err)

err = f(context.Background(), NewToken("", "", NewAttributes(map[string]interface{}{"testkey": map[string]interface{}{
"subkey": []interface{}{}}})))
assert.NotNil(err)

err = f(context.Background(), NewToken("", "", NewAttributesFromMap(map[string]interface{}{"testkey": map[string]interface{}{
err = f(context.Background(), NewToken("", "", NewAttributes(map[string]interface{}{"testkey": map[string]interface{}{
"subkey": []interface{}{5, 7, 6}}})))
assert.NotNil(err)

err = f(context.Background(), NewToken("", "", NewAttributesFromMap(map[string]interface{}{"testkey": map[string]interface{}{
err = f(context.Background(), NewToken("", "", NewAttributes(map[string]interface{}{"testkey": map[string]interface{}{
"subkey": []interface{}{""}}})))
assert.NotNil(err)
}
2 changes: 1 addition & 1 deletion context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func TestContext(t *testing.T) {
Token: simpleToken{
tokenType: "test",
principal: "test principal",
attributes: NewAttributesFromMap(map[string]interface{}{"testkey": "testval", "attr": 5}),
attributes: NewAttributes(map[string]interface{}{"testkey": "testval", "attr": 5}),
},
Request: Request{
URL: u,
Expand Down
11 changes: 7 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,19 @@ go 1.12

require (
github.com/SermoDigital/jose v0.9.2-0.20161205224733-f6df55f235c2
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/go-kit/kit v0.8.0
github.com/goph/emperror v0.17.1
github.com/gorilla/mux v1.7.3 // indirect
github.com/jtacoma/uritemplates v1.0.0 // indirect
github.com/justinas/alice v1.2.0 // indirect
github.com/pkg/errors v0.8.0
github.com/pkg/errors v0.8.1
github.com/spf13/cast v1.3.0
github.com/spf13/viper v1.6.1
github.com/stretchr/testify v1.3.0
github.com/spf13/viper v1.7.0
github.com/stretchr/testify v1.4.0
github.com/ugorji/go v1.1.4 // indirect
github.com/xmidt-org/arrange v0.1.9
github.com/xmidt-org/webpa-common v1.1.0
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
)
Loading

0 comments on commit fa01ecb

Please sign in to comment.