Skip to content

Commit e94a0e1

Browse files
committed
add avatar URL to org member httpmw
1 parent aef1d44 commit e94a0e1

File tree

2 files changed

+45
-12
lines changed

2 files changed

+45
-12
lines changed

coderd/httpmw/organizationparam.go

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,13 @@ func ExtractOrganizationParam(db database.Store) func(http.Handler) http.Handler
6363
}
6464
}
6565

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.
66+
// OrganizationMember is the database object plus the Username and Avatar URL. Including these
67+
// in the middleware is preferable to a join at the SQL layer so that we can keep the
68+
// autogenerated database types as they are.
6969
type OrganizationMember struct {
7070
database.OrganizationMember
71-
Username string
71+
Username string
72+
AvatarURL string
7273
}
7374

7475
// ExtractOrganizationMemberParam grabs a user membership from the "organization" and "user" URL parameter.
@@ -107,14 +108,17 @@ func ExtractOrganizationMemberParam(db database.Store) func(http.Handler) http.H
107108

108109
ctx = context.WithValue(ctx, organizationMemberParamContextKey{}, OrganizationMember{
109110
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.
111+
// Here we're making two exceptions to the rule about not leaking data about the user
112+
// to the API handler, which is to include the username and avatar URL.
113+
// If the caller has permission to read the OrganizationMember, then we're explicitly
114+
// saying here that they also have permission to see the member's username and avatar.
115+
// This is OK!
114116
//
115117
// 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+
// username in response to creating a workspace. Additionally, the frontend consumes
119+
// the Avatar URL and this allows the FE to avoid an extra request.
120+
Username: user.Username,
121+
AvatarURL: user.AvatarURL,
118122
})
119123
next.ServeHTTP(rw, r.WithContext(ctx))
120124
})

coderd/httpmw/organizationparam_test.go

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,17 @@ import (
88

99
"github.com/go-chi/chi/v5"
1010
"github.com/google/uuid"
11+
"github.com/stretchr/testify/assert"
1112
"github.com/stretchr/testify/require"
1213

1314
"github.com/coder/coder/v2/coderd/database"
1415
"github.com/coder/coder/v2/coderd/database/dbgen"
1516
"github.com/coder/coder/v2/coderd/database/dbmem"
1617
"github.com/coder/coder/v2/coderd/database/dbtime"
1718
"github.com/coder/coder/v2/coderd/httpmw"
19+
"github.com/coder/coder/v2/coderd/rbac"
1820
"github.com/coder/coder/v2/codersdk"
21+
"github.com/coder/coder/v2/testutil"
1922
)
2023

2124
func TestOrganizationParam(t *testing.T) {
@@ -139,6 +142,7 @@ func TestOrganizationParam(t *testing.T) {
139142
t.Run("Success", func(t *testing.T) {
140143
t.Parallel()
141144
var (
145+
ctx = testutil.Context(t, testutil.WaitShort)
142146
db = dbmem.New()
143147
rw = httptest.NewRecorder()
144148
r, user = setupAuthentication(db)
@@ -148,7 +152,14 @@ func TestOrganizationParam(t *testing.T) {
148152
_ = dbgen.OrganizationMember(t, db, database.OrganizationMember{
149153
OrganizationID: organization.ID,
150154
UserID: user.ID,
155+
Roles: []string{rbac.RoleOrgMember(organization.ID)},
151156
})
157+
_, err := db.UpdateUserRoles(ctx, database.UpdateUserRolesParams{
158+
ID: user.ID,
159+
GrantedRoles: []string{rbac.RoleTemplateAdmin()},
160+
})
161+
require.NoError(t, err)
162+
152163
chi.RouteContext(r.Context()).URLParams.Add("organization", organization.ID.String())
153164
chi.RouteContext(r.Context()).URLParams.Add("user", user.ID.String())
154165
rtr.Use(
@@ -161,9 +172,27 @@ func TestOrganizationParam(t *testing.T) {
161172
httpmw.ExtractOrganizationMemberParam(db),
162173
)
163174
rtr.Get("/", func(rw http.ResponseWriter, r *http.Request) {
164-
_ = httpmw.OrganizationParam(r)
165-
_ = httpmw.OrganizationMemberParam(r)
175+
org := httpmw.OrganizationParam(r)
176+
assert.NotZero(t, org)
177+
assert.NotZero(t, org.CreatedAt)
178+
// assert.NotZero(t, org.Description) // not supported
179+
assert.NotZero(t, org.ID)
180+
assert.NotEmpty(t, org.Name)
181+
orgMem := httpmw.OrganizationMemberParam(r)
166182
rw.WriteHeader(http.StatusOK)
183+
assert.NotZero(t, orgMem)
184+
assert.NotZero(t, orgMem.CreatedAt)
185+
assert.NotZero(t, orgMem.UpdatedAt)
186+
assert.Equal(t, org.ID, orgMem.OrganizationID)
187+
assert.Equal(t, user.ID, orgMem.UserID)
188+
assert.Equal(t, user.Username, orgMem.Username)
189+
assert.Equal(t, user.AvatarURL, orgMem.AvatarURL)
190+
assert.NotEmpty(t, orgMem.Roles)
191+
assert.NotZero(t, orgMem.OrganizationMember)
192+
assert.NotEmpty(t, orgMem.OrganizationMember.CreatedAt)
193+
assert.NotEmpty(t, orgMem.OrganizationMember.UpdatedAt)
194+
assert.NotEmpty(t, orgMem.OrganizationMember.UserID)
195+
assert.NotEmpty(t, orgMem.OrganizationMember.Roles)
167196
})
168197
rtr.ServeHTTP(rw, r)
169198
res := rw.Result()

0 commit comments

Comments
 (0)