Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit422bba4

Browse files
authored
chore: add aibridge database resources & define RBAC policies (#19796)
Closescoder/internal#986
1 parent348a2e0 commit422bba4

26 files changed

+770
-0
lines changed

‎coderd/apidoc/docs.go‎

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/apidoc/swagger.json‎

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/database/dbauthz/dbauthz.go‎

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,22 @@ func (q *querier) authorizePrebuiltWorkspace(ctx context.Context, action policy.
175175
returnxerrors.Errorf("authorize context: %w",workspaceErr)
176176
}
177177

178+
// authorizeAIBridgeInterceptionUpdate validates that the context's actor matches the initiator of the AIBridgeInterception.
179+
// This is used by all of the sub-resources which fall under the [ResourceAibridgeInterception] umbrella.
180+
func (q*querier)authorizeAIBridgeInterceptionUpdate(ctx context.Context,interceptionID uuid.UUID)error {
181+
inter,err:=q.db.GetAIBridgeInterceptionByID(ctx,interceptionID)
182+
iferr!=nil {
183+
returnxerrors.Errorf("fetch aibridge interception %q: %w",interceptionID,err)
184+
}
185+
186+
err=q.authorizeContext(ctx,policy.ActionUpdate,inter.RBACObject())
187+
iferr!=nil {
188+
returnerr
189+
}
190+
191+
returnnil
192+
}
193+
178194
typeauthContextKeystruct{}
179195

180196
// ActorFromContext returns the authorization subject from the context.
@@ -542,6 +558,29 @@ var (
542558
}),
543559
Scope:rbac.ScopeAll,
544560
}.WithCachedASTValue()
561+
562+
// See aibridged package.
563+
subjectAibridged= rbac.Subject{
564+
Type:rbac.SubjectAibridged,
565+
FriendlyName:"AIBridge Daemon",
566+
ID:uuid.Nil.String(),
567+
Roles:rbac.Roles([]rbac.Role{
568+
{
569+
Identifier: rbac.RoleIdentifier{Name:"aibridged"},
570+
DisplayName:"AIBridge Daemon",
571+
Site:rbac.Permissions(map[string][]policy.Action{
572+
rbac.ResourceUser.Type: {
573+
policy.ActionReadPersonal,// Required to read users' external auth links. // TODO: this is too broad; reduce scope to just external_auth_links by creating separate resource.
574+
},
575+
rbac.ResourceApiKey.Type: {policy.ActionRead},// Validate API keys.
576+
rbac.ResourceAibridgeInterception.Type: {policy.ActionCreate,policy.ActionRead,policy.ActionUpdate},
577+
}),
578+
Org:map[string][]rbac.Permission{},
579+
User: []rbac.Permission{},
580+
},
581+
}),
582+
Scope:rbac.ScopeAll,
583+
}.WithCachedASTValue()
545584
)
546585

547586
// AsProvisionerd returns a context with an actor that has permissions required
@@ -624,6 +663,12 @@ func AsUsagePublisher(ctx context.Context) context.Context {
624663
returnAs(ctx,subjectUsagePublisher)
625664
}
626665

666+
// AsAIBridged returns a context with an actor that has permissions
667+
// required for creating, reading, and updating aibridge-related resources.
668+
funcAsAIBridged(ctx context.Context) context.Context {
669+
returnAs(ctx,subjectAibridged)
670+
}
671+
627672
varAsRemoveActor= rbac.Subject{
628673
ID:"remove-actor",
629674
}
@@ -1878,6 +1923,10 @@ func (q *querier) FindMatchingPresetID(ctx context.Context, arg database.FindMat
18781923
returnq.db.FindMatchingPresetID(ctx,arg)
18791924
}
18801925

1926+
func (q*querier)GetAIBridgeInterceptionByID(ctx context.Context,id uuid.UUID) (database.AIBridgeInterception,error) {
1927+
returnfetch(q.log,q.auth,q.db.GetAIBridgeInterceptionByID)(ctx,id)
1928+
}
1929+
18811930
func (q*querier)GetAPIKeyByID(ctx context.Context,idstring) (database.APIKey,error) {
18821931
returnfetch(q.log,q.auth,q.db.GetAPIKeyByID)(ctx,id)
18831932
}
@@ -3757,6 +3806,34 @@ func (q *querier) GetWorkspacesEligibleForTransition(ctx context.Context, now ti
37573806
returnq.db.GetWorkspacesEligibleForTransition(ctx,now)
37583807
}
37593808

3809+
func (q*querier)InsertAIBridgeInterception(ctx context.Context,arg database.InsertAIBridgeInterceptionParams) (database.AIBridgeInterception,error) {
3810+
returninsert(q.log,q.auth,rbac.ResourceAibridgeInterception.WithOwner(arg.InitiatorID.String()),q.db.InsertAIBridgeInterception)(ctx,arg)
3811+
}
3812+
3813+
func (q*querier)InsertAIBridgeTokenUsage(ctx context.Context,arg database.InsertAIBridgeTokenUsageParams)error {
3814+
// All aibridge_token_usages records belong to the initiator of their associated interception.
3815+
iferr:=q.authorizeAIBridgeInterceptionUpdate(ctx,arg.InterceptionID);err!=nil {
3816+
returnerr
3817+
}
3818+
returnq.db.InsertAIBridgeTokenUsage(ctx,arg)
3819+
}
3820+
3821+
func (q*querier)InsertAIBridgeToolUsage(ctx context.Context,arg database.InsertAIBridgeToolUsageParams)error {
3822+
// All aibridge_tool_usages records belong to the initiator of their associated interception.
3823+
iferr:=q.authorizeAIBridgeInterceptionUpdate(ctx,arg.InterceptionID);err!=nil {
3824+
returnerr
3825+
}
3826+
returnq.db.InsertAIBridgeToolUsage(ctx,arg)
3827+
}
3828+
3829+
func (q*querier)InsertAIBridgeUserPrompt(ctx context.Context,arg database.InsertAIBridgeUserPromptParams)error {
3830+
// All aibridge_user_prompts records belong to the initiator of their associated interception.
3831+
iferr:=q.authorizeAIBridgeInterceptionUpdate(ctx,arg.InterceptionID);err!=nil {
3832+
returnerr
3833+
}
3834+
returnq.db.InsertAIBridgeUserPrompt(ctx,arg)
3835+
}
3836+
37603837
func (q*querier)InsertAPIKey(ctx context.Context,arg database.InsertAPIKeyParams) (database.APIKey,error) {
37613838
// TODO(Cian): ideally this would be encoded in the policy, but system users are just members and we
37623839
// don't currently have a capability to conditionally deny creating resources by owner ID in a role.

‎coderd/database/dbauthz/dbauthz_test.go‎

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4332,3 +4332,55 @@ func TestInsertAPIKey_AsPrebuildsUser(t *testing.T) {
43324332
_,err:=dbz.InsertAPIKey(ctx,testutil.Fake(t,faker, database.InsertAPIKeyParams{}))
43334333
require.True(t,dbauthz.IsNotAuthorizedError(err))
43344334
}
4335+
4336+
func (s*MethodTestSuite)TestAIBridge() {
4337+
s.Run("GetAIBridgeInterceptionByID",s.Mocked(func(db*dbmock.MockStore,faker*gofakeit.Faker,check*expects) {
4338+
sessID:= uuid.UUID{2}
4339+
sess:=testutil.Fake(s.T(),faker, database.AIBridgeInterception{ID:sessID})
4340+
db.EXPECT().GetAIBridgeInterceptionByID(gomock.Any(),sessID).Return(sess,nil).AnyTimes()
4341+
check.Args(sessID).Asserts(sess,policy.ActionRead).Returns(sess)
4342+
}))
4343+
4344+
s.Run("InsertAIBridgeInterception",s.Mocked(func(db*dbmock.MockStore,faker*gofakeit.Faker,check*expects) {
4345+
initID:= uuid.UUID{3}
4346+
user:=testutil.Fake(s.T(),faker, database.User{ID:initID})
4347+
// testutil.Fake cannot distinguish between a zero value and an explicitly requested value which is equivalent.
4348+
user.IsSystem=false
4349+
user.Deleted=false
4350+
4351+
sessID:= uuid.UUID{2}
4352+
sess:=testutil.Fake(s.T(),faker, database.AIBridgeInterception{ID:sessID,InitiatorID:initID})
4353+
4354+
params:= database.InsertAIBridgeInterceptionParams{ID:sess.ID,InitiatorID:sess.InitiatorID,Provider:sess.Provider,Model:sess.Model}
4355+
db.EXPECT().GetUserByID(gomock.Any(),initID).Return(user,nil).AnyTimes()// Validation.
4356+
db.EXPECT().InsertAIBridgeInterception(gomock.Any(),params).Return(sess,nil).AnyTimes()
4357+
check.Args(params).Asserts(sess,policy.ActionCreate)
4358+
}))
4359+
4360+
s.Run("InsertAIBridgeTokenUsage",s.Mocked(func(db*dbmock.MockStore,faker*gofakeit.Faker,check*expects) {
4361+
sessID:= uuid.UUID{2}
4362+
sess:=testutil.Fake(s.T(),faker, database.AIBridgeInterception{ID:sessID})
4363+
params:= database.InsertAIBridgeTokenUsageParams{InterceptionID:sess.ID}
4364+
db.EXPECT().GetAIBridgeInterceptionByID(gomock.Any(),sessID).Return(sess,nil).AnyTimes()// Validation.
4365+
db.EXPECT().InsertAIBridgeTokenUsage(gomock.Any(),params).Return(nil).AnyTimes()
4366+
check.Args(params).Asserts(sess,policy.ActionUpdate)
4367+
}))
4368+
4369+
s.Run("InsertAIBridgeToolUsage",s.Mocked(func(db*dbmock.MockStore,faker*gofakeit.Faker,check*expects) {
4370+
sessID:= uuid.UUID{2}
4371+
sess:=testutil.Fake(s.T(),faker, database.AIBridgeInterception{ID:sessID})
4372+
params:= database.InsertAIBridgeToolUsageParams{InterceptionID:sess.ID}
4373+
db.EXPECT().GetAIBridgeInterceptionByID(gomock.Any(),sessID).Return(sess,nil).AnyTimes()// Validation.
4374+
db.EXPECT().InsertAIBridgeToolUsage(gomock.Any(),params).Return(nil).AnyTimes()
4375+
check.Args(params).Asserts(sess,policy.ActionUpdate)
4376+
}))
4377+
4378+
s.Run("InsertAIBridgeUserPrompt",s.Mocked(func(db*dbmock.MockStore,faker*gofakeit.Faker,check*expects) {
4379+
sessID:= uuid.UUID{2}
4380+
sess:=testutil.Fake(s.T(),faker, database.AIBridgeInterception{ID:sessID})
4381+
params:= database.InsertAIBridgeUserPromptParams{InterceptionID:sess.ID}
4382+
db.EXPECT().GetAIBridgeInterceptionByID(gomock.Any(),sessID).Return(sess,nil).AnyTimes()// Validation.
4383+
db.EXPECT().InsertAIBridgeUserPrompt(gomock.Any(),params).Return(nil).AnyTimes()
4384+
check.Args(params).Asserts(sess,policy.ActionUpdate)
4385+
}))
4386+
}

‎coderd/database/dbmetrics/querymetrics.go‎

Lines changed: 35 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/database/dbmock/dbmock.go‎

Lines changed: 72 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp