@@ -4,142 +4,10 @@ import (
4
4
"context"
5
5
"net/http"
6
6
7
- "github.com/google/uuid"
8
- "golang.org/x/xerrors"
9
-
10
- "cdr.dev/slog"
11
7
"github.com/coder/coder/coderd/database"
12
8
"github.com/coder/coder/coderd/httpapi"
13
- "github.com/coder/coder/coderd/rbac"
14
9
)
15
10
16
- // Authorize will enforce if the user roles can complete the action on the RBACObject.
17
- // The organization and owner are found using the ExtractOrganization and
18
- // ExtractUser middleware if present.
19
- func Authorize (logger slog.Logger , auth rbac.Authorizer , actions ... rbac.Action ) func (http.Handler ) http.Handler {
20
- return func (next http.Handler ) http.Handler {
21
- return http .HandlerFunc (func (rw http.ResponseWriter , r * http.Request ) {
22
- roles := UserRoles (r )
23
- authObject := rbacObject (r )
24
- object := authObject .Object
25
-
26
- if object .Type == "" {
27
- panic ("developer error: auth object has no type" )
28
- }
29
-
30
- // First extract the object's owner and organization if present.
31
- unknownOrg := r .Context ().Value (organizationParamContextKey {})
32
- if organization , castOK := unknownOrg .(database.Organization ); unknownOrg != nil {
33
- if ! castOK {
34
- panic ("developer error: organization param middleware not provided for authorize" )
35
- }
36
- object = object .InOrg (organization .ID )
37
- }
38
-
39
- if authObject .WithOwner != nil {
40
- owner := authObject .WithOwner (r )
41
- object = object .WithOwner (owner .String ())
42
- } else {
43
- // Attempt to find the resource owner id
44
- unknownOwner := r .Context ().Value (userParamContextKey {})
45
- if owner , castOK := unknownOwner .(database.User ); unknownOwner != nil {
46
- if ! castOK {
47
- panic ("developer error: user param middleware not provided for authorize" )
48
- }
49
- object = object .WithOwner (owner .ID .String ())
50
- }
51
- }
52
-
53
- for _ , action := range actions {
54
- err := auth .ByRoleName (r .Context (), roles .ID .String (), roles .Roles , action , object )
55
- if err != nil {
56
- internalError := new (rbac.UnauthorizedError )
57
- if xerrors .As (err , internalError ) {
58
- logger = logger .With (slog .F ("internal" , internalError .Internal ()))
59
- }
60
- // Log information for debugging. This will be very helpful
61
- // in the early days if we over secure endpoints.
62
- logger .Warn (r .Context (), "unauthorized" ,
63
- slog .F ("roles" , roles .Roles ),
64
- slog .F ("user_id" , roles .ID ),
65
- slog .F ("username" , roles .Username ),
66
- slog .F ("route" , r .URL .Path ),
67
- slog .F ("action" , action ),
68
- slog .F ("object" , object ),
69
- )
70
- httpapi .Write (rw , http .StatusUnauthorized , httpapi.Response {
71
- Message : err .Error (),
72
- })
73
- return
74
- }
75
- }
76
- next .ServeHTTP (rw , r )
77
- })
78
- }
79
- }
80
-
81
- type authObjectKey struct {}
82
-
83
- type RBACObject struct {
84
- Object rbac.Object
85
-
86
- // WithOwner will set the Object.Owner field based on the request.
87
- // This allows the Owner field to be set dynamically based on the context
88
- // of the request.
89
- WithOwner func (r * http.Request ) uuid.UUID
90
- }
91
-
92
- func rbacObject (r * http.Request ) RBACObject {
93
- obj , ok := r .Context ().Value (authObjectKey {}).(RBACObject )
94
- if ! ok {
95
- panic ("developer error: auth object middleware not provided" )
96
- }
97
- return obj
98
- }
99
-
100
- func WithAPIKeyAsOwner () func (http.Handler ) http.Handler {
101
- return WithOwner (func (r * http.Request ) uuid.UUID {
102
- key := APIKey (r )
103
- return key .UserID
104
- })
105
- }
106
-
107
- // WithOwner sets the object owner for 'Authorize()' for all routes handled
108
- // by this middleware.
109
- func WithOwner (withOwner func (r * http.Request ) uuid.UUID ) func (http.Handler ) http.Handler {
110
- return func (next http.Handler ) http.Handler {
111
- return http .HandlerFunc (func (rw http.ResponseWriter , r * http.Request ) {
112
- obj , ok := r .Context ().Value (authObjectKey {}).(RBACObject )
113
- if ok {
114
- obj .WithOwner = withOwner
115
- } else {
116
- obj = RBACObject {WithOwner : withOwner }
117
- }
118
-
119
- ctx := context .WithValue (r .Context (), authObjectKey {}, obj )
120
- next .ServeHTTP (rw , r .WithContext (ctx ))
121
- })
122
- }
123
- }
124
-
125
- // WithRBACObject sets the object for 'Authorize()' for all routes handled
126
- // by this middleware. The important field to set is 'Type'
127
- func WithRBACObject (object rbac.Object ) func (http.Handler ) http.Handler {
128
- return func (next http.Handler ) http.Handler {
129
- return http .HandlerFunc (func (rw http.ResponseWriter , r * http.Request ) {
130
- obj , ok := r .Context ().Value (authObjectKey {}).(RBACObject )
131
- if ok {
132
- obj .Object = object
133
- } else {
134
- obj = RBACObject {Object : object }
135
- }
136
-
137
- ctx := context .WithValue (r .Context (), authObjectKey {}, obj )
138
- next .ServeHTTP (rw , r .WithContext (ctx ))
139
- })
140
- }
141
- }
142
-
143
11
// User roles are the 'subject' field of Authorize()
144
12
type userRolesKey struct {}
145
13
0 commit comments