@@ -3,6 +3,9 @@ package authzquery
3
3
import (
4
4
"context"
5
5
"database/sql"
6
+ "fmt"
7
+
8
+ "cdr.dev/slog"
6
9
7
10
"golang.org/x/xerrors"
8
11
@@ -17,20 +20,51 @@ var (
17
20
// NoActorError wraps ErrNoRows for the api to return a 404. This is the correct
18
21
// response when the user is not authorized.
19
22
NoActorError = xerrors .Errorf ("no authorization actor in context: %w" , sql .ErrNoRows )
20
- // TODO: Log this error every time it occurs.
21
- NotAuthorizedError = xerrors .Errorf ("unauthorized: %w" , sql .ErrNoRows )
22
23
)
23
24
25
+ // NotAuthorizedError is a sentinal error that unwraps to sql.ErrNoRows.
26
+ // This allows the internal error to be read by the caller if needed. Otherwise
27
+ // it will be handled as a 404.
28
+ type NotAuthorizedError struct {
29
+ Err error
30
+ }
31
+
32
+ func (e NotAuthorizedError ) Error () string {
33
+ return fmt .Sprintf ("unauthorized: %s" , e .Err .Error ())
34
+ }
35
+
36
+ // Unwrap will always unwrap to a sql.ErrNoRows so the API returns a 404.
37
+ // So 'errors.Is(err, sql.ErrNoRows)' will always be true.
38
+ func (e NotAuthorizedError ) Unwrap () error {
39
+ return sql .ErrNoRows
40
+ }
41
+
42
+ func LogNotAuthorizedError (ctx context.Context , logger slog.Logger , err error ) error {
43
+ // Only log the errors if it is an UnauthorizedError error.
44
+ internalError := new (rbac.UnauthorizedError )
45
+ if err != nil && xerrors .As (err , internalError ) {
46
+ logger .Debug (ctx , "unauthorized" ,
47
+ slog .F ("internal" , internalError .Internal ()),
48
+ slog .F ("input" , internalError .Input ()),
49
+ slog .Error (err ),
50
+ )
51
+ }
52
+ return NotAuthorizedError {
53
+ Err : err ,
54
+ }
55
+ }
56
+
24
57
func authorizedInsert [ArgumentType any ,
25
58
Insert func (ctx context.Context , arg ArgumentType ) error ](
26
59
// Arguments
60
+ logger slog.Logger ,
27
61
authorizer rbac.Authorizer ,
28
62
action rbac.Action ,
29
63
object rbac.Objecter ,
30
64
insertFunc Insert ) Insert {
31
65
32
66
return func (ctx context.Context , arg ArgumentType ) error {
33
- _ , err := authorizedInsertWithReturn (authorizer , action , object , func (ctx context.Context , arg ArgumentType ) (rbac.Objecter , error ) {
67
+ _ , err := authorizedInsertWithReturn (logger , authorizer , action , object , func (ctx context.Context , arg ArgumentType ) (rbac.Objecter , error ) {
34
68
return rbac.Object {}, insertFunc (ctx , arg )
35
69
})(ctx , arg )
36
70
return err
@@ -40,6 +74,7 @@ func authorizedInsert[ArgumentType any,
40
74
func authorizedInsertWithReturn [ObjectType any , ArgumentType any ,
41
75
Insert func (ctx context.Context , arg ArgumentType ) (ObjectType , error )](
42
76
// Arguments
77
+ logger slog.Logger ,
43
78
authorizer rbac.Authorizer ,
44
79
action rbac.Action ,
45
80
object rbac.Objecter ,
@@ -55,7 +90,7 @@ func authorizedInsertWithReturn[ObjectType any, ArgumentType any,
55
90
// Authorize the action
56
91
err = authorizer .Authorize (ctx , act , action , object .RBACObject ())
57
92
if err != nil {
58
- return empty , NotAuthorizedError
93
+ return empty , LogNotAuthorizedError ( ctx , logger , err )
59
94
}
60
95
61
96
// Insert the database object
@@ -67,11 +102,12 @@ func authorizedDelete[ObjectType rbac.Objecter, ArgumentType any,
67
102
Fetch func (ctx context.Context , arg ArgumentType ) (ObjectType , error ),
68
103
Delete func (ctx context.Context , arg ArgumentType ) error ](
69
104
// Arguments
105
+ logger slog.Logger ,
70
106
authorizer rbac.Authorizer ,
71
107
fetchFunc Fetch ,
72
108
deleteFunc Delete ) Delete {
73
109
74
- return authorizedFetchAndExec (authorizer ,
110
+ return authorizedFetchAndExec (logger , authorizer ,
75
111
rbac .ActionDelete , fetchFunc , deleteFunc )
76
112
}
77
113
@@ -80,23 +116,25 @@ func authorizedUpdateWithReturn[ObjectType rbac.Objecter,
80
116
Fetch func (ctx context.Context , arg ArgumentType ) (ObjectType , error ),
81
117
UpdateQuery func (ctx context.Context , arg ArgumentType ) (ObjectType , error )](
82
118
// Arguments
119
+ logger slog.Logger ,
83
120
authorizer rbac.Authorizer ,
84
121
fetchFunc Fetch ,
85
122
updateQuery UpdateQuery ) UpdateQuery {
86
123
87
- return authorizedFetchAndQuery (authorizer , rbac .ActionUpdate , fetchFunc , updateQuery )
124
+ return authorizedFetchAndQuery (logger , authorizer , rbac .ActionUpdate , fetchFunc , updateQuery )
88
125
}
89
126
90
127
func authorizedUpdate [ObjectType rbac.Objecter ,
91
128
ArgumentType any ,
92
129
Fetch func (ctx context.Context , arg ArgumentType ) (ObjectType , error ),
93
130
Exec func (ctx context.Context , arg ArgumentType ) error ](
94
131
// Arguments
132
+ logger slog.Logger ,
95
133
authorizer rbac.Authorizer ,
96
134
fetchFunc Fetch ,
97
135
updateExec Exec ) Exec {
98
136
99
- return authorizedFetchAndExec (authorizer , rbac .ActionUpdate , fetchFunc , updateExec )
137
+ return authorizedFetchAndExec (logger , authorizer , rbac .ActionUpdate , fetchFunc , updateExec )
100
138
}
101
139
102
140
// authorizedFetchAndExecWithConverter uses authorizedFetchAndQueryWithConverter but
@@ -107,12 +145,13 @@ func authorizedFetchAndExec[ObjectType rbac.Objecter,
107
145
Fetch func (ctx context.Context , arg ArgumentType ) (ObjectType , error ),
108
146
Exec func (ctx context.Context , arg ArgumentType ) error ](
109
147
// Arguments
148
+ logger slog.Logger ,
110
149
authorizer rbac.Authorizer ,
111
150
action rbac.Action ,
112
151
fetchFunc Fetch ,
113
152
execFunc Exec ) Exec {
114
153
115
- f := authorizedFetchAndQuery (authorizer , action , fetchFunc , func (ctx context.Context , arg ArgumentType ) (empty ObjectType , err error ) {
154
+ f := authorizedFetchAndQuery (logger , authorizer , action , fetchFunc , func (ctx context.Context , arg ArgumentType ) (empty ObjectType , err error ) {
116
155
return empty , execFunc (ctx , arg )
117
156
})
118
157
return func (ctx context.Context , arg ArgumentType ) error {
@@ -125,6 +164,7 @@ func authorizedFetchAndQuery[ObjectType rbac.Objecter, ArgumentType any,
125
164
Fetch func (ctx context.Context , arg ArgumentType ) (ObjectType , error ),
126
165
Query func (ctx context.Context , arg ArgumentType ) (ObjectType , error )](
127
166
// Arguments
167
+ logger slog.Logger ,
128
168
authorizer rbac.Authorizer ,
129
169
action rbac.Action ,
130
170
fetchFunc Fetch ,
@@ -146,7 +186,7 @@ func authorizedFetchAndQuery[ObjectType rbac.Objecter, ArgumentType any,
146
186
// Authorize the action
147
187
err = authorizer .Authorize (ctx , act , action , object .RBACObject ())
148
188
if err != nil {
149
- return empty , NotAuthorizedError
189
+ return empty , LogNotAuthorizedError ( ctx , logger , err )
150
190
}
151
191
152
192
return queryFunc (ctx , arg )
@@ -156,10 +196,11 @@ func authorizedFetchAndQuery[ObjectType rbac.Objecter, ArgumentType any,
156
196
func authorizedFetch [ObjectType rbac.Objecter , ArgumentType any ,
157
197
Fetch func (ctx context.Context , arg ArgumentType ) (ObjectType , error )](
158
198
// Arguments
199
+ logger slog.Logger ,
159
200
authorizer rbac.Authorizer ,
160
201
fetchFunc Fetch ) Fetch {
161
202
162
- return authorizedQuery (authorizer , rbac .ActionRead , fetchFunc )
203
+ return authorizedQuery (logger , authorizer , rbac .ActionRead , fetchFunc )
163
204
}
164
205
165
206
// authorizedQuery is a generic function that wraps a database
@@ -175,6 +216,7 @@ func authorizedFetch[ObjectType rbac.Objecter, ArgumentType any,
175
216
func authorizedQuery [ArgumentType any , ObjectType rbac.Objecter ,
176
217
DatabaseFunc func (ctx context.Context , arg ArgumentType ) (ObjectType , error )](
177
218
// Arguments
219
+ logger slog.Logger ,
178
220
authorizer rbac.Authorizer ,
179
221
action rbac.Action ,
180
222
f DatabaseFunc ) DatabaseFunc {
@@ -195,7 +237,7 @@ func authorizedQuery[ArgumentType any, ObjectType rbac.Objecter,
195
237
// Authorize the action
196
238
err = authorizer .Authorize (ctx , act , action , object .RBACObject ())
197
239
if err != nil {
198
- return empty , NotAuthorizedError
240
+ return empty , LogNotAuthorizedError ( ctx , logger , err )
199
241
}
200
242
201
243
return object , nil
@@ -236,6 +278,7 @@ func authorizedFetchSet[ArgumentType any, ObjectType rbac.Objecter,
236
278
// are predicated on the RBAC permissions of the related Template object.
237
279
func authorizedQueryWithRelated [ObjectType any , ArgumentType any , Related rbac.Objecter ](
238
280
// Arguments
281
+ logger slog.Logger ,
239
282
authorizer rbac.Authorizer ,
240
283
action rbac.Action ,
241
284
relatedFunc func (ObjectType , ArgumentType ) (Related , error ),
0 commit comments