4
4
"context"
5
5
"encoding/json"
6
6
"fmt"
7
+ "sync"
7
8
"testing"
8
9
9
10
"github.com/google/uuid"
@@ -12,6 +13,7 @@ import (
12
13
"github.com/stretchr/testify/require"
13
14
"golang.org/x/xerrors"
14
15
16
+ "github.com/coder/coder/coderd/rbac/regosql"
15
17
"github.com/coder/coder/testutil"
16
18
)
17
19
@@ -63,11 +65,14 @@ func TestFilterError(t *testing.T) {
63
65
64
66
t .Run ("CancelledContext" , func (t * testing.T ) {
65
67
t .Parallel ()
66
- t .Skipf ("This test is racy as rego eval checks the ctx canceled in a go routine. " +
67
- "It is a coin flip if the query finishes before the 'cancel' is checked. " +
68
- "So sometimes the 'Authorize' call succeeds even if ctx is canceled." )
69
68
70
- auth := NewAuthorizer (prometheus .NewRegistry ())
69
+ auth := & MockAuthorizer {
70
+ AuthorizeFunc : func (ctx context.Context , subject Subject , action Action , object Object ) error {
71
+ // Authorize func always returns nil, unless the context is cancelled.
72
+ return ctx .Err ()
73
+ },
74
+ }
75
+
71
76
subject := Subject {
72
77
ID : uuid .NewString (),
73
78
Roles : RoleNames {
@@ -77,20 +82,44 @@ func TestFilterError(t *testing.T) {
77
82
Scope : ScopeAll ,
78
83
}
79
84
80
- ctx , cancel := context .WithCancel (context .Background ())
81
- defer cancel ()
82
- objects := []Objecter {
83
- ResourceUser ,
84
- ResourceUser ,
85
- & objectBomb {
85
+ t .Run ("SmallSet" , func (t * testing.T ) {
86
+ t .Parallel ()
87
+
88
+ ctx , cancel := context .WithCancel (context .Background ())
89
+ defer cancel ()
90
+ objects := []Objecter {
91
+ ResourceUser ,
92
+ ResourceUser ,
93
+ & objectBomb {
94
+ Objecter : ResourceUser ,
95
+ bomb : cancel ,
96
+ },
97
+ ResourceUser ,
98
+ }
99
+
100
+ _ , err := Filter (ctx , auth , subject , ActionRead , objects )
101
+ require .ErrorIs (t , err , context .Canceled )
102
+ })
103
+
104
+ // Triggers Prepared Authorize
105
+ t .Run ("LargeSet" , func (t * testing.T ) {
106
+ t .Parallel ()
107
+
108
+ ctx , cancel := context .WithCancel (context .Background ())
109
+ defer cancel ()
110
+
111
+ objects := make ([]Objecter , 100 )
112
+ for i := 0 ; i < 100 ; i ++ {
113
+ objects [i ] = ResourceUser
114
+ }
115
+ objects [20 ] = & objectBomb {
86
116
Objecter : ResourceUser ,
87
117
bomb : cancel ,
88
- },
89
- ResourceUser ,
90
- }
118
+ }
91
119
92
- _ , err := Filter (ctx , auth , subject , ActionRead , objects )
93
- require .ErrorIs (t , err , context .Canceled )
120
+ _ , err := Filter (ctx , auth , subject , ActionRead , objects )
121
+ require .ErrorIs (t , err , context .Canceled )
122
+ })
94
123
})
95
124
}
96
125
@@ -1095,3 +1124,42 @@ func must[T any](value T, err error) T {
1095
1124
}
1096
1125
return value
1097
1126
}
1127
+
1128
+ type MockAuthorizer struct {
1129
+ AuthorizeFunc func (context.Context , Subject , Action , Object ) error
1130
+ }
1131
+
1132
+ var _ Authorizer = (* MockAuthorizer )(nil )
1133
+
1134
+ func (d * MockAuthorizer ) Authorize (ctx context.Context , s Subject , a Action , o Object ) error {
1135
+ return d .AuthorizeFunc (ctx , s , a , o )
1136
+ }
1137
+
1138
+ func (d * MockAuthorizer ) Prepare (_ context.Context , subject Subject , action Action , _ string ) (PreparedAuthorized , error ) {
1139
+ return & mockPreparedAuthorizer {
1140
+ Original : d ,
1141
+ Subject : subject ,
1142
+ Action : action ,
1143
+ }, nil
1144
+ }
1145
+
1146
+ var _ PreparedAuthorized = (* mockPreparedAuthorizer )(nil )
1147
+
1148
+ // fakePreparedAuthorizer is the prepared version of a FakeAuthorizer. It will
1149
+ // return the same error as the original FakeAuthorizer.
1150
+ type mockPreparedAuthorizer struct {
1151
+ sync.RWMutex
1152
+ Original * MockAuthorizer
1153
+ Subject Subject
1154
+ Action Action
1155
+ }
1156
+
1157
+ func (f * mockPreparedAuthorizer ) Authorize (ctx context.Context , object Object ) error {
1158
+ return f .Original .Authorize (ctx , f .Subject , f .Action , object )
1159
+ }
1160
+
1161
+ // CompileToSQL returns a compiled version of the authorizer that will work for
1162
+ // in memory databases. This fake version will not work against a SQL database.
1163
+ func (* mockPreparedAuthorizer ) CompileToSQL (_ context.Context , _ regosql.ConvertConfig ) (string , error ) {
1164
+ return "not a valid sql string" , nil
1165
+ }
0 commit comments