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

Commited02070

Browse files
committed
chore: aibridge database & RBAC
1 parent994e223 commited02070

25 files changed

+651
-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: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,27 @@ 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,sessID uuid.UUID)error {
181+
act,ok:=ActorFromContext(ctx)
182+
if!ok {
183+
returnErrNoActor
184+
}
185+
186+
sess,err:=q.db.GetAIBridgeInterceptionByID(ctx,sessID)
187+
iferr!=nil {
188+
returnxerrors.Errorf("fetch aibridge session %q: %w",sessID,err)
189+
}
190+
191+
err=q.auth.Authorize(ctx,act,policy.ActionUpdate,sess.RBACObject())
192+
iferr!=nil {
193+
returnlogNotAuthorizedError(ctx,q.log,err)
194+
}
195+
196+
returnnil
197+
}
198+
178199
typeauthContextKeystruct{}
179200

180201
// ActorFromContext returns the authorization subject from the context.
@@ -542,6 +563,29 @@ var (
542563
}),
543564
Scope:rbac.ScopeAll,
544565
}.WithCachedASTValue()
566+
567+
// See aibridged package.
568+
subjectAibridged= rbac.Subject{
569+
Type:rbac.SubjectAibridged,
570+
FriendlyName:"AIBridge Daemon",
571+
ID:uuid.Nil.String(),
572+
Roles:rbac.Roles([]rbac.Role{
573+
{
574+
Identifier: rbac.RoleIdentifier{Name:"aibridged"},
575+
DisplayName:"AIBridge Daemon",
576+
Site:rbac.Permissions(map[string][]policy.Action{
577+
rbac.ResourceUser.Type: {
578+
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.
579+
},
580+
rbac.ResourceApiKey.Type: {policy.ActionRead},// Validate API keys.
581+
rbac.ResourceAibridgeInterception.Type: {policy.ActionCreate,policy.ActionRead,policy.ActionUpdate},
582+
}),
583+
Org:map[string][]rbac.Permission{},
584+
User: []rbac.Permission{},
585+
},
586+
}),
587+
Scope:rbac.ScopeAll,
588+
}.WithCachedASTValue()
545589
)
546590

547591
// AsProvisionerd returns a context with an actor that has permissions required
@@ -624,6 +668,12 @@ func AsUsagePublisher(ctx context.Context) context.Context {
624668
returnAs(ctx,subjectUsagePublisher)
625669
}
626670

671+
// AsAIBridged returns a context with an actor that has permissions
672+
// required for creating, reading, and updating aibridge-related resources.
673+
funcAsAIBridged(ctx context.Context) context.Context {
674+
returnAs(ctx,subjectAibridged)
675+
}
676+
627677
varAsRemoveActor= rbac.Subject{
628678
ID:"remove-actor",
629679
}
@@ -1878,6 +1928,10 @@ func (q *querier) FindMatchingPresetID(ctx context.Context, arg database.FindMat
18781928
returnq.db.FindMatchingPresetID(ctx,arg)
18791929
}
18801930

1931+
func (q*querier)GetAIBridgeInterceptionByID(ctx context.Context,id uuid.UUID) (database.AIBridgeInterception,error) {
1932+
returnfetch(q.log,q.auth,q.db.GetAIBridgeInterceptionByID)(ctx,id)
1933+
}
1934+
18811935
func (q*querier)GetAPIKeyByID(ctx context.Context,idstring) (database.APIKey,error) {
18821936
returnfetch(q.log,q.auth,q.db.GetAPIKeyByID)(ctx,id)
18831937
}
@@ -3757,6 +3811,34 @@ func (q *querier) GetWorkspacesEligibleForTransition(ctx context.Context, now ti
37573811
returnq.db.GetWorkspacesEligibleForTransition(ctx,now)
37583812
}
37593813

3814+
func (q*querier)InsertAIBridgeInterception(ctx context.Context,arg database.InsertAIBridgeInterceptionParams) (database.AIBridgeInterception,error) {
3815+
returninsert(q.log,q.auth,rbac.ResourceAibridgeInterception.WithOwner(arg.InitiatorID.String()),q.db.InsertAIBridgeInterception)(ctx,arg)
3816+
}
3817+
3818+
func (q*querier)InsertAIBridgeTokenUsage(ctx context.Context,arg database.InsertAIBridgeTokenUsageParams)error {
3819+
// All aibridge_token_usages records belong to the initiator of their associated session.
3820+
iferr:=q.authorizeAIBridgeInterceptionUpdate(ctx,arg.InterceptionID);err!=nil {
3821+
returnerr
3822+
}
3823+
returnq.db.InsertAIBridgeTokenUsage(ctx,arg)
3824+
}
3825+
3826+
func (q*querier)InsertAIBridgeToolUsage(ctx context.Context,arg database.InsertAIBridgeToolUsageParams)error {
3827+
// All aibridge_tool_usages records belong to the initiator of their associated session.
3828+
iferr:=q.authorizeAIBridgeInterceptionUpdate(ctx,arg.InterceptionID);err!=nil {
3829+
returnerr
3830+
}
3831+
returnq.db.InsertAIBridgeToolUsage(ctx,arg)
3832+
}
3833+
3834+
func (q*querier)InsertAIBridgeUserPrompt(ctx context.Context,arg database.InsertAIBridgeUserPromptParams)error {
3835+
// All aibridge_user_prompts records belong to the initiator of their associated session.
3836+
iferr:=q.authorizeAIBridgeInterceptionUpdate(ctx,arg.InterceptionID);err!=nil {
3837+
returnerr
3838+
}
3839+
returnq.db.InsertAIBridgeUserPrompt(ctx,arg)
3840+
}
3841+
37603842
func (q*querier)InsertAPIKey(ctx context.Context,arg database.InsertAPIKeyParams) (database.APIKey,error) {
37613843
// TODO(Cian): ideally this would be encoded in the policy, but system users are just members and we
37623844
// 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