Skip to content

Commit

Permalink
Merge pull request #15 from openfga/method_extractor
Browse files Browse the repository at this point in the history
chore: adds method extractor.
  • Loading branch information
jcchavezs authored Aug 10, 2024
2 parents c9d43cf + fcb933e commit 655ea4d
Show file tree
Hide file tree
Showing 9 changed files with 125 additions and 32 deletions.
4 changes: 2 additions & 2 deletions extauthz/cmd/extauthz/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ func TestNoUserExtractedFails(t *testing.T) {

e := extractor.ExtractorSet{
Name: "extauthz",
User: func(ctx context.Context, value *auth_pb.CheckRequest) (string, bool, error) {
return "", false, expectedErr
User: func(ctx context.Context, value *auth_pb.CheckRequest) (extractor.Extraction, bool, error) {
return extractor.Extraction{}, false, expectedErr
},
}

Expand Down
8 changes: 3 additions & 5 deletions extauthz/e2e/config.yaml.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,10 @@ extraction_sets:
user:
type: mock
config:
value: subject:test_subject
value: subject:user_123
object:
type: mock
config:
value: resource:test_resource
value: resource:service_abc
relation:
type: mock
config:
value: GET
type: method
2 changes: 0 additions & 2 deletions extauthz/e2e/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
version: '3.2'

services:
httpbin:
image: mccutchen/go-httpbin:v2.9.0
Expand Down
54 changes: 45 additions & 9 deletions extauthz/e2e/store.fga.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,54 @@ model: |
type resource
relations
define caller: [subject]
define GET: caller
define can_call: [ subject with allowed_methods ]
condition allowed_methods(allowed: list<string>, method: string) {
allowed.exists_one(r, r == method) || allowed.exists_one(r, r == "*")
}
tuples:
- user: subject:test_subject
relation: caller
object: resource:test_resource
- user: subject:user_123
relation: can_call
object: resource:service_abc
condition:
name: allowed_methods
context:
allowed: ["GET"]
- user: subject:user_456
relation: can_call
object: resource:service_xyz
condition:
name: allowed_methods
context:
allowed: ["*"]

tests:
- name: subject can GET the caller
- name: user_123 can do only GET to service_abc
check:
- user: subject:test_subject
object: resource:test_resource
- user: subject:user_123
assertions:
can_call: true
object: resource:service_abc
context:
method: "GET"
- user: subject:user_123
assertions:
can_call: false
object: resource:service_abc
context:
method: "POST"
- name: user_456 can do only GET to service_xyz
check:
- user: subject:user_456
assertions:
can_call: true
object: resource:service_xyz
context:
method: "GET"
- user: subject:user_456
assertions:
GET: true
can_call: true
object: resource:service_xyz
context:
method: "POST"
11 changes: 10 additions & 1 deletion extauthz/internal/extractor/extractor.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,13 @@ import (
authv3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3"
)

type Extraction struct {
Value string
Context map[string]any
}

// Extractor is the interface for extracting values from a CheckRequest.
type Extractor func(ctx context.Context, value *authv3.CheckRequest) (string, bool, error)
type Extractor func(ctx context.Context, value *authv3.CheckRequest) (Extraction, bool, error)

type ExtractorSet struct {
Name string
Expand All @@ -23,6 +28,8 @@ func GetExtractorConfig(name string) (Config, error) {
switch name {
case "mock":
return &MockConfig{}, nil
case "method":
return nil, nil
default:
return nil, errors.New("extractor not found")
}
Expand All @@ -32,6 +39,8 @@ func MakeExtractor(name string, cfg Config) (Extractor, error) {
switch name {
case "mock":
return NewMock(cfg.(*MockConfig)), nil
case "method":
return NewMethod(cfg), nil
default:
return nil, errors.New("extractor not found")
}
Expand Down
18 changes: 18 additions & 0 deletions extauthz/internal/extractor/method.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package extractor

import (
"context"

authv3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3"
)

func NewMethod(cfg any) Extractor {
return func(ctx context.Context, value *authv3.CheckRequest) (Extraction, bool, error) {
return Extraction{
Value: "can_call",
Context: map[string]interface{}{
"method": value.GetAttributes().GetRequest().GetHttp().GetMethod(),
},
}, true, nil
}
}
9 changes: 5 additions & 4 deletions extauthz/internal/extractor/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ import (
)

type MockConfig struct {
Val string `yaml:"value"`
Err error `yaml:"error"`
Val string `yaml:"value"`
Context map[string]interface{} `yaml:"context"`
Err error `yaml:"error"`
}

func NewMock(cfg *MockConfig) Extractor {
return func(ctx context.Context, value *authv3.CheckRequest) (string, bool, error) {
return cfg.Val, cfg.Val != "", cfg.Err
return func(ctx context.Context, value *authv3.CheckRequest) (Extraction, bool, error) {
return Extraction{Value: cfg.Val, Context: cfg.Context}, cfg.Val != "", cfg.Err
}
}
45 changes: 38 additions & 7 deletions extauthz/internal/server/authz/authz.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,13 @@ func (e ExtAuthZFilter) Check(ctx context.Context, req *envoy.CheckRequest) (res
}

type extracted struct {
user string
object string
relation string
user extractor.Extraction
object extractor.Extraction
relation extractor.Extraction
}

func (e ExtAuthZFilter) extract(ctx context.Context, req *envoy.CheckRequest) (*extracted, error) {
var user, object, relation string
var user, object, relation extractor.Extraction
for _, es := range e.extractionSet {
var (
found bool
Expand Down Expand Up @@ -110,6 +110,22 @@ func (e ExtAuthZFilter) extract(ctx context.Context, req *envoy.CheckRequest) (*
return nil, nil
}

func mergeMaps(map1, map2 map[string]any) map[string]any {
UniqueMap := make(map[string]any)

// for loop for the first map
for key, val := range map1 {
UniqueMap[key] = val
}

// for loop for the second map
for key, val := range map2 {
UniqueMap[key] = val
}
// return merged result
return UniqueMap
}

// Check implements the Check method of the Authorization interface.
func (e ExtAuthZFilter) check(ctx context.Context, req *envoy.CheckRequest) (response *envoy.CheckResponse, err error) {
extracted, err := e.extract(ctx, req)
Expand All @@ -121,10 +137,25 @@ func (e ExtAuthZFilter) check(ctx context.Context, req *envoy.CheckRequest) (res
return deny(codes.InvalidArgument, "No extraction set found"), nil
}

context := map[string]any{}

if extracted.user.Context != nil {
context = mergeMaps(context, extracted.user.Context)
}

if extracted.object.Context != nil {
context = mergeMaps(context, extracted.object.Context)
}

if extracted.relation.Context != nil {
context = mergeMaps(context, extracted.relation.Context)
}

body := client.ClientCheckRequest{
User: extracted.user,
Relation: extracted.relation,
Object: extracted.object,
User: extracted.user.Value,
Relation: extracted.relation.Value,
Object: extracted.object.Value,
Context: &context,
}

options := client.ClientCheckOptions{
Expand Down
6 changes: 4 additions & 2 deletions extauthz/internal/server/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,10 @@ func (e *Extractor) UnmarshalYAML(value *yaml.Node) error {
return fmt.Errorf("getting %s: %w", e.Type, err)
}

if err := rawConfig.Config.Decode(e.Config); err != nil {
return err
if e.Config != nil {
if err := rawConfig.Config.Decode(e.Config); err != nil {
return err
}
}

return nil
Expand Down

0 comments on commit 655ea4d

Please sign in to comment.