Skip to content

Commit

Permalink
Merge pull request #42 from rchowinfoblox/filter-cpt-api-v2
Browse files Browse the repository at this point in the history
PTCI-830: Add FilterCompartmentPermissions/Features api to v2 branch
  • Loading branch information
jcookson-infoblox authored Dec 2, 2024
2 parents a6eb93c + 86bafc6 commit 86c2529
Show file tree
Hide file tree
Showing 11 changed files with 691 additions and 43 deletions.
12 changes: 12 additions & 0 deletions common/authorizer/authorizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ type ClaimsVerifier func([]string, []string) (string, []error)
// (map of acct_id to map of service to array of features)
type AcctEntitlementsType map[string]map[string][]string

// FilterCompartmentPermissionsType is a convenience data type, returned by FilterCompartmentPermissions()
// (map of application to array of permissions)
type FilterCompartmentPermissionsType []string

// FilterCompartmentFeaturesType is a convenience data type, returned by FilterCompartmentFeatures()
// (map of application to array of feature)
type FilterCompartmentFeaturesType map[string][]string

// Authorizer interface is implemented for making arbitrary requests to Opa.
type Authorizer interface {
// Evaluate evaluates the authorization policy for the given request.
Expand All @@ -31,4 +39,8 @@ type Authorizer interface {
GetAcctEntitlements(ctx context.Context, accountIDs, serviceNames []string) (*AcctEntitlementsType, error)

GetCurrentUserCompartments(ctx context.Context) ([]string, error)

FilterCompartmentPermissions(ctx context.Context, permissions FilterCompartmentPermissionsType) (FilterCompartmentPermissionsType, error)

FilterCompartmentFeatures(ctx context.Context, features FilterCompartmentFeaturesType) (FilterCompartmentFeaturesType, error)
}
6 changes: 6 additions & 0 deletions common/authorizer/literal.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ const (
// DefaultCurrentUserCompartmentsPath is default OPA path to fetch current user's compartments
DefaultCurrentUserCompartmentsPath = "v1/data/authz/rbac/current_user_compartments"

// DefaultFilterCompartmentPermissionsApiPath is default OPA path to filter compartment permissions
DefaultFilterCompartmentPermissionsApiPath = "v1/data/authz/rbac/filter_compartment_permissions_api"

// DefaultFilterCompartmentFeaturesApiPath is default OPA path to filter compartment features
DefaultFilterCompartmentFeaturesApiPath = "v1/data/authz/rbac/filter_compartment_features_api"

REDACTED = "redacted"
TypeKey = ABACKey("ABACType")
VerbKey = ABACKey("ABACVerb")
Expand Down
30 changes: 30 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.

3 changes: 2 additions & 1 deletion common/opautil/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func (o OPAResponse) Obligations() (*ObligationsNode, error) {

func RedactJWT(jwt string) string {
parts := strings.Split(jwt, ".")
// Redact signature
if len(parts) > 0 {
parts[len(parts)-1] = az.REDACTED
}
Expand All @@ -54,7 +55,7 @@ func RedactJWT(jwt string) string {

func RedactJWTForDebug(jwt string) string {
parts := strings.Split(jwt, ".")
// Redact signature, header and body since we do not want to display any for debug logging
// Redact header/payload/signature, since we do not want to display any for debug logging
for i := range parts {
parts[i] = parts[i][:Min(len(parts[i]), 16)] + "/" + az.REDACTED
}
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
github.com/golang/mock v1.6.0
github.com/google/uuid v1.3.0
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
github.com/infobloxopen/atlas-claims v1.0.0
github.com/infobloxopen/atlas-claims v1.1.2
github.com/infobloxopen/seal v0.2.3
github.com/open-policy-agent/opa v0.37.2
github.com/sirupsen/logrus v1.8.1
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -302,8 +302,8 @@ github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/infobloxopen/atlas-claims v1.0.0 h1:uGwFxbEGDZSql3ePeH/z/TA14IInGBNOkzOOGNrdrBI=
github.com/infobloxopen/atlas-claims v1.0.0/go.mod h1:6aN87f8OZRqQZ6abcI6tbHiXnE5QiTyNVd31Cb67hy8=
github.com/infobloxopen/atlas-claims v1.1.2 h1:IzKTrRYuXuaBL3gIIffaLT1rOMM7k0ApOPlI6MyP2pE=
github.com/infobloxopen/atlas-claims v1.1.2/go.mod h1:6aN87f8OZRqQZ6abcI6tbHiXnE5QiTyNVd31Cb67hy8=
github.com/infobloxopen/seal v0.2.3 h1:TVIw52FxVVwehat/m23+hjoFXbIvyKBA9XCVI21p68A=
github.com/infobloxopen/seal v0.2.3/go.mod h1:IHbkKw7rx7oJKNtyjHL+1XaGKo5NU8CjFE3ZpA5mrB8=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
Expand Down
52 changes: 29 additions & 23 deletions http_opa/authorizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,17 @@ var SERVICENAME = "opa"

// httpAuthorizer is an implementation of the az.Authorizer interface for HTTP-based authorization using OPA.
type httpAuthorizer struct {
application string
clienter opa_client.Clienter
opaEvaluator az.OpaEvaluator
decisionInputHandler az.DecisionInputHandler
claimsVerifier az.ClaimsVerifier
entitledServices []string
acctEntitlementsApi string
currUserCompartmentsApi string
endpointModifier *EndpointModifier
application string
clienter opa_client.Clienter
opaEvaluator az.OpaEvaluator
decisionInputHandler az.DecisionInputHandler
claimsVerifier az.ClaimsVerifier
entitledServices []string
acctEntitlementsApi string
currUserCompartmentsApi string
filterCompartmentPermsApi string
filterCompartmentFeatsApi string
endpointModifier *EndpointModifier
}

var defDecisionInputer = new(az.DefaultDecisionInputer)
Expand All @@ -40,11 +42,13 @@ var defDecisionInputer = new(az.DefaultDecisionInputer)
func NewHttpAuthorizer(application string, opts ...Option) az.Authorizer {
// Configuration options for the authorizer
cfg := &Config{
address: opa_client.DefaultAddress,
decisionInputHandler: defDecisionInputer,
claimsVerifier: commonClaim.UnverifiedClaimFromBearers,
acctEntitlementsApi: az.DefaultAcctEntitlementsApiPath,
currUserCompartmentsApi: az.DefaultCurrentUserCompartmentsPath,
address: opa_client.DefaultAddress,
decisionInputHandler: defDecisionInputer,
claimsVerifier: commonClaim.UnverifiedClaimFromBearers,
acctEntitlementsApi: az.DefaultAcctEntitlementsApiPath,
currUserCompartmentsApi: az.DefaultCurrentUserCompartmentsPath,
filterCompartmentPermsApi: az.DefaultFilterCompartmentPermissionsApiPath,
filterCompartmentFeatsApi: az.DefaultFilterCompartmentFeaturesApiPath,
}
for _, opt := range opts {
opt(cfg)
Expand All @@ -56,15 +60,17 @@ func NewHttpAuthorizer(application string, opts ...Option) az.Authorizer {
}

a := httpAuthorizer{
clienter: clienter,
opaEvaluator: cfg.opaEvaluator,
application: application,
decisionInputHandler: cfg.decisionInputHandler,
claimsVerifier: cfg.claimsVerifier,
entitledServices: cfg.entitledServices,
acctEntitlementsApi: cfg.acctEntitlementsApi,
currUserCompartmentsApi: cfg.currUserCompartmentsApi,
endpointModifier: cfg.endpointModifier,
clienter: clienter,
opaEvaluator: cfg.opaEvaluator,
application: application,
decisionInputHandler: cfg.decisionInputHandler,
claimsVerifier: cfg.claimsVerifier,
entitledServices: cfg.entitledServices,
acctEntitlementsApi: cfg.acctEntitlementsApi,
currUserCompartmentsApi: cfg.currUserCompartmentsApi,
filterCompartmentPermsApi: cfg.filterCompartmentPermsApi,
filterCompartmentFeatsApi: cfg.filterCompartmentFeatsApi,
endpointModifier: cfg.endpointModifier,
}
return &a
}
Expand Down
34 changes: 25 additions & 9 deletions http_opa/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,17 @@ type Config struct {
// address to opa
address string

clienter opa_client.Clienter
opaEvaluator az.OpaEvaluator
authorizer []az.Authorizer
decisionInputHandler az.DecisionInputHandler
claimsVerifier az.ClaimsVerifier
entitledServices []string
acctEntitlementsApi string
currUserCompartmentsApi string
endpointModifier *EndpointModifier
clienter opa_client.Clienter
opaEvaluator az.OpaEvaluator
authorizer []az.Authorizer
decisionInputHandler az.DecisionInputHandler
claimsVerifier az.ClaimsVerifier
entitledServices []string
acctEntitlementsApi string
currUserCompartmentsApi string
filterCompartmentPermsApi string
filterCompartmentFeatsApi string
endpointModifier *EndpointModifier
}

func (c Config) GetAuthorizer() []az.Authorizer {
Expand Down Expand Up @@ -162,6 +164,20 @@ func WithCurrentUserCompartmentsPath(currUserCompartmentsApi string) Option {
}
}

// WithFilterComparmentPermissionsApiPath overrides default CurrentUserCompartmentsApiPath
func WithFilterComparmentPermissionsApiPath(filterCompartmentPermsApi string) Option {
return func(c *Config) {
c.filterCompartmentPermsApi = filterCompartmentPermsApi
}
}

// WithFilterComparmentFeaturesApiPath overrides default CurrentUserCompartmentsApiPath
func WithFilterComparmentFeaturesApiPath(filterCompartmentFeatsApi string) Option {
return func(c *Config) {
c.filterCompartmentFeatsApi = filterCompartmentFeatsApi
}
}

// WithAcctSegmentsNeeded overrides default 0
func WithEndpointModifier(modifier *EndpointModifier) Option {
return func(c *Config) {
Expand Down
117 changes: 117 additions & 0 deletions http_opa/filter_compartment_perms_feats.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package httpopa

import (
"context"
"fmt"

"github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus"
az "github.com/infobloxopen/atlas-authz-middleware/v2/common/authorizer"
commonClaim "github.com/infobloxopen/atlas-authz-middleware/v2/common/claim"
"github.com/infobloxopen/atlas-authz-middleware/v2/common/opautil"
atlas_claims "github.com/infobloxopen/atlas-claims"
logrus "github.com/sirupsen/logrus"
)

// FilterCompartmentPermissionsInput is the input payload for filter_compartment_permissions_api
type FilterCompartmentPermissionsInput struct {
JWT string `json:"jwt"`
Permissions az.FilterCompartmentPermissionsType `json:"permissions"`
}

// FilterCompartmentPermissionsResult is the data type json.Unmarshaled from OPA RESTAPI query
// to filter_compartment_permissions_api rego rule
type FilterCompartmentPermissionsResult struct {
Result az.FilterCompartmentPermissionsType `json:"result"`
}

// FilterCompartmentPermissions filters list of permissions based on the JWT in the context
func (a *httpAuthorizer) FilterCompartmentPermissions(ctx context.Context, permissions az.FilterCompartmentPermissionsType) (az.FilterCompartmentPermissionsType, error) {
lgNtry := ctxlogrus.Extract(ctx)
permsResult := FilterCompartmentPermissionsResult{}

// This fetches auth data from auth headers in metadata from context:
// bearer = data from "authorization bearer" metadata header
// newBearer = data from "set-authorization bearer" metadata header
bearer, newBearer := atlas_claims.AuthBearersFromCtx(ctx)

claimsVerifier := a.claimsVerifier
if claimsVerifier == nil {
claimsVerifier = commonClaim.UnverifiedClaimFromBearers
}

rawJWT, errs := claimsVerifier([]string{bearer}, []string{newBearer})
if len(errs) > 0 {
return nil, fmt.Errorf("%q", errs)
}

opaReq := opautil.OPARequest{
Input: &FilterCompartmentPermissionsInput{
JWT: opautil.RedactJWT(rawJWT),
Permissions: permissions,
},
}

err := a.clienter.CustomQuery(ctx, a.filterCompartmentPermsApi, opaReq, &permsResult)
if err != nil {
lgNtry.WithError(err).Error("filter_compartment_permissions_fail")
return nil, err
}

lgNtry.WithFields(logrus.Fields{
"permsResult": fmt.Sprintf("%#v", permsResult),
}).Trace("filter_compartment_permissions_okay")

return permsResult.Result, nil
}

// FilterCompartmentFeaturesInput is the input payload for filter_compartment_features_api
type FilterCompartmentFeaturesInput struct {
JWT string `json:"jwt"`
ApplicationFeatures az.FilterCompartmentFeaturesType `json:"application_features"`
}

// FilterCompartmentFeaturesResult is the data type json.Unmarshaled from OPA RESTAPI query
// to filter_compartment_features_api rego rule
type FilterCompartmentFeaturesResult struct {
Result az.FilterCompartmentFeaturesType `json:"result"`
}

// FilterCompartmentFeatures filters list of features based on the JWT in the context
func (a *httpAuthorizer) FilterCompartmentFeatures(ctx context.Context, features az.FilterCompartmentFeaturesType) (az.FilterCompartmentFeaturesType, error) {
lgNtry := ctxlogrus.Extract(ctx)
featsResult := FilterCompartmentFeaturesResult{}

// This fetches auth data from auth headers in metadata from context:
// bearer = data from "authorization bearer" metadata header
// newBearer = data from "set-authorization bearer" metadata header
bearer, newBearer := atlas_claims.AuthBearersFromCtx(ctx)

claimsVerifier := a.claimsVerifier
if claimsVerifier == nil {
claimsVerifier = commonClaim.UnverifiedClaimFromBearers
}

rawJWT, errs := claimsVerifier([]string{bearer}, []string{newBearer})
if len(errs) > 0 {
return nil, fmt.Errorf("%q", errs)
}

opaReq := opautil.OPARequest{
Input: &FilterCompartmentFeaturesInput{
JWT: opautil.RedactJWT(rawJWT),
ApplicationFeatures: features,
},
}

err := a.clienter.CustomQuery(ctx, a.filterCompartmentFeatsApi, opaReq, &featsResult)
if err != nil {
lgNtry.WithError(err).Error("filter_compartment_features_fail")
return nil, err
}

lgNtry.WithFields(logrus.Fields{
"featsResult": fmt.Sprintf("%#v", featsResult),
}).Trace("filter_compartment_features_okay")

return featsResult.Result, nil
}
Loading

0 comments on commit 86c2529

Please sign in to comment.