1414// limitations under the License.
1515
1616// Authorization package
17- package authz
17+ package authz_test
1818
1919import (
2020 "fmt"
2121 "io"
2222 "os"
2323 "testing"
24+ "time"
2425
26+ "github.com/cenkalti/backoff/v4"
27+ "github.com/chainloop-dev/chainloop/app/controlplane/internal/authz"
28+ "github.com/chainloop-dev/chainloop/app/controlplane/internal/biz/testhelpers"
2529 "github.com/google/uuid"
2630 "github.com/stretchr/testify/assert"
2731 "github.com/stretchr/testify/require"
@@ -30,8 +34,8 @@ import (
3034func TestAddPolicies (t * testing.T ) {
3135 testcases := []struct {
3236 name string
33- subject * SubjectAPIToken
34- policies []* Policy
37+ subject * authz. SubjectAPIToken
38+ policies []* authz. Policy
3539 wantErr bool
3640 wantNumberPolicies int
3741 }{
@@ -41,38 +45,38 @@ func TestAddPolicies(t *testing.T) {
4145 },
4246 {
4347 name : "no subject" ,
44- policies : []* Policy {
45- PolicyWorkflowContractList ,
48+ policies : []* authz. Policy {
49+ authz . PolicyWorkflowContractList ,
4650 },
4751 wantErr : true ,
4852 },
4953 {
5054 name : "no policies" ,
51- subject : & SubjectAPIToken {ID : uuid .NewString ()},
55+ subject : & authz. SubjectAPIToken {ID : uuid .NewString ()},
5256 wantErr : true ,
5357 },
5458 {
5559 name : "adds two policies" ,
56- subject : & SubjectAPIToken {ID : uuid .NewString ()},
57- policies : []* Policy {
58- PolicyWorkflowContractList ,
59- PolicyReferrerRead ,
60+ subject : & authz. SubjectAPIToken {ID : uuid .NewString ()},
61+ policies : []* authz. Policy {
62+ authz . PolicyWorkflowContractList ,
63+ authz . PolicyReferrerRead ,
6064 },
6165 wantNumberPolicies : 2 ,
6266 },
6367 {
6468 name : "handles duplicated policies" ,
65- subject : & SubjectAPIToken {
69+ subject : & authz. SubjectAPIToken {
6670 ID : uuid .NewString (),
6771 },
68- policies : []* Policy {
69- PolicyWorkflowContractList ,
70- PolicyWorkflowContractRead ,
71- PolicyWorkflowContractUpdate ,
72- PolicyWorkflowContractList ,
73- PolicyArtifactDownload ,
74- PolicyWorkflowContractUpdate ,
75- PolicyArtifactDownload ,
72+ policies : []* authz. Policy {
73+ authz . PolicyWorkflowContractList ,
74+ authz . PolicyWorkflowContractRead ,
75+ authz . PolicyWorkflowContractUpdate ,
76+ authz . PolicyWorkflowContractList ,
77+ authz . PolicyArtifactDownload ,
78+ authz . PolicyWorkflowContractUpdate ,
79+ authz . PolicyArtifactDownload ,
7680 },
7781 wantNumberPolicies : 4 ,
7882 },
@@ -103,22 +107,22 @@ func TestAddPolicies(t *testing.T) {
103107}
104108
105109func TestAddPoliciesDuplication (t * testing.T ) {
106- want := []* Policy {
107- PolicyWorkflowContractList ,
108- PolicyWorkflowContractRead ,
110+ want := []* authz. Policy {
111+ authz . PolicyWorkflowContractList ,
112+ authz . PolicyWorkflowContractRead ,
109113 }
110114
111115 enforcer , closer := testEnforcer (t )
112116 defer closer .Close ()
113- sub := & SubjectAPIToken {ID : uuid .NewString ()}
117+ sub := & authz. SubjectAPIToken {ID : uuid .NewString ()}
114118
115119 err := enforcer .AddPolicies (sub , want ... )
116120 require .NoError (t , err )
117121 got := enforcer .GetFilteredPolicy (0 , sub .String ())
118122 assert .Len (t , got , 2 )
119123
120124 // Update the list of policies we want to add by appending an extra one
121- want = append (want , PolicyWorkflowContractUpdate )
125+ want = append (want , authz . PolicyWorkflowContractUpdate )
122126 // AddPolicies only add the policies that are not already present preventing duplication
123127 err = enforcer .AddPolicies (sub , want ... )
124128 assert .NoError (t , err )
@@ -127,15 +131,15 @@ func TestAddPoliciesDuplication(t *testing.T) {
127131}
128132
129133func TestClearPolicies (t * testing.T ) {
130- want := []* Policy {
131- PolicyWorkflowContractList ,
132- PolicyWorkflowContractRead ,
134+ want := []* authz. Policy {
135+ authz . PolicyWorkflowContractList ,
136+ authz . PolicyWorkflowContractRead ,
133137 }
134138
135139 enforcer , closer := testEnforcer (t )
136140 defer closer .Close ()
137- sub := & SubjectAPIToken {ID : uuid .NewString ()}
138- sub2 := & SubjectAPIToken {ID : uuid .NewString ()}
141+ sub := & authz. SubjectAPIToken {ID : uuid .NewString ()}
142+ sub2 := & authz. SubjectAPIToken {ID : uuid .NewString ()}
139143
140144 // Create policies for two different subjects
141145 err := enforcer .AddPolicies (sub , want ... )
@@ -160,13 +164,68 @@ func TestClearPolicies(t *testing.T) {
160164 assert .Len (t , got , 2 )
161165}
162166
163- func testEnforcer (t * testing.T ) (* Enforcer , io.Closer ) {
167+ func testEnforcer (t * testing.T ) (* authz. Enforcer , io.Closer ) {
164168 f , err := os .CreateTemp (t .TempDir (), "policy*.csv" )
165169 if err != nil {
166170 require .FailNow (t , err .Error ())
167171 }
168172
169- enforcer , err := NewFiletypeEnforcer (f .Name ())
173+ enforcer , err := authz . NewFiletypeEnforcer (f .Name ())
170174 require .NoError (t , err )
171175 return enforcer , f
172176}
177+
178+ func TestMultiReplicaPropagation (t * testing.T ) {
179+ // Create two enforcers that share the same database
180+ db := testhelpers .NewTestDatabase (t )
181+ defer db .Close (t )
182+
183+ enforcerA , err := authz .NewDatabaseEnforcer (testhelpers .NewConfData (db , t ).Database )
184+ require .NoError (t , err )
185+ enforcerB , err := authz .NewDatabaseEnforcer (testhelpers .NewConfData (db , t ).Database )
186+ require .NoError (t , err )
187+
188+ // Subject and policies to add
189+ sub := & authz.SubjectAPIToken {ID : uuid .NewString ()}
190+ want := []* authz.Policy {authz .PolicyWorkflowContractList , authz .PolicyWorkflowContractRead }
191+
192+ // Create policies in one enforcer
193+ err = enforcerA .AddPolicies (sub , want ... )
194+ require .NoError (t , err )
195+
196+ // Make sure it propagates to the other one
197+ got := enforcerA .GetFilteredPolicy (0 , sub .String ())
198+ assert .Len (t , got , 2 )
199+
200+ // it might take a bit for the policies to propagate to the other enforcer
201+ err = fnWithRetry (func () error {
202+ got = enforcerB .GetFilteredPolicy (0 , sub .String ())
203+ if len (got ) == 2 {
204+ return nil
205+ }
206+ return fmt .Errorf ("policies not propagated yet" )
207+ })
208+ require .NoError (t , err )
209+ assert .Len (t , got , 2 )
210+
211+ // Then delete them from the second one and check propagation again
212+ require .NoError (t , enforcerB .ClearPolicies (sub ))
213+ assert .Len (t , enforcerB .GetFilteredPolicy (0 , sub .String ()), 0 )
214+
215+ // Make sure it propagates to the other one
216+ err = fnWithRetry (func () error {
217+ got = enforcerA .GetFilteredPolicy (0 , sub .String ())
218+ if len (got ) == 0 {
219+ return nil
220+ }
221+
222+ return fmt .Errorf ("policies not propagated yet" )
223+ })
224+ require .NoError (t , err )
225+ assert .Len (t , enforcerA .GetFilteredPolicy (0 , sub .String ()), 0 )
226+ }
227+
228+ func fnWithRetry (f func () error ) error {
229+ // Max 1 seconds
230+ return backoff .Retry (f , backoff .WithMaxRetries (backoff .NewConstantBackOff (100 * time .Millisecond ), 10 ))
231+ }
0 commit comments