Skip to content

Commit

Permalink
add support for http mw in v2 version (#30)
Browse files Browse the repository at this point in the history
- reorganize code for grpc and http mw in v2 folder
- NOVER-5:add refactoring and http mw changes in v2 version
- NOVER-5: update go mod and path to v2
- NOVER-5: remove grpc_opa package in v2 version
- NOVER-5: update documentation for authorizer
- NOVER-5: remove external dependencies
- NOVER-5: update go version
  • Loading branch information
cbarik-infoblox authored Feb 8, 2024
1 parent d191e93 commit 515846d
Show file tree
Hide file tree
Showing 46 changed files with 1,287 additions and 2,853 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: 1.15.x
go-version: 1.21.x
- name: Unit tests
if: ${{ !matrix.e2e }}
run: |
Expand Down
16 changes: 9 additions & 7 deletions cmd/authz_mw_cli/authz_mw_cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@ import (
"regexp"
"strings"

opamw "github.com/infobloxopen/atlas-authz-middleware/grpc_opa"
opacl "github.com/infobloxopen/atlas-authz-middleware/pkg/opa_client"
"github.com/infobloxopen/atlas-authz-middleware/v2/http_opa"
opacl "github.com/infobloxopen/atlas-authz-middleware/v2/pkg/opa_client"

"github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus"
"github.com/grpc-ecosystem/go-grpc-middleware/util/metautils"

logrus "github.com/sirupsen/logrus"

az "github.com/infobloxopen/atlas-authz-middleware/v2/common/authorizer"
"google.golang.org/grpc/metadata"
)

Expand Down Expand Up @@ -107,9 +108,10 @@ func validate(ctx context.Context, opaIpPort string) {
var decInputr MyDecisionInputr
decInputr.DecisionInput.DecisionDocument = decisionDoc

authzr := opamw.NewDefaultAuthorizer(app,
opamw.WithAddress(opaIpPort),
opamw.WithDecisionInputHandler(&decInputr),
//TODO: add a flag to indicate whether to use http authorizer or grpc authorizer, when grpc authorizer is implemented in v2
authzr := httpopa.NewHttpAuthorizer(app,
httpopa.WithAddress(opaIpPort),
httpopa.WithDecisionInputHandler(&decInputr),
)

resultCtx, resultErr := authzr.AffirmAuthorization(ctx, fullMethod, nil)
Expand Down Expand Up @@ -139,9 +141,9 @@ func acct_entitlements(ctx context.Context, opaIpPort string) {
}

type MyDecisionInputr struct {
opamw.DecisionInput
az.DecisionInput
}

func (d MyDecisionInputr) GetDecisionInput(ctx context.Context, fullMethod string, grpcReq interface{}) (*opamw.DecisionInput, error) {
func (d MyDecisionInputr) GetDecisionInput(ctx context.Context, fullMethod string, grpcReq interface{}) (*az.DecisionInput, error) {
return &d.DecisionInput, nil
}
26 changes: 26 additions & 0 deletions common/authorizer/authorizer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package authorizer

import "context"

// OpaEvaluator implements calling OPA with a request and receiving the raw response
type OpaEvaluator func(ctx context.Context, decisionDocument string, opaReq, opaResp interface{}) error

type ClaimsVerifier func([]string, []string) (string, []error)

// Authorizer interface is implemented for making arbitrary requests to Opa.
type Authorizer interface {
// Evaluate evaluates the authorization policy for the given request.
// It takes the context, full method name, request object, and an OpaEvaluator as input.
// It returns a boolean indicating whether the request is authorized, a modified context,
// and an error if any.
Evaluate(ctx context.Context, fullMethod string, req interface{}, opaEvaluator OpaEvaluator) (bool, context.Context, error)

// OpaQuery executes a query against the OPA (Open Policy Agent) with the specified decision document.
// If the decision document is an empty string, the query is executed against the default decision document
// configured in OPA.
// It takes the context, decision document name, OPA request object, and OPA response object as input.
// It returns an error if any.
OpaQuery(ctx context.Context, decisionDocument string, opaReq, opaResp interface{}) error

AffirmAuthorization(ctx context.Context, fullMethod string, eq interface{}) (context.Context, error)
}
47 changes: 47 additions & 0 deletions common/authorizer/decision_input.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package authorizer

import "context"

// DecisionInput is app/service-specific data supplied by app/service ABAC requests
type DecisionInput struct {
Type string `json:"type"` // Object/resource-type to match
Verb string `json:"verb"` // Verb to match
SealCtx []interface{} `json:"ctx"` // Array of app/service-specific context data to match
DecisionDocument string `json:"-"` // OPA decision document to query, by default "",
// which is default decision document configured in OPA
}

// fullMethod is of the form "Service.FullMethod"
type DecisionInputHandler interface {
// GetDecisionInput returns an app/service-specific DecisionInput.
// A nil DecisionInput should NOT be returned unless error.
GetDecisionInput(ctx context.Context, fullMethod string, req interface{}) (*DecisionInput, error)
}

// DefaultDecisionInputer is an example DecisionInputHandler that is used as default
type DefaultDecisionInputer struct{}

func (m DefaultDecisionInputer) String() string {
return "authorizer.DefaultDecisionInputer{}"
}

// GetDecisionInput is an example DecisionInputHandler that returns some decision input
// based on some incoming Context values. App/services will most likely supply their
// own DecisionInputHandler using WithDecisionInputHandler option.
func (m *DefaultDecisionInputer) GetDecisionInput(ctx context.Context, fullMethod string, grpcReq interface{}) (*DecisionInput, error) {
var abacType string
if v, ok := ctx.Value(TypeKey).(string); ok {
abacType = v
}

var abacVerb string
if v, ok := ctx.Value(VerbKey).(string); ok {
abacVerb = v
}

decInp := DecisionInput{
Type: abacType,
Verb: abacVerb,
}
return &decInp, nil
}
15 changes: 15 additions & 0 deletions common/authorizer/literal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package authorizer

// ABACKey is a context.Context key type
type ABACKey string
type ObligationKey string

const (
// DefaultValidatePath is default OPA path to perform authz validation
DefaultValidatePath = "v1/data/authz/rbac/validate_v1"

REDACTED = "redacted"
TypeKey = ABACKey("ABACType")
VerbKey = ABACKey("ABACVerb")
ObKey = ObligationKey("obligations")
)
80 changes: 80 additions & 0 deletions common/authorizer/mock_Authorizer.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion grpc_opa/claims.go → common/claim/claims.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package grpc_opa_middleware
package claim

import (
atlas_claims "github.com/infobloxopen/atlas-claims"
Expand Down
6 changes: 6 additions & 0 deletions common/literal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package common

const (
// DefaultAcctEntitlementsApiPath is default OPA path to fetch acct entitlements
DefaultAcctEntitlementsApiPath = "v1/data/authz/rbac/acct_entitlements_api"
)
2 changes: 1 addition & 1 deletion grpc_opa/nil_interface.go → common/nil_interface.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package grpc_opa_middleware
package common

import (
"reflect"
Expand Down
15 changes: 13 additions & 2 deletions grpc_opa/nil_interface_test.go → common/nil_interface_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package grpc_opa_middleware
package common

import (
"reflect"
Expand All @@ -23,30 +23,41 @@ func Test_IsNilInterface(t *testing.T) {
var uninitializedStringVar string

type myStructType struct{}

var uninitializedStructVar myStructType

type myPtrType *myStructType

var uninitializedPtrVar myPtrType

type myMapType map[string]string

var uninitializedMapVar myMapType

type myArrayType [3]string

var uninitializedArrayVar myArrayType

type mySliceType []string

var uninitializedSliceVar mySliceType

type myFuncType func(*testing.T)

var uninitializedFuncVar myFuncType
func myTestFunc(*testing.T){}

func myTestFunc(*testing.T) {}

var initializedFuncVar myFuncType = myTestFunc

type myChanType chan string

var uninitializedChanVar myChanType

type myInterfaceType interface{ myMethod() }

func (myST *myStructType) myMethod() {}

var uninitializedStructPtrVar *myStructType
var uninitializedInterfaceVar myInterfaceType
var nilinitializedInterfaceVar myInterfaceType = uninitializedStructPtrVar
Expand Down
22 changes: 22 additions & 0 deletions common/opautil/entitled_features.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package opautil

import "context"

// EntitledFeaturesKeyType is the type of the entitled_features key stored in the caller's context
type EntitledFeaturesKeyType string

// EntitledFeaturesKey is the entitled_features key stored in the caller's context.
// It is also the entitled_features key in the OPA response.
const EntitledFeaturesKey = EntitledFeaturesKeyType("entitled_features")

// AddRawEntitledFeatures adds raw entitled_features (if they exist) from OPAResponse to context
// The raw JSON-unmarshaled entitled_features is of the form:
//
// map[string]interface {}{"lic":[]interface {}{"dhcp", "ipam"}, "rpz":[]interface {}{"bogon", "malware"}}}
func (o OPAResponse) AddRawEntitledFeatures(ctx context.Context) context.Context {
efIfc, ok := o[string(EntitledFeaturesKey)]
if ok {
ctx = context.WithValue(ctx, EntitledFeaturesKey, efIfc)
}
return ctx
}
Loading

0 comments on commit 515846d

Please sign in to comment.