Skip to content

Commit 76b949c

Browse files
committed
coderd/rbac: update chat rbac
1 parent 7d24eed commit 76b949c

File tree

7 files changed

+143
-18
lines changed

7 files changed

+143
-18
lines changed

coderd/database/dbauthz/dbauthz.go

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1270,10 +1270,7 @@ func (q *querier) DeleteApplicationConnectAPIKeysByUserID(ctx context.Context, u
12701270
}
12711271

12721272
func (q *querier) DeleteChat(ctx context.Context, id uuid.UUID) error {
1273-
if err := q.authorizeContext(ctx, policy.ActionDelete, rbac.ResourceChat.WithID(id)); err != nil {
1274-
return err
1275-
}
1276-
return q.db.DeleteChat(ctx, id)
1273+
return deleteQ(q.log, q.auth, q.db.GetChatByID, q.db.DeleteChat)(ctx, id)
12771274
}
12781275

12791276
func (q *querier) DeleteCoordinator(ctx context.Context, id uuid.UUID) error {
@@ -1694,24 +1691,19 @@ func (q *querier) GetAuthorizationUserRoles(ctx context.Context, userID uuid.UUI
16941691
}
16951692

16961693
func (q *querier) GetChatByID(ctx context.Context, id uuid.UUID) (database.Chat, error) {
1697-
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceChat.WithID(id)); err != nil {
1698-
return database.Chat{}, err
1699-
}
1700-
return q.db.GetChatByID(ctx, id)
1694+
return fetch(q.log, q.auth, q.db.GetChatByID)(ctx, id)
17011695
}
17021696

17031697
func (q *querier) GetChatMessagesByChatID(ctx context.Context, chatID uuid.UUID) ([]database.ChatMessage, error) {
1704-
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceChat.WithID(chatID)); err != nil {
1698+
c, err := q.GetChatByID(ctx, chatID)
1699+
if err != nil {
17051700
return nil, err
17061701
}
1707-
return q.db.GetChatMessagesByChatID(ctx, chatID)
1702+
return q.db.GetChatMessagesByChatID(ctx, c.ID)
17081703
}
17091704

17101705
func (q *querier) GetChatsByOwnerID(ctx context.Context, ownerID uuid.UUID) ([]database.Chat, error) {
1711-
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceChat); err != nil {
1712-
return nil, err
1713-
}
1714-
return q.db.GetChatsByOwnerID(ctx, ownerID)
1706+
return fetchWithPostFilter(q.auth, policy.ActionRead, q.db.GetChatsByOwnerID)(ctx, ownerID)
17151707
}
17161708

17171709
func (q *querier) GetCoordinatorResumeTokenSigningKey(ctx context.Context) (string, error) {
@@ -3348,7 +3340,14 @@ func (q *querier) InsertChat(ctx context.Context, arg database.InsertChatParams)
33483340
}
33493341

33503342
func (q *querier) InsertChatMessages(ctx context.Context, arg database.InsertChatMessagesParams) ([]database.ChatMessage, error) {
3351-
return insert(q.log, q.auth, rbac.ResourceChat.WithID(arg.ChatID), q.db.InsertChatMessages)(ctx, arg)
3343+
c, err := q.db.GetChatByID(ctx, arg.ChatID)
3344+
if err != nil {
3345+
return nil, err
3346+
}
3347+
if err := q.authorizeContext(ctx, policy.ActionUpdate, c); err != nil {
3348+
return nil, err
3349+
}
3350+
return q.db.InsertChatMessages(ctx, arg)
33523351
}
33533352

33543353
func (q *querier) InsertCryptoKey(ctx context.Context, arg database.InsertCryptoKeyParams) (database.CryptoKey, error) {
@@ -4000,10 +3999,10 @@ func (q *querier) UpdateAPIKeyByID(ctx context.Context, arg database.UpdateAPIKe
40003999
}
40014000

40024001
func (q *querier) UpdateChatByID(ctx context.Context, arg database.UpdateChatByIDParams) error {
4003-
if err := q.authorizeContext(ctx, policy.ActionUpdate, rbac.ResourceChat.WithID(arg.ID)); err != nil {
4004-
return err
4002+
fetch := func(ctx context.Context, arg database.UpdateChatByIDParams) (database.Chat, error) {
4003+
return q.db.GetChatByID(ctx, arg.ID)
40054004
}
4006-
return q.db.UpdateChatByID(ctx, arg)
4005+
return update(q.log, q.auth, fetch, q.db.UpdateChatByID)(ctx, arg)
40074006
}
40084007

40094008
func (q *querier) UpdateCryptoKeyDeletesAt(ctx context.Context, arg database.UpdateCryptoKeyDeletesAtParams) (database.CryptoKey, error) {

coderd/database/dbauthz/dbauthz_test.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5307,3 +5307,76 @@ func (s *MethodTestSuite) TestResourcesProvisionerdserver() {
53075307
}).Asserts(rbac.ResourceWorkspaceAgentDevcontainers, policy.ActionCreate)
53085308
}))
53095309
}
5310+
5311+
func (s *MethodTestSuite) TestChat() {
5312+
createChat := func(t *testing.T, db database.Store) (database.User, database.Chat, database.ChatMessage) {
5313+
t.Helper()
5314+
5315+
usr := dbgen.User(t, db, database.User{})
5316+
chat := dbgen.Chat(s.T(), db, database.Chat{
5317+
OwnerID: usr.ID,
5318+
})
5319+
msg := dbgen.ChatMessage(s.T(), db, database.ChatMessage{
5320+
ChatID: chat.ID,
5321+
})
5322+
5323+
return usr, chat, msg
5324+
}
5325+
5326+
s.Run("DeleteChat", s.Subtest(func(db database.Store, check *expects) {
5327+
_, c, _ := createChat(s.T(), db)
5328+
check.Args(c.ID).Asserts(c, policy.ActionDelete)
5329+
}))
5330+
5331+
s.Run("GetChatByID", s.Subtest(func(db database.Store, check *expects) {
5332+
_, c, _ := createChat(s.T(), db)
5333+
check.Args(c.ID).Asserts(c, policy.ActionRead).Returns(c)
5334+
}))
5335+
5336+
s.Run("GetChatMessagesByChatID", s.Subtest(func(db database.Store, check *expects) {
5337+
_, c, m := createChat(s.T(), db)
5338+
check.Args(c.ID).Asserts(c, policy.ActionRead).Returns([]database.ChatMessage{m})
5339+
}))
5340+
5341+
s.Run("GetChatsByOwnerID", s.Subtest(func(db database.Store, check *expects) {
5342+
u1, u1c1, _ := createChat(s.T(), db)
5343+
u1c2 := dbgen.Chat(s.T(), db, database.Chat{
5344+
OwnerID: u1.ID,
5345+
})
5346+
_, _, _ = createChat(s.T(), db) // other user's chat
5347+
check.Args(u1.ID).Asserts(u1c2, policy.ActionRead, u1c1, policy.ActionRead).Returns([]database.Chat{u1c1, u1c2})
5348+
}))
5349+
5350+
s.Run("InsertChat", s.Subtest(func(db database.Store, check *expects) {
5351+
usr := dbgen.User(s.T(), db, database.User{})
5352+
check.Args(database.InsertChatParams{
5353+
OwnerID: usr.ID,
5354+
Title: "test chat",
5355+
CreatedAt: dbtime.Now(),
5356+
UpdatedAt: dbtime.Now(),
5357+
}).Asserts(rbac.ResourceChat.WithOwner(usr.ID.String()), policy.ActionCreate)
5358+
}))
5359+
5360+
s.Run("InsertChatMessages", s.Subtest(func(db database.Store, check *expects) {
5361+
usr := dbgen.User(s.T(), db, database.User{})
5362+
chat := dbgen.Chat(s.T(), db, database.Chat{
5363+
OwnerID: usr.ID,
5364+
})
5365+
check.Args(database.InsertChatMessagesParams{
5366+
ChatID: chat.ID,
5367+
CreatedAt: dbtime.Now(),
5368+
Model: "test-model",
5369+
Provider: "test-provider",
5370+
Content: []byte(`[]`),
5371+
}).Asserts(chat, policy.ActionUpdate)
5372+
}))
5373+
5374+
s.Run("UpdateChatByID", s.Subtest(func(db database.Store, check *expects) {
5375+
_, c, _ := createChat(s.T(), db)
5376+
check.Args(database.UpdateChatByIDParams{
5377+
ID: c.ID,
5378+
Title: "new title",
5379+
UpdatedAt: dbtime.Now(),
5380+
}).Asserts(c, policy.ActionUpdate)
5381+
}))
5382+
}

coderd/database/dbgen/dbgen.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,19 @@ func Chat(t testing.TB, db database.Store, seed database.Chat) database.Chat {
154154
return chat
155155
}
156156

157+
func ChatMessage(t testing.TB, db database.Store, seed database.ChatMessage) database.ChatMessage {
158+
msg, err := db.InsertChatMessages(genCtx, database.InsertChatMessagesParams{
159+
CreatedAt: takeFirst(seed.CreatedAt, dbtime.Now()),
160+
ChatID: takeFirst(seed.ChatID, uuid.New()),
161+
Model: takeFirst(seed.Model, "train"),
162+
Provider: takeFirst(seed.Provider, "thomas"),
163+
Content: takeFirstSlice(seed.Content, []byte(`[{"text": "Choo choo!"}]`)),
164+
})
165+
require.NoError(t, err, "insert chat message")
166+
require.Len(t, msg, 1, "insert one chat message did not return exactly one message")
167+
return msg[0]
168+
}
169+
157170
func WorkspaceAgentPortShare(t testing.TB, db database.Store, orig database.WorkspaceAgentPortShare) database.WorkspaceAgentPortShare {
158171
ps, err := db.UpsertWorkspaceAgentPortShare(genCtx, database.UpsertWorkspaceAgentPortShareParams{
159172
WorkspaceID: takeFirst(orig.WorkspaceID, uuid.New()),

coderd/database/dbmem/dbmem.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8487,6 +8487,8 @@ func (q *FakeQuerier) InsertChatMessages(ctx context.Context, arg database.Inser
84878487
Content: content,
84888488
})
84898489
}
8490+
8491+
q.chatMessages = append(q.chatMessages, messages...)
84908492
return messages, nil
84918493
}
84928494

coderd/database/modelmethods.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,3 +568,8 @@ func (m WorkspaceAgentVolumeResourceMonitor) Debounce(
568568

569569
return m.DebouncedUntil, false
570570
}
571+
572+
func (c Chat) RBACObject() rbac.Object {
573+
return rbac.ResourceChat.WithID(c.ID).
574+
WithOwner(c.OwnerID.String())
575+
}

coderd/rbac/roles.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,8 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
299299
ResourceOrganizationMember.Type: {policy.ActionRead},
300300
// Users can create provisioner daemons scoped to themselves.
301301
ResourceProvisionerDaemon.Type: {policy.ActionRead, policy.ActionCreate, policy.ActionRead, policy.ActionUpdate},
302+
// Users can create, read, update, and delete their own agentic chat messages.
303+
ResourceChat.Type: {policy.ActionCreate, policy.ActionRead, policy.ActionUpdate, policy.ActionDelete},
302304
})...,
303305
),
304306
}.withCachedRegoValue()

coderd/rbac/roles_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -831,6 +831,37 @@ func TestRolePermissions(t *testing.T) {
831831
},
832832
},
833833
},
834+
// Members may read their own chats.
835+
{
836+
Name: "CreateReadUpdateDeleteMyChats",
837+
Actions: []policy.Action{policy.ActionCreate, policy.ActionRead, policy.ActionUpdate, policy.ActionDelete},
838+
Resource: rbac.ResourceChat.WithOwner(currentUser.String()),
839+
AuthorizeMap: map[bool][]hasAuthSubjects{
840+
true: {memberMe, orgMemberMe, owner},
841+
false: {
842+
userAdmin, orgUserAdmin, templateAdmin,
843+
orgAuditor, orgTemplateAdmin,
844+
otherOrgMember, otherOrgAuditor, otherOrgUserAdmin, otherOrgTemplateAdmin,
845+
orgAdmin, otherOrgAdmin,
846+
},
847+
},
848+
},
849+
// Only owners can create, read, update, and delete other users' chats.
850+
{
851+
Name: "CreateReadUpdateDeleteOtherUserChats",
852+
Actions: []policy.Action{policy.ActionCreate, policy.ActionRead, policy.ActionUpdate, policy.ActionDelete},
853+
Resource: rbac.ResourceChat.WithOwner(uuid.NewString()), // some other user
854+
AuthorizeMap: map[bool][]hasAuthSubjects{
855+
true: {owner},
856+
false: {
857+
memberMe, orgMemberMe,
858+
userAdmin, orgUserAdmin, templateAdmin,
859+
orgAuditor, orgTemplateAdmin,
860+
otherOrgMember, otherOrgAuditor, otherOrgUserAdmin, otherOrgTemplateAdmin,
861+
orgAdmin, otherOrgAdmin,
862+
},
863+
},
864+
},
834865
}
835866

836867
// We expect every permission to be tested above.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy