From 5089024f4b27d912468a0adb0c1358003df09724 Mon Sep 17 00:00:00 2001 From: Alireza Eliaderani Date: Sat, 15 Aug 2020 23:27:25 +0200 Subject: [PATCH] Add policy to leges --- coverage.txt | 182 ++++++++++++++++++++++++++------------------------ leges.go | 45 ++++++++----- leges_test.go | 68 +++++++++++++++++++ 3 files changed, 191 insertions(+), 104 deletions(-) diff --git a/coverage.txt b/coverage.txt index c63f184..040f4ef 100644 --- a/coverage.txt +++ b/coverage.txt @@ -1,44 +1,4 @@ mode: atomic -github.com/siadat/leges/leges.go:27.43,29.2 1 0 -github.com/siadat/leges/leges.go:31.43,33.2 1 0 -github.com/siadat/leges/leges.go:41.47,43.2 1 0 -github.com/siadat/leges/leges.go:45.47,47.2 1 0 -github.com/siadat/leges/leges.go:59.66,62.51 2 3 -github.com/siadat/leges/leges.go:66.2,66.53 1 3 -github.com/siadat/leges/leges.go:70.2,70.19 1 3 -github.com/siadat/leges/leges.go:62.51,64.3 1 0 -github.com/siadat/leges/leges.go:66.53,68.3 1 0 -github.com/siadat/leges/leges.go:73.55,76.24 2 3 -github.com/siadat/leges/leges.go:80.2,80.12 1 3 -github.com/siadat/leges/leges.go:76.24,78.3 1 0 -github.com/siadat/leges/leges.go:85.54,88.33 2 3 -github.com/siadat/leges/leges.go:112.2,112.12 1 3 -github.com/siadat/leges/leges.go:88.33,89.43 1 6 -github.com/siadat/leges/leges.go:93.3,93.47 1 6 -github.com/siadat/leges/leges.go:97.3,98.17 2 6 -github.com/siadat/leges/leges.go:106.3,109.4 1 6 -github.com/siadat/leges/leges.go:89.43,91.4 1 0 -github.com/siadat/leges/leges.go:93.47,95.4 1 0 -github.com/siadat/leges/leges.go:98.17,104.4 1 0 -github.com/siadat/leges/leges.go:116.62,120.34 2 3 -github.com/siadat/leges/leges.go:125.2,128.46 3 3 -github.com/siadat/leges/leges.go:133.2,133.12 1 3 -github.com/siadat/leges/leges.go:120.34,122.3 1 0 -github.com/siadat/leges/leges.go:128.46,131.3 2 0 -github.com/siadat/leges/leges.go:137.63,138.43 1 3 -github.com/siadat/leges/leges.go:142.2,144.43 2 3 -github.com/siadat/leges/leges.go:164.2,164.24 1 1 -github.com/siadat/leges/leges.go:138.43,140.3 1 0 -github.com/siadat/leges/leges.go:144.43,145.61 1 6 -github.com/siadat/leges/leges.go:149.3,150.17 2 2 -github.com/siadat/leges/leges.go:159.3,159.20 1 2 -github.com/siadat/leges/leges.go:145.61,146.12 1 4 -github.com/siadat/leges/leges.go:150.17,157.4 1 0 -github.com/siadat/leges/leges.go:159.20,161.4 1 2 -github.com/siadat/leges/leges.go:167.56,168.29 1 6 -github.com/siadat/leges/leges.go:173.2,173.14 1 4 -github.com/siadat/leges/leges.go:168.29,169.21 1 6 -github.com/siadat/leges/leges.go:169.21,171.4 1 2 github.com/siadat/leges/policy.go:24.34,25.16 1 6 github.com/siadat/leges/policy.go:28.2,28.12 1 6 github.com/siadat/leges/policy.go:25.16,27.3 1 0 @@ -81,17 +41,48 @@ github.com/siadat/leges/httpserver/httpserver.go:92.16,94.3 1 3 github.com/siadat/leges/httpserver/httpserver.go:98.47,101.16 3 15 github.com/siadat/leges/httpserver/httpserver.go:104.2,104.21 1 15 github.com/siadat/leges/httpserver/httpserver.go:101.16,102.13 1 0 -github.com/siadat/leges/policy.go:24.34,25.16 1 63 -github.com/siadat/leges/policy.go:28.2,28.12 1 61 -github.com/siadat/leges/policy.go:25.16,27.3 1 2 -github.com/siadat/leges/policy.go:31.57,33.2 1 59 -github.com/siadat/leges/request.go:21.35,22.43 1 10019 -github.com/siadat/leges/request.go:25.2,25.45 1 10017 -github.com/siadat/leges/request.go:28.2,28.20 1 10015 -github.com/siadat/leges/request.go:31.2,31.12 1 10014 -github.com/siadat/leges/request.go:22.43,24.3 1 2 -github.com/siadat/leges/request.go:25.45,27.3 1 2 -github.com/siadat/leges/request.go:28.20,30.3 1 1 +github.com/siadat/leges/leges.go:31.43,33.2 1 0 +github.com/siadat/leges/leges.go:35.43,37.2 1 0 +github.com/siadat/leges/leges.go:45.47,47.2 1 0 +github.com/siadat/leges/leges.go:49.47,51.2 1 0 +github.com/siadat/leges/leges.go:63.66,66.51 2 3 +github.com/siadat/leges/leges.go:70.2,70.53 1 3 +github.com/siadat/leges/leges.go:74.2,74.19 1 3 +github.com/siadat/leges/leges.go:66.51,68.3 1 0 +github.com/siadat/leges/leges.go:70.53,72.3 1 0 +github.com/siadat/leges/leges.go:77.55,80.24 2 3 +github.com/siadat/leges/leges.go:84.2,84.12 1 3 +github.com/siadat/leges/leges.go:80.24,82.3 1 0 +github.com/siadat/leges/leges.go:89.54,92.33 2 3 +github.com/siadat/leges/leges.go:98.2,98.12 1 3 +github.com/siadat/leges/leges.go:92.33,93.45 1 6 +github.com/siadat/leges/leges.go:93.45,95.4 1 0 +github.com/siadat/leges/leges.go:101.48,105.42 3 6 +github.com/siadat/leges/leges.go:109.2,109.46 1 6 +github.com/siadat/leges/leges.go:113.2,114.16 2 6 +github.com/siadat/leges/leges.go:122.2,127.12 2 6 +github.com/siadat/leges/leges.go:105.42,107.3 1 0 +github.com/siadat/leges/leges.go:109.46,111.3 1 0 +github.com/siadat/leges/leges.go:114.16,120.3 1 0 +github.com/siadat/leges/leges.go:131.62,135.34 2 3 +github.com/siadat/leges/leges.go:140.2,143.46 3 3 +github.com/siadat/leges/leges.go:148.2,148.12 1 3 +github.com/siadat/leges/leges.go:135.34,137.3 1 0 +github.com/siadat/leges/leges.go:143.46,146.3 2 0 +github.com/siadat/leges/leges.go:152.63,153.43 1 3 +github.com/siadat/leges/leges.go:157.2,159.43 2 3 +github.com/siadat/leges/leges.go:179.2,179.24 1 1 +github.com/siadat/leges/leges.go:153.43,155.3 1 0 +github.com/siadat/leges/leges.go:159.43,160.61 1 5 +github.com/siadat/leges/leges.go:164.3,165.17 2 2 +github.com/siadat/leges/leges.go:174.3,174.20 1 2 +github.com/siadat/leges/leges.go:160.61,161.12 1 3 +github.com/siadat/leges/leges.go:165.17,172.4 1 0 +github.com/siadat/leges/leges.go:174.20,176.4 1 2 +github.com/siadat/leges/leges.go:182.56,183.29 1 5 +github.com/siadat/leges/leges.go:188.2,188.14 1 3 +github.com/siadat/leges/leges.go:183.29,184.21 1 5 +github.com/siadat/leges/leges.go:184.21,186.4 1 2 github.com/siadat/leges/httpserver/httpserver.go:21.70,25.16 3 0 github.com/siadat/leges/httpserver/httpserver.go:32.2,33.16 2 0 github.com/siadat/leges/httpserver/httpserver.go:40.2,47.16 3 0 @@ -123,43 +114,56 @@ github.com/siadat/leges/cmd/leges/main.go:37.12,43.54 5 0 github.com/siadat/leges/cmd/leges/main.go:46.3,46.25 1 0 github.com/siadat/leges/cmd/leges/main.go:43.54,44.14 1 0 github.com/siadat/leges/cmd/leges/main.go:51.62,52.13 1 0 -github.com/siadat/leges/leges.go:27.43,29.2 1 0 github.com/siadat/leges/leges.go:31.43,33.2 1 0 -github.com/siadat/leges/leges.go:41.47,43.2 1 0 +github.com/siadat/leges/leges.go:35.43,37.2 1 0 github.com/siadat/leges/leges.go:45.47,47.2 1 0 -github.com/siadat/leges/leges.go:59.66,62.51 2 19 -github.com/siadat/leges/leges.go:66.2,66.53 1 19 -github.com/siadat/leges/leges.go:70.2,70.19 1 16 -github.com/siadat/leges/leges.go:62.51,64.3 1 0 -github.com/siadat/leges/leges.go:66.53,68.3 1 3 -github.com/siadat/leges/leges.go:73.55,76.24 2 19 -github.com/siadat/leges/leges.go:80.2,80.12 1 19 -github.com/siadat/leges/leges.go:76.24,78.3 1 4 -github.com/siadat/leges/leges.go:85.54,88.33 2 19 -github.com/siadat/leges/leges.go:112.2,112.12 1 16 -github.com/siadat/leges/leges.go:88.33,89.43 1 61 -github.com/siadat/leges/leges.go:93.3,93.47 1 60 -github.com/siadat/leges/leges.go:97.3,98.17 2 59 -github.com/siadat/leges/leges.go:106.3,109.4 1 58 -github.com/siadat/leges/leges.go:89.43,91.4 1 1 -github.com/siadat/leges/leges.go:93.47,95.4 1 1 -github.com/siadat/leges/leges.go:98.17,104.4 1 1 -github.com/siadat/leges/leges.go:116.62,120.34 2 10013 -github.com/siadat/leges/leges.go:125.2,128.46 3 10013 -github.com/siadat/leges/leges.go:133.2,133.12 1 10013 -github.com/siadat/leges/leges.go:120.34,122.3 1 20002 -github.com/siadat/leges/leges.go:128.46,131.3 2 0 -github.com/siadat/leges/leges.go:137.63,138.43 1 10015 -github.com/siadat/leges/leges.go:142.2,144.43 2 10013 -github.com/siadat/leges/leges.go:164.2,164.24 1 5 -github.com/siadat/leges/leges.go:138.43,140.3 1 2 -github.com/siadat/leges/leges.go:144.43,145.61 1 26102 -github.com/siadat/leges/leges.go:149.3,150.17 2 10014 -github.com/siadat/leges/leges.go:159.3,159.20 1 10013 -github.com/siadat/leges/leges.go:145.61,146.12 1 16088 -github.com/siadat/leges/leges.go:150.17,157.4 1 1 -github.com/siadat/leges/leges.go:159.20,161.4 1 10007 -github.com/siadat/leges/leges.go:167.56,168.29 1 26102 -github.com/siadat/leges/leges.go:173.2,173.14 1 16088 -github.com/siadat/leges/leges.go:168.29,169.21 1 63662 -github.com/siadat/leges/leges.go:169.21,171.4 1 10014 +github.com/siadat/leges/leges.go:49.47,51.2 1 0 +github.com/siadat/leges/leges.go:63.66,66.51 2 20 +github.com/siadat/leges/leges.go:70.2,70.53 1 20 +github.com/siadat/leges/leges.go:74.2,74.19 1 17 +github.com/siadat/leges/leges.go:66.51,68.3 1 0 +github.com/siadat/leges/leges.go:70.53,72.3 1 3 +github.com/siadat/leges/leges.go:77.55,80.24 2 20 +github.com/siadat/leges/leges.go:84.2,84.12 1 20 +github.com/siadat/leges/leges.go:80.24,82.3 1 4 +github.com/siadat/leges/leges.go:89.54,92.33 2 20 +github.com/siadat/leges/leges.go:98.2,98.12 1 17 +github.com/siadat/leges/leges.go:92.33,93.45 1 61 +github.com/siadat/leges/leges.go:93.45,95.4 1 3 +github.com/siadat/leges/leges.go:101.48,105.42 3 562 +github.com/siadat/leges/leges.go:109.2,109.46 1 561 +github.com/siadat/leges/leges.go:113.2,114.16 2 560 +github.com/siadat/leges/leges.go:122.2,127.12 2 559 +github.com/siadat/leges/leges.go:105.42,107.3 1 1 +github.com/siadat/leges/leges.go:109.46,111.3 1 1 +github.com/siadat/leges/leges.go:114.16,120.3 1 1 +github.com/siadat/leges/leges.go:131.62,135.34 2 10015 +github.com/siadat/leges/leges.go:140.2,143.46 3 10015 +github.com/siadat/leges/leges.go:148.2,148.12 1 10015 +github.com/siadat/leges/leges.go:135.34,137.3 1 20002 +github.com/siadat/leges/leges.go:143.46,146.3 2 0 +github.com/siadat/leges/leges.go:152.63,153.43 1 10017 +github.com/siadat/leges/leges.go:157.2,159.43 2 10015 +github.com/siadat/leges/leges.go:179.2,179.24 1 6 +github.com/siadat/leges/leges.go:153.43,155.3 1 2 +github.com/siadat/leges/leges.go:159.43,160.61 1 26521 +github.com/siadat/leges/leges.go:164.3,165.17 2 10014 +github.com/siadat/leges/leges.go:174.3,174.20 1 10013 +github.com/siadat/leges/leges.go:160.61,161.12 1 16507 +github.com/siadat/leges/leges.go:165.17,172.4 1 1 +github.com/siadat/leges/leges.go:174.20,176.4 1 10008 +github.com/siadat/leges/leges.go:182.56,183.29 1 26521 +github.com/siadat/leges/leges.go:188.2,188.14 1 16507 +github.com/siadat/leges/leges.go:183.29,184.21 1 64056 +github.com/siadat/leges/leges.go:184.21,186.4 1 10014 +github.com/siadat/leges/policy.go:24.34,25.16 1 564 +github.com/siadat/leges/policy.go:28.2,28.12 1 562 +github.com/siadat/leges/policy.go:25.16,27.3 1 2 +github.com/siadat/leges/policy.go:31.57,33.2 1 560 +github.com/siadat/leges/request.go:21.35,22.43 1 10021 +github.com/siadat/leges/request.go:25.2,25.45 1 10019 +github.com/siadat/leges/request.go:28.2,28.20 1 10017 +github.com/siadat/leges/request.go:31.2,31.12 1 10016 +github.com/siadat/leges/request.go:22.43,24.3 1 2 +github.com/siadat/leges/request.go:25.45,27.3 1 2 +github.com/siadat/leges/request.go:28.20,30.3 1 1 diff --git a/leges.go b/leges.go index 5c950a7..ede1b50 100644 --- a/leges.go +++ b/leges.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/antonmedv/expr" "github.com/antonmedv/expr/vm" + "sync" ) var ErrDuplicatePolicyID = errors.New("duplicate policies with id") @@ -15,6 +16,9 @@ type Leges struct { cachedPolicies map[string]cachedPolicy // environment are a set of attributes that are always merged with the request environment Attributes + + // lock provides mutex for cachedPolicies + lock sync.Mutex } type ErrExprRunFailed struct { @@ -86,29 +90,40 @@ func (l *Leges) loadPolicies(polices []Policy) error { l.cachedPolicies = make(map[string]cachedPolicy, len(polices)) for _, policy := range polices { - if err := policy.Validate(); err != nil { + if err := l.AddPolicy(policy); err != nil { return err } + } - if _, ok := l.cachedPolicies[policy.ID]; ok { - return fmt.Errorf("id=%q: %w", policy.ID, ErrDuplicatePolicyID) - } + return nil +} - program, err := policy.compileCondition() - if err != nil { - return &ErrExprCompileFailed{ - Environment: l.environment, - Policy: policy, - Err: err, - } - } +func (l *Leges) AddPolicy(policy Policy) error { + l.lock.Lock() + defer l.lock.Unlock() + + if err := policy.Validate(); err != nil { + return err + } - l.cachedPolicies[policy.ID] = cachedPolicy{ - policy: policy, - program: program, + if _, ok := l.cachedPolicies[policy.ID]; ok { + return fmt.Errorf("id=%q: %w", policy.ID, ErrDuplicatePolicyID) + } + + program, err := policy.compileCondition() + if err != nil { + return &ErrExprCompileFailed{ + Environment: l.environment, + Policy: policy, + Err: err, } } + l.cachedPolicies[policy.ID] = cachedPolicy{ + policy: policy, + program: program, + } + return nil } diff --git a/leges_test.go b/leges_test.go index d8418c2..e243b26 100644 --- a/leges_test.go +++ b/leges_test.go @@ -547,6 +547,74 @@ func TestSimplePolicy(t *testing.T) { } +func TestLeges_AddPolicy(t *testing.T) { + rules := mustNewLeges(t, []leges.Policy{}, nil) + request := leges.Request{ + Action: "UPDATE", + Subject: leges.Attributes{ + "id": "user1", + }, + Object: leges.Attributes{ + "type": "account", + "owner_id": "user1", + }, + } + + t.Run("no policies matches", func(t *testing.T) { + ok, _, err := rules.Match(request) + require.NoError(t, err) + require.Equal(t, false, ok) + }) + + t.Run("policy1 matches", func(t *testing.T) { + policy1 := leges.Policy{ + ID: "policy1", + Condition: ` + object.type == "account" + and object.owner_id == subject.id + `, + Actions: []string{ + "PUBLIC_VIEW", + "GET", + "UPDATE", + "MY_ACTION", + }, + } + rules.AddPolicy(policy1) + ok, policy, err := rules.Match(request) + require.NoError(t, err) + require.Equal(t, true, ok) + require.Equal(t, "policy1", policy.ID) + }) + + t.Run("add policies concurrently to check for race conditions", func(t *testing.T) { + wg := sync.WaitGroup{} + for i := 0; i < 500; i++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + policy := leges.Policy{ + ID: fmt.Sprintf("policy%d", i+2), + Condition: ` + object.type == "account" + and object.owner_id == subject.id + `, + Actions: []string{ + "PUBLIC_VIEW", + "GET", + "UPDATE", + "MY_ACTION", + }, + } + err := rules.AddPolicy(policy) + require.NoError(t, err) + }(i) + } + wg.Wait() + }) + +} + func mustNewLeges(t *testing.T, policies []leges.Policy, sharedEnv leges.Attributes) *leges.Leges { t.Helper() rules, err := leges.NewLeges(policies, sharedEnv)