@@ -54,6 +54,7 @@ func LogNotAuthorizedError(ctx context.Context, logger slog.Logger, err error) e
54
54
}
55
55
}
56
56
57
+ // insert is the same as insertWithReturn, but does not return the inserted object.
57
58
func insert [ArgumentType any ,
58
59
Insert func (ctx context.Context , arg ArgumentType ) error ](
59
60
// Arguments
@@ -69,6 +70,9 @@ func insert[ArgumentType any,
69
70
}
70
71
}
71
72
73
+ // insertWithReturn runs an rbac.ActionCreate on the rbac object argument before
74
+ // running the insertFunc. The insertFunc is expected to return the object that
75
+ // was inserted.
72
76
func insertWithReturn [ObjectType any , ArgumentType any ,
73
77
Insert func (ctx context.Context , arg ArgumentType ) (ObjectType , error )](
74
78
// Arguments
@@ -130,9 +134,45 @@ func update[ObjectType rbac.Objecter,
130
134
return fetchAndExec (logger , authorizer , rbac .ActionUpdate , fetchFunc , updateExec )
131
135
}
132
136
133
- // authorizedFetchAndExecWithConverter uses authorizedFetchAndQueryWithConverter but
134
- // only cares about the error return type. SQL execs only return an error.
135
- // See authorizedFetchAndQueryWithConverter for more details.
137
+ // fetch is a generic function that wraps a database
138
+ // query function (returns an object and an error) with authorization. The
139
+ // returned function has the same arguments as the database function.
140
+ //
141
+ // The database query function will **ALWAYS** hit the database, even if the
142
+ // user cannot read the resource. This is because the resource details are
143
+ // required to run a proper authorization check.
144
+ func fetch [ArgumentType any , ObjectType rbac.Objecter ,
145
+ DatabaseFunc func (ctx context.Context , arg ArgumentType ) (ObjectType , error )](
146
+ // Arguments
147
+ logger slog.Logger ,
148
+ authorizer rbac.Authorizer ,
149
+ f DatabaseFunc ) DatabaseFunc {
150
+ return func (ctx context.Context , arg ArgumentType ) (empty ObjectType , err error ) {
151
+ // Fetch the rbac subject
152
+ act , ok := ActorFromContext (ctx )
153
+ if ! ok {
154
+ return empty , NoActorError
155
+ }
156
+
157
+ // Fetch the database object
158
+ object , err := f (ctx , arg )
159
+ if err != nil {
160
+ return empty , xerrors .Errorf ("fetch object: %w" , err )
161
+ }
162
+
163
+ // Authorize the action
164
+ err = authorizer .Authorize (ctx , act , rbac .ActionRead , object .RBACObject ())
165
+ if err != nil {
166
+ return empty , LogNotAuthorizedError (ctx , logger , err )
167
+ }
168
+
169
+ return object , nil
170
+ }
171
+ }
172
+
173
+ // fetchAndExec uses fetchAndQuery but only returns the error. The naming comes
174
+ // from SQL 'exec' functions which only return an error.
175
+ // See fetchAndQuery for more information.
136
176
func fetchAndExec [ObjectType rbac.Objecter ,
137
177
ArgumentType any ,
138
178
Fetch func (ctx context.Context , arg ArgumentType ) (ObjectType , error ),
@@ -152,6 +192,10 @@ func fetchAndExec[ObjectType rbac.Objecter,
152
192
}
153
193
}
154
194
195
+ // fetchAndQuery is a generic function that wraps a database fetch and query.
196
+ // The fetch is used to know which rbac object the action should be asserted on
197
+ // **before** the query runs. The returns from the fetch are only used to
198
+ // assert rbac. The final return of this function comes from the Query function.
155
199
func fetchAndQuery [ObjectType rbac.Objecter , ArgumentType any ,
156
200
Fetch func (ctx context.Context , arg ArgumentType ) (ObjectType , error ),
157
201
Query func (ctx context.Context , arg ArgumentType ) (ObjectType , error )](
@@ -184,42 +228,6 @@ func fetchAndQuery[ObjectType rbac.Objecter, ArgumentType any,
184
228
}
185
229
}
186
230
187
- // fetch is a generic function that wraps a database
188
- // query function (returns an object and an error) with authorization. The
189
- // returned function has the same arguments as the database function.
190
- //
191
- // The database query function will **ALWAYS** hit the database, even if the
192
- // user cannot read the resource. This is because the resource details are
193
- // required to run a proper authorization check.
194
- func fetch [ArgumentType any , ObjectType rbac.Objecter ,
195
- DatabaseFunc func (ctx context.Context , arg ArgumentType ) (ObjectType , error )](
196
- // Arguments
197
- logger slog.Logger ,
198
- authorizer rbac.Authorizer ,
199
- f DatabaseFunc ) DatabaseFunc {
200
- return func (ctx context.Context , arg ArgumentType ) (empty ObjectType , err error ) {
201
- // Fetch the rbac subject
202
- act , ok := ActorFromContext (ctx )
203
- if ! ok {
204
- return empty , NoActorError
205
- }
206
-
207
- // Fetch the database object
208
- object , err := f (ctx , arg )
209
- if err != nil {
210
- return empty , xerrors .Errorf ("fetch object: %w" , err )
211
- }
212
-
213
- // Authorize the action
214
- err = authorizer .Authorize (ctx , act , rbac .ActionRead , object .RBACObject ())
215
- if err != nil {
216
- return empty , LogNotAuthorizedError (ctx , logger , err )
217
- }
218
-
219
- return object , nil
220
- }
221
- }
222
-
223
231
// fetchWithPostFilter is like fetch, but works with lists of objects.
224
232
// SQL filters are much more optimal.
225
233
func fetchWithPostFilter [ArgumentType any , ObjectType rbac.Objecter ,
0 commit comments