5
5
"net/http"
6
6
7
7
"github.com/coder/coder/v2/coderd/database"
8
+ "github.com/coder/coder/v2/coderd/database/dbauthz"
8
9
"github.com/coder/coder/v2/coderd/httpapi"
9
10
"github.com/coder/coder/v2/codersdk"
10
11
)
@@ -25,8 +26,8 @@ func OrganizationParam(r *http.Request) database.Organization {
25
26
26
27
// OrganizationMemberParam returns the organization membership that allowed the query
27
28
// from the ExtractOrganizationParam handler.
28
- func OrganizationMemberParam (r * http.Request ) database. OrganizationMember {
29
- organizationMember , ok := r .Context ().Value (organizationMemberParamContextKey {}).(database. OrganizationMember )
29
+ func OrganizationMemberParam (r * http.Request ) OrganizationMember {
30
+ organizationMember , ok := r .Context ().Value (organizationMemberParamContextKey {}).(OrganizationMember )
30
31
if ! ok {
31
32
panic ("developer error: organization member param middleware not provided" )
32
33
}
@@ -62,14 +63,31 @@ func ExtractOrganizationParam(db database.Store) func(http.Handler) http.Handler
62
63
}
63
64
}
64
65
66
+ // OrganizationMember is the database object plus the Username. Including the Username in this
67
+ // middleware is preferable to a join at the SQL layer so that we can keep the autogenerated
68
+ // database types as they are.
69
+ type OrganizationMember struct {
70
+ database.OrganizationMember
71
+ Username string
72
+ }
73
+
65
74
// ExtractOrganizationMemberParam grabs a user membership from the "organization" and "user" URL parameter.
66
75
// This middleware requires the ExtractUser and ExtractOrganization middleware higher in the stack
67
76
func ExtractOrganizationMemberParam (db database.Store ) func (http.Handler ) http.Handler {
68
77
return func (next http.Handler ) http.Handler {
69
78
return http .HandlerFunc (func (rw http.ResponseWriter , r * http.Request ) {
70
79
ctx := r .Context ()
80
+ // We need to resolve the `{user}` URL parameter so that we can get the userID and
81
+ // username. We do this as SystemRestricted since the caller might have permission
82
+ // to access the OrganizationMember object, but *not* the User object. So, it is
83
+ // very important that we do not add the User object to the request context or otherwise
84
+ // leak it to the API handler.
85
+ // nolint:gocritic
86
+ user , ok := extractUserContext (dbauthz .AsSystemRestricted (ctx ), db , rw , r )
87
+ if ! ok {
88
+ return
89
+ }
71
90
organization := OrganizationParam (r )
72
- user := UserParam (r )
73
91
74
92
organizationMember , err := db .GetOrganizationMemberByUserID (ctx , database.GetOrganizationMemberByUserIDParams {
75
93
OrganizationID : organization .ID ,
@@ -87,7 +105,17 @@ func ExtractOrganizationMemberParam(db database.Store) func(http.Handler) http.H
87
105
return
88
106
}
89
107
90
- ctx = context .WithValue (ctx , organizationMemberParamContextKey {}, organizationMember )
108
+ ctx = context .WithValue (ctx , organizationMemberParamContextKey {}, OrganizationMember {
109
+ OrganizationMember : organizationMember ,
110
+ // Here we're making one exception to the rule about not leaking data about the user
111
+ // to the API handler, which is to include the username. If the caller has permission
112
+ // to read the OrganizationMember, then we're explicitly saying here that they also
113
+ // have permission to see the member's username, which is itself uncontroversial.
114
+ //
115
+ // API handlers need this information for audit logging and returning the owner's
116
+ // username in response to creating a workspace.
117
+ Username : user .Username ,
118
+ })
91
119
next .ServeHTTP (rw , r .WithContext (ctx ))
92
120
})
93
121
}
0 commit comments