fix: api horizontal privilege (#2434)
This commit is contained in:
@ -72,6 +72,8 @@ import (
|
||||
"github.com/coze-dev/coze-studio/backend/crossdomain/knowledge/knowledgemock"
|
||||
knowledge "github.com/coze-dev/coze-studio/backend/crossdomain/knowledge/model"
|
||||
"github.com/coze-dev/coze-studio/backend/crossdomain/message/messagemock"
|
||||
crosspermission "github.com/coze-dev/coze-studio/backend/crossdomain/permission"
|
||||
"github.com/coze-dev/coze-studio/backend/crossdomain/permission/permissionmock"
|
||||
crossplugin "github.com/coze-dev/coze-studio/backend/crossdomain/plugin"
|
||||
pluginImpl "github.com/coze-dev/coze-studio/backend/crossdomain/plugin/impl"
|
||||
pluginmodel "github.com/coze-dev/coze-studio/backend/crossdomain/plugin/model"
|
||||
@ -81,6 +83,7 @@ import (
|
||||
conventity "github.com/coze-dev/coze-studio/backend/domain/conversation/conversation/entity"
|
||||
entity4 "github.com/coze-dev/coze-studio/backend/domain/memory/database/entity"
|
||||
entity2 "github.com/coze-dev/coze-studio/backend/domain/openauth/openapiauth/entity"
|
||||
permission "github.com/coze-dev/coze-studio/backend/domain/permission"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/plugin/dto"
|
||||
entity3 "github.com/coze-dev/coze-studio/backend/domain/plugin/entity"
|
||||
entity5 "github.com/coze-dev/coze-studio/backend/domain/plugin/entity"
|
||||
@ -181,6 +184,11 @@ func newWfTestRunner(t *testing.T) *wfTestRunner {
|
||||
ctxcache.Store(c, consts.SessionDataKeyInCtx, &userentity.Session{
|
||||
UserID: 123,
|
||||
})
|
||||
// Add API auth info for OpenAPI endpoints
|
||||
ctxcache.Store(c, consts.OpenapiAuthKeyInCtx, &entity2.ApiKey{
|
||||
UserID: 123,
|
||||
ConnectorID: consts.APIConnectorID,
|
||||
})
|
||||
ctx.Next(c)
|
||||
})
|
||||
h.POST("/api/workflow_api/node_template_list", NodeTemplateList)
|
||||
@ -330,6 +338,11 @@ func newWfTestRunner(t *testing.T) *wfTestRunner {
|
||||
mockAgentRun := agentrunmock.NewMockAgentRun(ctrl)
|
||||
crossagentrun.SetDefaultSVC(mockAgentRun)
|
||||
|
||||
// Initialize permission service for tests
|
||||
mockPermission := permissionmock.NewMockPermission(ctrl)
|
||||
mockPermission.EXPECT().CheckAuthz(gomock.Any(), gomock.Any()).Return(&permission.CheckAuthzResult{Decision: permission.Allow}, nil).AnyTimes()
|
||||
crosspermission.SetDefaultSVC(mockPermission)
|
||||
|
||||
mockey.Mock((*user.UserApplicationService).MGetUserBasicInfo).Return(&playground.MGetUserBasicInfoResponse{
|
||||
UserBasicInfoMap: make(map[string]*playground.UserBasicInfo),
|
||||
}, nil).Build()
|
||||
@ -1029,6 +1042,8 @@ func (r *wfTestRunner) openapiStream(id string, input any) *sse.Reader {
|
||||
hReq.SetMethod("POST")
|
||||
hReq.SetBody(m)
|
||||
hReq.SetHeader("Content-Type", "application/json")
|
||||
// Add Authorization header for API authentication
|
||||
hReq.SetHeader("Authorization", "Bearer test-api-key-123")
|
||||
err = c.Do(context.Background(), hReq, hResp)
|
||||
assert.NoError(r.t, err)
|
||||
|
||||
@ -1059,6 +1074,8 @@ func (r *wfTestRunner) openapiResume(id string, eventID string, resumeData strin
|
||||
hReq.SetMethod("POST")
|
||||
hReq.SetBody(m)
|
||||
hReq.SetHeader("Content-Type", "application/json")
|
||||
// Add Authorization header for API authentication
|
||||
hReq.SetHeader("Authorization", "Bearer test-api-key-123")
|
||||
err = c.Do(context.Background(), hReq, hResp)
|
||||
assert.NoError(r.t, err)
|
||||
|
||||
|
||||
@ -45,6 +45,7 @@ import (
|
||||
"github.com/coze-dev/coze-studio/backend/application/workflow"
|
||||
"github.com/coze-dev/coze-studio/backend/bizpkg/config"
|
||||
connectorModel "github.com/coze-dev/coze-studio/backend/crossdomain/connector/model"
|
||||
|
||||
knowledgeModel "github.com/coze-dev/coze-studio/backend/crossdomain/knowledge/model"
|
||||
pluginConsts "github.com/coze-dev/coze-studio/backend/crossdomain/plugin/consts"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/app/entity"
|
||||
@ -52,6 +53,7 @@ import (
|
||||
"github.com/coze-dev/coze-studio/backend/domain/app/service"
|
||||
connector "github.com/coze-dev/coze-studio/backend/domain/connector/service"
|
||||
variables "github.com/coze-dev/coze-studio/backend/domain/memory/variables/service"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/permission"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/plugin/dto"
|
||||
searchEntity "github.com/coze-dev/coze-studio/backend/domain/search/entity"
|
||||
search "github.com/coze-dev/coze-studio/backend/domain/search/service"
|
||||
@ -389,7 +391,37 @@ func (a *APPApplicationService) getLatestPublishRecord(ctx context.Context, appI
|
||||
return latestRecord, nil
|
||||
}
|
||||
|
||||
func checkUserSpace(ctx context.Context, uid int64, spaceID int64) error {
|
||||
// Use permission service to check workspace access
|
||||
result, err := permission.DefaultSVC().CheckAuthz(ctx, &permission.CheckAuthzData{
|
||||
ResourceIdentifier: []*permission.ResourceIdentifier{
|
||||
{
|
||||
Type: permission.ResourceTypeWorkspace,
|
||||
ID: []int64{spaceID},
|
||||
Action: permission.ActionRead,
|
||||
},
|
||||
},
|
||||
OperatorID: uid,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check workspace permission: %w", err)
|
||||
}
|
||||
|
||||
if result.Decision != permission.Allow {
|
||||
return fmt.Errorf("user %d does not have access to space %d", uid, spaceID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *APPApplicationService) ReportUserBehavior(ctx context.Context, req *playground.ReportUserBehaviorRequest) (resp *playground.ReportUserBehaviorResponse, err error) {
|
||||
uid := ctxutil.MustGetUIDFromCtx(ctx)
|
||||
|
||||
err = checkUserSpace(ctx, uid, req.GetSpaceID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = a.projectEventBus.PublishProject(ctx, &searchEntity.ProjectDomainEvent{
|
||||
OpType: searchEntity.Updated,
|
||||
Project: &searchEntity.ProjectDocument{
|
||||
|
||||
@ -20,6 +20,8 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/coze-dev/coze-studio/backend/application/permission"
|
||||
|
||||
"github.com/coze-dev/coze-studio/backend/application/app"
|
||||
"github.com/coze-dev/coze-studio/backend/application/base/appinfra"
|
||||
"github.com/coze-dev/coze-studio/backend/application/connector"
|
||||
@ -41,6 +43,8 @@ import (
|
||||
singleagentImpl "github.com/coze-dev/coze-studio/backend/crossdomain/agent/impl"
|
||||
crossagentrun "github.com/coze-dev/coze-studio/backend/crossdomain/agentrun"
|
||||
agentrunImpl "github.com/coze-dev/coze-studio/backend/crossdomain/agentrun/impl"
|
||||
crossapp "github.com/coze-dev/coze-studio/backend/crossdomain/app"
|
||||
appImpl "github.com/coze-dev/coze-studio/backend/crossdomain/app/impl"
|
||||
crossconnector "github.com/coze-dev/coze-studio/backend/crossdomain/connector"
|
||||
connectorImpl "github.com/coze-dev/coze-studio/backend/crossdomain/connector/impl"
|
||||
crossconversation "github.com/coze-dev/coze-studio/backend/crossdomain/conversation"
|
||||
@ -53,6 +57,8 @@ import (
|
||||
knowledgeImpl "github.com/coze-dev/coze-studio/backend/crossdomain/knowledge/impl"
|
||||
crossmessage "github.com/coze-dev/coze-studio/backend/crossdomain/message"
|
||||
messageImpl "github.com/coze-dev/coze-studio/backend/crossdomain/message/impl"
|
||||
crosspermission "github.com/coze-dev/coze-studio/backend/crossdomain/permission"
|
||||
permissionImpl "github.com/coze-dev/coze-studio/backend/crossdomain/permission/impl"
|
||||
crossplugin "github.com/coze-dev/coze-studio/backend/crossdomain/plugin"
|
||||
pluginImpl "github.com/coze-dev/coze-studio/backend/crossdomain/plugin/impl"
|
||||
crosssearch "github.com/coze-dev/coze-studio/backend/crossdomain/search"
|
||||
@ -90,6 +96,8 @@ type basicServices struct {
|
||||
templateSVC *template.ApplicationService
|
||||
openAuthSVC *openauth.OpenAuthApplicationService
|
||||
uploadSVC *upload.UploadService
|
||||
|
||||
permissionSVC *permission.PermissionApplicationService
|
||||
}
|
||||
|
||||
type primaryServices struct {
|
||||
@ -101,6 +109,7 @@ type primaryServices struct {
|
||||
knowledgeSVC *knowledge.KnowledgeApplicationService
|
||||
workflowSVC *workflow.ApplicationService
|
||||
shortcutSVC *shortcutcmd.ShortcutCmdApplicationService
|
||||
appSVC *app.APPApplicationService
|
||||
}
|
||||
|
||||
type complexServices struct {
|
||||
@ -138,6 +147,9 @@ func Init(ctx context.Context) (err error) {
|
||||
return fmt.Errorf("Init - initVitalServices failed, err: %v", err)
|
||||
}
|
||||
|
||||
// Initialize permission service first as it's required by other services
|
||||
crosspermission.SetDefaultSVC(permissionImpl.InitDomainService(basicServices.permissionSVC.DomainSVC))
|
||||
|
||||
crossconnector.SetDefaultSVC(connectorImpl.InitDomainService(basicServices.connectorSVC.DomainSVC))
|
||||
crossdatabase.SetDefaultSVC(databaseImpl.InitDomainService(primaryServices.memorySVC.DatabaseDomainSVC))
|
||||
crossknowledge.SetDefaultSVC(knowledgeImpl.InitDomainService(primaryServices.knowledgeSVC.DomainSVC))
|
||||
@ -153,6 +165,8 @@ func Init(ctx context.Context) (err error) {
|
||||
crosssearch.SetDefaultSVC(searchImpl.InitDomainService(complexServices.searchSVC.DomainSVC))
|
||||
crossupload.SetDefaultSVC(uploadImpl.InitDomainService(basicServices.uploadSVC.UploadSVC))
|
||||
|
||||
crossapp.SetDefaultSVC(appImpl.InitDomainService(complexServices.appSVC.DomainSVC))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -179,6 +193,8 @@ func initBasicServices(ctx context.Context, infra *appinfra.AppDependencies, e *
|
||||
Storage: infra.OSS,
|
||||
})
|
||||
|
||||
permissionSVC := permission.InitService(&permission.ServiceComponents{})
|
||||
|
||||
return &basicServices{
|
||||
infra: infra,
|
||||
eventbus: e,
|
||||
@ -189,6 +205,8 @@ func initBasicServices(ctx context.Context, infra *appinfra.AppDependencies, e *
|
||||
templateSVC: templateSVC,
|
||||
openAuthSVC: openAuthSVC,
|
||||
uploadSVC: uploadSVC,
|
||||
|
||||
permissionSVC: permissionSVC,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@ -91,6 +91,9 @@ func (c *ConversationApplicationService) Run(ctx context.Context, sseSender *sse
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cmdMeta.ObjectID > 0 && cmdMeta.ObjectID != agentInfo.AgentID {
|
||||
return errorx.New(errno.ErrConversationPermissionCode, errorx.KV("msg", "agent not match"))
|
||||
}
|
||||
shortcutCmd = cmdMeta
|
||||
}
|
||||
|
||||
|
||||
@ -322,8 +322,24 @@ func (c *ConversationApplicationService) DeleteMessage(ctx context.Context, mr *
|
||||
|
||||
func (c *ConversationApplicationService) BreakMessage(ctx context.Context, mr *message.BreakMessageRequest) (*message.BreakMessageResponse, error) {
|
||||
resp := new(message.BreakMessageResponse)
|
||||
messageInfo, err := c.MessageDomainSVC.GetByID(ctx, mr.GetAnswerMessageID())
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
if messageInfo == nil {
|
||||
return resp, errorx.New(errno.ErrConversationMessageNotFound)
|
||||
}
|
||||
|
||||
err := c.MessageDomainSVC.Broken(ctx, &entity.BrokenMeta{
|
||||
userID := ctxutil.GetUIDFromCtx(ctx)
|
||||
if messageInfo.UserID != conv.Int64ToStr(*userID) {
|
||||
return resp, errorx.New(errno.ErrConversationPermissionCode, errorx.KV("msg", "permission denied"))
|
||||
}
|
||||
|
||||
if messageInfo.ConversationID != mr.ConversationID {
|
||||
return resp, errorx.New(errno.ErrConversationPermissionCode, errorx.KV("msg", "conversation not match"))
|
||||
}
|
||||
|
||||
err = c.MessageDomainSVC.Broken(ctx, &entity.BrokenMeta{
|
||||
ID: *mr.AnswerMessageID,
|
||||
Position: mr.BrokenPos,
|
||||
})
|
||||
|
||||
@ -63,12 +63,20 @@ func (a *OpenapiAgentRunApplication) OpenapiAgentRun(ctx context.Context, sseSen
|
||||
return caErr
|
||||
}
|
||||
|
||||
if creatorID != agentInfo.CreatorID {
|
||||
return errorx.New(errno.ErrConversationPermissionCode, errorx.KV("msg", "agent not match"))
|
||||
}
|
||||
|
||||
conversationData, ccErr := a.checkConversation(ctx, ar, creatorID, connectorID)
|
||||
if ccErr != nil {
|
||||
logs.CtxErrorf(ctx, "checkConversation err:%v", ccErr)
|
||||
return ccErr
|
||||
}
|
||||
|
||||
if conversationData.CreatorID != creatorID {
|
||||
return errorx.New(errno.ErrConversationPermissionCode, errorx.KV("msg", "user not match"))
|
||||
}
|
||||
|
||||
spaceID := agentInfo.SpaceID
|
||||
arr, err := a.buildAgentRunRequest(ctx, ar, connectorID, spaceID, conversationData)
|
||||
if err != nil {
|
||||
@ -643,6 +651,10 @@ func (a *OpenapiAgentRunApplication) CancelRun(ctx context.Context, req *run.Can
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if req.ConversationID != runRecord.ConversationID {
|
||||
return nil, errorx.New(errno.ErrConversationPermissionCode, errorx.KV("msg", "conversation not match"))
|
||||
}
|
||||
|
||||
if userID != conversationData.CreatorID {
|
||||
return nil, errorx.New(errno.ErrConversationPermissionCode, errorx.KV("msg", "user not match"))
|
||||
}
|
||||
|
||||
@ -159,8 +159,9 @@ func TestOpenapiAgentRun_Success(t *testing.T) {
|
||||
// Mock agent check
|
||||
mockAgent := &saEntity.SingleAgent{
|
||||
SingleAgent: &singleagent.SingleAgent{
|
||||
AgentID: 67890,
|
||||
SpaceID: 54321,
|
||||
AgentID: 67890,
|
||||
SpaceID: 54321,
|
||||
CreatorID: 12345,
|
||||
},
|
||||
}
|
||||
mockSingleAgent.EXPECT().ObtainAgentByIdentity(ctx, gomock.Any()).Return(mockAgent, nil)
|
||||
@ -217,8 +218,9 @@ func TestOpenapiAgentRun_CheckConversationError(t *testing.T) {
|
||||
// Mock agent check success
|
||||
mockAgent := &saEntity.SingleAgent{
|
||||
SingleAgent: &singleagent.SingleAgent{
|
||||
AgentID: 67890,
|
||||
SpaceID: 54321,
|
||||
AgentID: 67890,
|
||||
SpaceID: 54321,
|
||||
CreatorID: 12345,
|
||||
},
|
||||
}
|
||||
mockSingleAgent.EXPECT().ObtainAgentByIdentity(ctx, gomock.Any()).Return(mockAgent, nil)
|
||||
@ -240,8 +242,9 @@ func TestOpenapiAgentRun_ConversationPermissionError(t *testing.T) {
|
||||
// Mock agent check success
|
||||
mockAgent := &saEntity.SingleAgent{
|
||||
SingleAgent: &singleagent.SingleAgent{
|
||||
AgentID: 67890,
|
||||
SpaceID: 54321,
|
||||
AgentID: 67890,
|
||||
SpaceID: 54321,
|
||||
CreatorID: 12345,
|
||||
},
|
||||
}
|
||||
mockSingleAgent.EXPECT().ObtainAgentByIdentity(ctx, gomock.Any()).Return(mockAgent, nil)
|
||||
@ -268,8 +271,9 @@ func TestOpenapiAgentRun_CreateNewConversation(t *testing.T) {
|
||||
// Mock agent check success
|
||||
mockAgent := &saEntity.SingleAgent{
|
||||
SingleAgent: &singleagent.SingleAgent{
|
||||
AgentID: 67890,
|
||||
SpaceID: 54321,
|
||||
AgentID: 67890,
|
||||
SpaceID: 54321,
|
||||
CreatorID: 12345,
|
||||
},
|
||||
}
|
||||
mockSingleAgent.EXPECT().ObtainAgentByIdentity(ctx, gomock.Any()).Return(mockAgent, nil)
|
||||
@ -305,7 +309,8 @@ func TestOpenapiAgentRun_AgentRunError(t *testing.T) {
|
||||
mockAgent := &saEntity.SingleAgent{
|
||||
SingleAgent: &singleagent.SingleAgent{
|
||||
AgentID: 67890,
|
||||
SpaceID: 54321,
|
||||
SpaceID: 54321,
|
||||
CreatorID: 12345,
|
||||
},
|
||||
}
|
||||
mockSingleAgent.EXPECT().ObtainAgentByIdentity(ctx, gomock.Any()).Return(mockAgent, nil)
|
||||
@ -339,8 +344,9 @@ func TestOpenapiAgentRun_WithShortcutCommand(t *testing.T) {
|
||||
// Mock agent check success
|
||||
mockAgent := &saEntity.SingleAgent{
|
||||
SingleAgent: &singleagent.SingleAgent{
|
||||
AgentID: 67890,
|
||||
SpaceID: 54321,
|
||||
AgentID: 67890,
|
||||
SpaceID: 54321,
|
||||
CreatorID: 12345,
|
||||
},
|
||||
}
|
||||
mockSingleAgent.EXPECT().ObtainAgentByIdentity(ctx, gomock.Any()).Return(mockAgent, nil)
|
||||
@ -379,8 +385,9 @@ func TestOpenapiAgentRun_WithMultipleMessages(t *testing.T) {
|
||||
// Mock agent check success
|
||||
mockAgent := &saEntity.SingleAgent{
|
||||
SingleAgent: &singleagent.SingleAgent{
|
||||
AgentID: 67890,
|
||||
SpaceID: 54321,
|
||||
AgentID: 67890,
|
||||
SpaceID: 54321,
|
||||
CreatorID: 12345,
|
||||
},
|
||||
}
|
||||
mockSingleAgent.EXPECT().ObtainAgentByIdentity(ctx, gomock.Any()).Return(mockAgent, nil)
|
||||
@ -421,8 +428,9 @@ func TestOpenapiAgentRun_WithAssistantMessage(t *testing.T) {
|
||||
// Mock agent check success
|
||||
mockAgent := &saEntity.SingleAgent{
|
||||
SingleAgent: &singleagent.SingleAgent{
|
||||
AgentID: 67890,
|
||||
SpaceID: 54321,
|
||||
AgentID: 67890,
|
||||
SpaceID: 54321,
|
||||
CreatorID: 12345,
|
||||
},
|
||||
}
|
||||
mockSingleAgent.EXPECT().ObtainAgentByIdentity(ctx, gomock.Any()).Return(mockAgent, nil)
|
||||
@ -490,8 +498,9 @@ func TestOpenapiAgentRun_WithMixedContentTypes(t *testing.T) {
|
||||
// Mock agent check success
|
||||
mockAgent := &saEntity.SingleAgent{
|
||||
SingleAgent: &singleagent.SingleAgent{
|
||||
AgentID: 67890,
|
||||
SpaceID: 54321,
|
||||
AgentID: 67890,
|
||||
SpaceID: 54321,
|
||||
CreatorID: 12345,
|
||||
},
|
||||
}
|
||||
mockSingleAgent.EXPECT().ObtainAgentByIdentity(ctx, gomock.Any()).Return(mockAgent, nil)
|
||||
@ -554,8 +563,9 @@ func TestOpenapiAgentRun_ParseAdditionalMessages_InvalidRole(t *testing.T) {
|
||||
// Mock agent check success
|
||||
mockAgent := &saEntity.SingleAgent{
|
||||
SingleAgent: &singleagent.SingleAgent{
|
||||
AgentID: 67890,
|
||||
SpaceID: 54321,
|
||||
AgentID: 67890,
|
||||
SpaceID: 54321,
|
||||
CreatorID: 12345,
|
||||
},
|
||||
}
|
||||
mockSingleAgent.EXPECT().ObtainAgentByIdentity(ctx, gomock.Any()).Return(mockAgent, nil)
|
||||
@ -597,8 +607,9 @@ func TestOpenapiAgentRun_ParseAdditionalMessages_InvalidType(t *testing.T) {
|
||||
// Mock agent check success
|
||||
mockAgent := &saEntity.SingleAgent{
|
||||
SingleAgent: &singleagent.SingleAgent{
|
||||
AgentID: 67890,
|
||||
SpaceID: 54321,
|
||||
AgentID: 67890,
|
||||
SpaceID: 54321,
|
||||
CreatorID: 12345,
|
||||
},
|
||||
}
|
||||
mockSingleAgent.EXPECT().ObtainAgentByIdentity(ctx, gomock.Any()).Return(mockAgent, nil)
|
||||
@ -640,8 +651,9 @@ func TestOpenapiAgentRun_ParseAdditionalMessages_AnswerWithNonTextContent(t *tes
|
||||
// Mock agent check success
|
||||
mockAgent := &saEntity.SingleAgent{
|
||||
SingleAgent: &singleagent.SingleAgent{
|
||||
AgentID: 67890,
|
||||
SpaceID: 54321,
|
||||
AgentID: 67890,
|
||||
SpaceID: 54321,
|
||||
CreatorID: 12345,
|
||||
},
|
||||
}
|
||||
mockSingleAgent.EXPECT().ObtainAgentByIdentity(ctx, gomock.Any()).Return(mockAgent, nil)
|
||||
@ -681,8 +693,9 @@ func TestOpenapiAgentRun_ParseAdditionalMessages_MixApiWithFileURL(t *testing.T)
|
||||
// Mock agent check success
|
||||
mockAgent := &saEntity.SingleAgent{
|
||||
SingleAgent: &singleagent.SingleAgent{
|
||||
AgentID: 67890,
|
||||
SpaceID: 54321,
|
||||
AgentID: 67890,
|
||||
SpaceID: 54321,
|
||||
CreatorID: 12345,
|
||||
},
|
||||
}
|
||||
mockSingleAgent.EXPECT().ObtainAgentByIdentity(ctx, gomock.Any()).Return(mockAgent, nil)
|
||||
@ -725,8 +738,9 @@ func TestOpenapiAgentRun_ParseAdditionalMessages_MixApiWithFileID(t *testing.T)
|
||||
// Mock agent check success
|
||||
mockAgent := &saEntity.SingleAgent{
|
||||
SingleAgent: &singleagent.SingleAgent{
|
||||
AgentID: 67890,
|
||||
SpaceID: 54321,
|
||||
AgentID: 67890,
|
||||
SpaceID: 54321,
|
||||
CreatorID: 12345,
|
||||
},
|
||||
}
|
||||
mockSingleAgent.EXPECT().ObtainAgentByIdentity(ctx, gomock.Any()).Return(mockAgent, nil)
|
||||
@ -780,8 +794,9 @@ func TestOpenapiAgentRun_ParseAdditionalMessages_FileIDError(t *testing.T) {
|
||||
// Mock agent check success
|
||||
mockAgent := &saEntity.SingleAgent{
|
||||
SingleAgent: &singleagent.SingleAgent{
|
||||
AgentID: 67890,
|
||||
SpaceID: 54321,
|
||||
AgentID: 67890,
|
||||
SpaceID: 54321,
|
||||
CreatorID: 12345,
|
||||
},
|
||||
}
|
||||
mockSingleAgent.EXPECT().ObtainAgentByIdentity(ctx, gomock.Any()).Return(mockAgent, nil)
|
||||
@ -829,8 +844,9 @@ func TestOpenapiAgentRun_ParseAdditionalMessages_EmptyContent(t *testing.T) {
|
||||
// Mock agent check success
|
||||
mockAgent := &saEntity.SingleAgent{
|
||||
SingleAgent: &singleagent.SingleAgent{
|
||||
AgentID: 67890,
|
||||
SpaceID: 54321,
|
||||
AgentID: 67890,
|
||||
SpaceID: 54321,
|
||||
CreatorID: 12345,
|
||||
},
|
||||
}
|
||||
mockSingleAgent.EXPECT().ObtainAgentByIdentity(ctx, gomock.Any()).Return(mockAgent, nil)
|
||||
@ -881,8 +897,9 @@ func TestOpenapiAgentRun_ParseAdditionalMessages_NilMessage(t *testing.T) {
|
||||
// Mock agent check success
|
||||
mockAgent := &saEntity.SingleAgent{
|
||||
SingleAgent: &singleagent.SingleAgent{
|
||||
AgentID: 67890,
|
||||
SpaceID: 54321,
|
||||
AgentID: 67890,
|
||||
SpaceID: 54321,
|
||||
CreatorID: 12345,
|
||||
},
|
||||
}
|
||||
mockSingleAgent.EXPECT().ObtainAgentByIdentity(ctx, gomock.Any()).Return(mockAgent, nil)
|
||||
|
||||
@ -32,9 +32,11 @@ import (
|
||||
resource "github.com/coze-dev/coze-studio/backend/api/model/resource/common"
|
||||
"github.com/coze-dev/coze-studio/backend/application/base/ctxutil"
|
||||
"github.com/coze-dev/coze-studio/backend/application/search"
|
||||
|
||||
model "github.com/coze-dev/coze-studio/backend/crossdomain/knowledge/model"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/knowledge/entity"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/knowledge/service"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/permission"
|
||||
resourceEntity "github.com/coze-dev/coze-studio/backend/domain/search/entity"
|
||||
cd "github.com/coze-dev/coze-studio/backend/infra/document"
|
||||
"github.com/coze-dev/coze-studio/backend/infra/document/parser"
|
||||
@ -65,6 +67,12 @@ func (k *KnowledgeApplicationService) CreateKnowledge(ctx context.Context, req *
|
||||
if uid == nil {
|
||||
return nil, errorx.New(errno.ErrKnowledgePermissionCode, errorx.KV("msg", "session required"))
|
||||
}
|
||||
|
||||
err := k.checkPermission(ctx, uid, ptr.Of(req.SpaceID), nil, nil, nil)
|
||||
if err != nil {
|
||||
return dataset.NewCreateDatasetResponse(), err
|
||||
}
|
||||
|
||||
createReq := service.CreateKnowledgeRequest{
|
||||
Name: req.Name,
|
||||
Description: req.Description,
|
||||
@ -115,6 +123,16 @@ func (k *KnowledgeApplicationService) DatasetDetail(ctx context.Context, req *da
|
||||
var err error
|
||||
var datasetIDs []int64
|
||||
|
||||
uid := ctxutil.GetUIDFromCtx(ctx)
|
||||
if uid == nil {
|
||||
return nil, errorx.New(errno.ErrKnowledgePermissionCode, errorx.KV("msg", "session required"))
|
||||
}
|
||||
|
||||
err = k.checkPermission(ctx, uid, ptr.Of(req.SpaceID), nil, nil, nil)
|
||||
if err != nil {
|
||||
return dataset.NewDatasetDetailResponse(), err
|
||||
}
|
||||
|
||||
datasetIDs, err = slices.TransformWithErrorCheck(req.GetDatasetIDs(), func(s string) (int64, error) {
|
||||
id, err := strconv.ParseInt(s, 10, 64)
|
||||
return id, err
|
||||
@ -146,6 +164,12 @@ func (k *KnowledgeApplicationService) DatasetDetail(ctx context.Context, req *da
|
||||
}
|
||||
|
||||
func (k *KnowledgeApplicationService) ListKnowledge(ctx context.Context, req *dataset.ListDatasetRequest) (*dataset.ListDatasetResponse, error) {
|
||||
|
||||
uid := ctxutil.GetUIDFromCtx(ctx)
|
||||
if uid == nil {
|
||||
return nil, errorx.New(errno.ErrKnowledgePermissionCode, errorx.KV("msg", "session required"))
|
||||
}
|
||||
|
||||
var err error
|
||||
var projectID int64
|
||||
request := service.ListKnowledgeRequest{}
|
||||
@ -178,6 +202,12 @@ func (k *KnowledgeApplicationService) ListKnowledge(ctx context.Context, req *da
|
||||
}
|
||||
if req.GetSpaceID() != 0 {
|
||||
request.SpaceID = &req.SpaceID
|
||||
|
||||
err = k.checkPermission(ctx, uid, ptr.Of(req.SpaceID), nil, nil, nil)
|
||||
if err != nil {
|
||||
return dataset.NewListDatasetResponse(), err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
request.OrderType = &orderType
|
||||
@ -219,7 +249,18 @@ func (k *KnowledgeApplicationService) ListKnowledge(ctx context.Context, req *da
|
||||
}
|
||||
|
||||
func (k *KnowledgeApplicationService) DeleteKnowledge(ctx context.Context, req *dataset.DeleteDatasetRequest) (*dataset.DeleteDatasetResponse, error) {
|
||||
err := k.DomainSVC.DeleteKnowledge(ctx, &service.DeleteKnowledgeRequest{
|
||||
|
||||
uid := ctxutil.GetUIDFromCtx(ctx)
|
||||
if uid == nil {
|
||||
return nil, errorx.New(errno.ErrKnowledgePermissionCode, errorx.KV("msg", "session required"))
|
||||
}
|
||||
|
||||
err := k.checkPermission(ctx, uid, nil, nil, ptr.Of(req.GetDatasetID()), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = k.DomainSVC.DeleteKnowledge(ctx, &service.DeleteKnowledgeRequest{
|
||||
KnowledgeID: req.GetDatasetID(),
|
||||
})
|
||||
if err != nil {
|
||||
@ -241,6 +282,17 @@ func (k *KnowledgeApplicationService) DeleteKnowledge(ctx context.Context, req *
|
||||
}
|
||||
|
||||
func (k *KnowledgeApplicationService) UpdateKnowledge(ctx context.Context, req *dataset.UpdateDatasetRequest) (*dataset.UpdateDatasetResponse, error) {
|
||||
|
||||
uid := ctxutil.GetUIDFromCtx(ctx)
|
||||
if uid == nil {
|
||||
return nil, errorx.New(errno.ErrKnowledgePermissionCode, errorx.KV("msg", "session required"))
|
||||
}
|
||||
|
||||
err := k.checkPermission(ctx, uid, nil, nil, ptr.Of(req.GetDatasetID()), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
now := time.Now().UnixMilli()
|
||||
updateReq := service.UpdateKnowledgeRequest{
|
||||
KnowledgeID: req.GetDatasetID(),
|
||||
@ -253,7 +305,7 @@ func (k *KnowledgeApplicationService) UpdateKnowledge(ctx context.Context, req *
|
||||
if req.Status != nil {
|
||||
updateReq.Status = ptr.Of(convertDatasetStatus2Entity(req.GetStatus()))
|
||||
}
|
||||
err := k.DomainSVC.UpdateKnowledge(ctx, &updateReq)
|
||||
err = k.DomainSVC.UpdateKnowledge(ctx, &updateReq)
|
||||
if err != nil {
|
||||
logs.CtxErrorf(ctx, "update knowledge failed, err: %v", err)
|
||||
return dataset.NewUpdateDatasetResponse(), err
|
||||
@ -288,6 +340,11 @@ func (k *KnowledgeApplicationService) CreateDocument(ctx context.Context, req *d
|
||||
return dataset.NewCreateDocumentResponse(), errors.New("knowledge not found")
|
||||
}
|
||||
knowledgeInfo := listResp.KnowledgeList[0]
|
||||
|
||||
if knowledgeInfo.CreatorID != *uid {
|
||||
return dataset.NewCreateDocumentResponse(), errorx.New(errno.ErrKnowledgePermissionCode, errorx.KV("msg", "permission denied"))
|
||||
}
|
||||
|
||||
documents := []*entity.Document{}
|
||||
if len(req.GetDocumentBases()) == 0 {
|
||||
return dataset.NewCreateDocumentResponse(), errors.New("document base is empty")
|
||||
@ -345,6 +402,12 @@ func (k *KnowledgeApplicationService) CreateDocument(ctx context.Context, req *d
|
||||
}
|
||||
|
||||
func (k *KnowledgeApplicationService) ListDocument(ctx context.Context, req *dataset.ListDocumentRequest) (*dataset.ListDocumentResponse, error) {
|
||||
|
||||
uid := ctxutil.GetUIDFromCtx(ctx)
|
||||
if uid == nil {
|
||||
return nil, errorx.New(errno.ErrKnowledgePermissionCode, errorx.KV("msg", "session required"))
|
||||
}
|
||||
|
||||
var limit int = int(req.GetSize())
|
||||
var offset int = int(req.GetPage() * req.GetSize())
|
||||
var err error
|
||||
@ -374,6 +437,9 @@ func (k *KnowledgeApplicationService) ListDocument(ctx context.Context, req *dat
|
||||
resp.Total = int32(listResp.Total)
|
||||
resp.DocumentInfos = make([]*dataset.DocumentInfo, 0)
|
||||
for i := range documents {
|
||||
if documents[i].CreatorID != *uid {
|
||||
return dataset.NewListDocumentResponse(), errorx.New(errno.ErrKnowledgePermissionCode, errorx.KV("msg", "permission denied"))
|
||||
}
|
||||
resp.DocumentInfos = append(resp.DocumentInfos, convertDocument2Model(documents[i]))
|
||||
}
|
||||
return resp, nil
|
||||
@ -383,6 +449,26 @@ func (k *KnowledgeApplicationService) DeleteDocument(ctx context.Context, req *d
|
||||
if len(req.GetDocumentIds()) == 0 {
|
||||
return dataset.NewDeleteDocumentResponse(), errors.New("document ids is empty")
|
||||
}
|
||||
|
||||
uid := ctxutil.GetUIDFromCtx(ctx)
|
||||
if uid == nil {
|
||||
return nil, errorx.New(errno.ErrKnowledgePermissionCode, errorx.KV("msg", "session required"))
|
||||
}
|
||||
|
||||
transformedIDs, err := slices.TransformWithErrorCheck(req.GetDocumentIds(), func(s string) (int64, error) {
|
||||
id, err := strconv.ParseInt(s, 10, 64)
|
||||
return id, err
|
||||
})
|
||||
if err != nil {
|
||||
logs.CtxErrorf(ctx, "convert string ids failed, err: %v", err)
|
||||
return dataset.NewDeleteDocumentResponse(), err
|
||||
}
|
||||
|
||||
err = k.checkPermission(ctx, uid, nil, transformedIDs, nil, nil)
|
||||
if err != nil {
|
||||
return dataset.NewDeleteDocumentResponse(), err
|
||||
}
|
||||
|
||||
for i := range req.GetDocumentIds() {
|
||||
docID, err := strconv.ParseInt(req.GetDocumentIds()[i], 10, 64)
|
||||
if err != nil {
|
||||
@ -401,7 +487,17 @@ func (k *KnowledgeApplicationService) DeleteDocument(ctx context.Context, req *d
|
||||
}
|
||||
|
||||
func (k *KnowledgeApplicationService) UpdateDocument(ctx context.Context, req *dataset.UpdateDocumentRequest) (*dataset.UpdateDocumentResponse, error) {
|
||||
err := k.DomainSVC.UpdateDocument(ctx, &service.UpdateDocumentRequest{
|
||||
|
||||
uid := ctxutil.GetUIDFromCtx(ctx)
|
||||
if uid == nil {
|
||||
return nil, errorx.New(errno.ErrKnowledgePermissionCode, errorx.KV("msg", "session required"))
|
||||
}
|
||||
err := k.checkPermission(ctx, uid, nil, []int64{req.GetDocumentID()}, nil, nil)
|
||||
if err != nil {
|
||||
return dataset.NewUpdateDocumentResponse(), err
|
||||
}
|
||||
|
||||
err = k.DomainSVC.UpdateDocument(ctx, &service.UpdateDocumentRequest{
|
||||
DocumentID: req.GetDocumentID(),
|
||||
DocumentName: req.DocumentName,
|
||||
TableInfo: &entity.TableInfo{
|
||||
@ -415,7 +511,66 @@ func (k *KnowledgeApplicationService) UpdateDocument(ctx context.Context, req *d
|
||||
return &dataset.UpdateDocumentResponse{}, nil
|
||||
}
|
||||
|
||||
func (k *KnowledgeApplicationService) checkPermission(ctx context.Context, uid *int64, spaceID *int64, documentIDs []int64, knowledgeID *int64, sliceIDs []int64) error {
|
||||
|
||||
rd := []*permission.ResourceIdentifier{}
|
||||
|
||||
if spaceID != nil {
|
||||
rd = append(rd, &permission.ResourceIdentifier{
|
||||
Type: permission.ResourceTypeWorkspace,
|
||||
ID: []int64{*spaceID},
|
||||
Action: permission.ActionRead,
|
||||
})
|
||||
}
|
||||
|
||||
if documentIDs != nil {
|
||||
rd = append(rd, &permission.ResourceIdentifier{
|
||||
Type: permission.ResourceTypeKnowledgeDocument,
|
||||
ID: documentIDs,
|
||||
Action: permission.ActionRead,
|
||||
})
|
||||
}
|
||||
if sliceIDs != nil {
|
||||
rd = append(rd, &permission.ResourceIdentifier{
|
||||
Type: permission.ResourceTypeKnowledgeSlice,
|
||||
ID: sliceIDs,
|
||||
Action: permission.ActionRead,
|
||||
})
|
||||
}
|
||||
|
||||
if knowledgeID != nil {
|
||||
rd = append(rd, &permission.ResourceIdentifier{
|
||||
Type: permission.ResourceTypeKnowledge,
|
||||
ID: []int64{*knowledgeID},
|
||||
Action: permission.ActionRead,
|
||||
})
|
||||
}
|
||||
|
||||
if len(rd) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
checkResult, err := permission.DefaultSVC().CheckAuthz(ctx, &permission.CheckAuthzData{
|
||||
ResourceIdentifier: rd,
|
||||
OperatorID: *uid,
|
||||
})
|
||||
if err != nil {
|
||||
logs.CtxErrorf(ctx, "check authz failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
if checkResult.Decision != permission.Allow {
|
||||
return errorx.New(errno.ErrKnowledgePermissionCode, errorx.KV("msg", "permission denied"))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k *KnowledgeApplicationService) GetDocumentProgress(ctx context.Context, req *dataset.GetDocumentProgressRequest) (*dataset.GetDocumentProgressResponse, error) {
|
||||
|
||||
uid := ctxutil.GetUIDFromCtx(ctx)
|
||||
if uid == nil {
|
||||
return nil, errorx.New(errno.ErrKnowledgePermissionCode, errorx.KV("msg", "session required"))
|
||||
}
|
||||
docIDs, err := slices.TransformWithErrorCheck(req.GetDocumentIds(), func(s string) (int64, error) {
|
||||
id, err := strconv.ParseInt(s, 10, 64)
|
||||
return id, err
|
||||
@ -424,6 +579,11 @@ func (k *KnowledgeApplicationService) GetDocumentProgress(ctx context.Context, r
|
||||
logs.CtxErrorf(ctx, "convert string ids failed, err: %v", err)
|
||||
return dataset.NewGetDocumentProgressResponse(), err
|
||||
}
|
||||
err = k.checkPermission(ctx, uid, nil, docIDs, nil, nil)
|
||||
if err != nil {
|
||||
return dataset.NewGetDocumentProgressResponse(), err
|
||||
}
|
||||
|
||||
domainResp, err := k.DomainSVC.MGetDocumentProgress(ctx, &service.MGetDocumentProgressRequest{
|
||||
DocumentIDs: docIDs,
|
||||
})
|
||||
@ -450,6 +610,10 @@ func (k *KnowledgeApplicationService) GetDocumentProgress(ctx context.Context, r
|
||||
}
|
||||
|
||||
func (k *KnowledgeApplicationService) Resegment(ctx context.Context, req *dataset.ResegmentRequest) (*dataset.ResegmentResponse, error) {
|
||||
uid := ctxutil.GetUIDFromCtx(ctx)
|
||||
if uid == nil {
|
||||
return nil, errorx.New(errno.ErrKnowledgePermissionCode, errorx.KV("msg", "session required"))
|
||||
}
|
||||
resp := dataset.NewResegmentResponse()
|
||||
resp.DocumentInfos = make([]*dataset.DocumentInfo, 0)
|
||||
for i := range req.GetDocumentIds() {
|
||||
@ -471,6 +635,10 @@ func (k *KnowledgeApplicationService) Resegment(ctx context.Context, req *datase
|
||||
logs.CtxErrorf(ctx, "resegment document failed, err: %v", err)
|
||||
return dataset.NewResegmentResponse(), err
|
||||
}
|
||||
if resegmentResp.Document.Info.CreatorID != *uid {
|
||||
return dataset.NewResegmentResponse(), errorx.New(errno.ErrKnowledgePermissionCode, errorx.KV("msg", "permission denied"))
|
||||
}
|
||||
|
||||
resp.DocumentInfos = append(resp.DocumentInfos, &dataset.DocumentInfo{
|
||||
Name: resegmentResp.Document.Name,
|
||||
DocumentID: resegmentResp.Document.ID,
|
||||
@ -494,6 +662,9 @@ func (k *KnowledgeApplicationService) CreateSlice(ctx context.Context, req *data
|
||||
if len(listResp.Documents) != 1 {
|
||||
return dataset.NewCreateSliceResponse(), errors.New("document not found")
|
||||
}
|
||||
if listResp.Documents[0].CreatorID != *uid {
|
||||
return dataset.NewCreateSliceResponse(), errorx.New(errno.ErrKnowledgePermissionCode, errorx.KV("msg", "permission denied"))
|
||||
}
|
||||
sliceEntity := &model.Slice{
|
||||
Info: model.Info{
|
||||
CreatorID: *uid,
|
||||
@ -531,12 +702,28 @@ func (k *KnowledgeApplicationService) CreateSlice(ctx context.Context, req *data
|
||||
}
|
||||
|
||||
func (k *KnowledgeApplicationService) DeleteSlice(ctx context.Context, req *dataset.DeleteSliceRequest) (*dataset.DeleteSliceResponse, error) {
|
||||
uid := ctxutil.GetUIDFromCtx(ctx)
|
||||
if uid == nil {
|
||||
return nil, errorx.New(errno.ErrKnowledgePermissionCode, errorx.KV("msg", "session required"))
|
||||
}
|
||||
|
||||
sliceIDs := make([]int64, 0, len(req.GetSliceIds()))
|
||||
for i := range req.GetSliceIds() {
|
||||
sliceID, err := strconv.ParseInt(req.GetSliceIds()[i], 10, 64)
|
||||
if err != nil {
|
||||
logs.CtxErrorf(ctx, "parse int failed, err: %v", err)
|
||||
return dataset.NewDeleteSliceResponse(), err
|
||||
}
|
||||
sliceIDs = append(sliceIDs, sliceID)
|
||||
}
|
||||
|
||||
err := k.checkPermission(ctx, uid, nil, nil, nil, sliceIDs)
|
||||
if err != nil {
|
||||
logs.CtxErrorf(ctx, "check permission failed, err: %v", err)
|
||||
return dataset.NewDeleteSliceResponse(), err
|
||||
}
|
||||
for i := range sliceIDs {
|
||||
sliceID := sliceIDs[i]
|
||||
err = k.DomainSVC.DeleteSlice(ctx, &service.DeleteSliceRequest{
|
||||
SliceID: sliceID,
|
||||
})
|
||||
@ -559,6 +746,10 @@ func (k *KnowledgeApplicationService) UpdateSlice(ctx context.Context, req *data
|
||||
if err != nil {
|
||||
return nil, errorx.New(errno.ErrKnowledgeInvalidParamCode, errorx.KV("msg", "slice not found"))
|
||||
}
|
||||
|
||||
if getSliceResp.Slice != nil && getSliceResp.Slice.Info.CreatorID != *uid {
|
||||
return nil, errorx.New(errno.ErrKnowledgePermissionCode, errorx.KV("msg", "permission denied"))
|
||||
}
|
||||
docID := getSliceResp.Slice.DocumentID
|
||||
|
||||
listResp, err := k.DomainSVC.ListDocument(ctx, &service.ListDocumentRequest{
|
||||
@ -646,6 +837,19 @@ func packTableSliceColumnData(ctx context.Context, slice *model.Slice, text stri
|
||||
}
|
||||
|
||||
func (k *KnowledgeApplicationService) ListSlice(ctx context.Context, req *dataset.ListSliceRequest) (*dataset.ListSliceResponse, error) {
|
||||
|
||||
uid := ctxutil.GetUIDFromCtx(ctx)
|
||||
if uid == nil {
|
||||
return nil, errorx.New(errno.ErrKnowledgePermissionCode, errorx.KV("msg", "session required"))
|
||||
}
|
||||
|
||||
if req.DatasetID != nil {
|
||||
err := k.checkPermission(ctx, uid, nil, nil, ptr.Of(*req.DatasetID), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
listResp, err := k.DomainSVC.ListSlice(ctx, &service.ListSliceRequest{
|
||||
KnowledgeID: req.DatasetID,
|
||||
DocumentID: req.DocumentID,
|
||||
@ -684,6 +888,17 @@ func (k *KnowledgeApplicationService) GetTableSchema(ctx context.Context, req *d
|
||||
domainResp *service.TableSchemaResponse
|
||||
err error
|
||||
)
|
||||
uid := ctxutil.GetUIDFromCtx(ctx)
|
||||
if uid == nil {
|
||||
return nil, errorx.New(errno.ErrKnowledgePermissionCode, errorx.KV("msg", "session required"))
|
||||
}
|
||||
|
||||
if req.GetDocumentID() > 0 {
|
||||
err = k.checkPermission(ctx, uid, nil, []int64{req.GetDocumentID()}, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if req.SourceFile == nil { // alter table
|
||||
domainResp, err = k.DomainSVC.GetAlterTableSchema(ctx, &service.AlterTableSchemaRequest{
|
||||
@ -751,6 +966,18 @@ func (k *KnowledgeApplicationService) ValidateTableSchema(ctx context.Context, r
|
||||
if srcInfo == nil {
|
||||
return nil, fmt.Errorf("source info not provided")
|
||||
}
|
||||
|
||||
uid := ctxutil.GetUIDFromCtx(ctx)
|
||||
if uid == nil {
|
||||
return nil, errorx.New(errno.ErrKnowledgePermissionCode, errorx.KV("msg", "session required"))
|
||||
}
|
||||
if req.GetDocumentID() > 0 {
|
||||
err = k.checkPermission(ctx, uid, nil, []int64{req.GetDocumentID()}, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var tableSheet *entity.TableSheet
|
||||
if req.TableSheet != nil {
|
||||
tableSheet = &entity.TableSheet{
|
||||
@ -773,6 +1000,18 @@ func (k *KnowledgeApplicationService) ValidateTableSchema(ctx context.Context, r
|
||||
}
|
||||
|
||||
func (k *KnowledgeApplicationService) GetDocumentTableInfo(ctx context.Context, req *document.GetDocumentTableInfoRequest) (*document.GetDocumentTableInfoResponse, error) {
|
||||
|
||||
uid := ctxutil.GetUIDFromCtx(ctx)
|
||||
if uid == nil {
|
||||
return nil, errorx.New(errno.ErrKnowledgePermissionCode, errorx.KV("msg", "session required"))
|
||||
}
|
||||
if req.GetDocumentID() > 0 {
|
||||
err := k.checkPermission(ctx, uid, nil, []int64{req.GetDocumentID()}, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
domainResp, err := k.DomainSVC.GetDocumentTableInfo(ctx, &service.GetDocumentTableInfoRequest{
|
||||
DocumentID: req.DocumentID,
|
||||
SourceInfo: &service.TableSourceInfo{
|
||||
@ -804,6 +1043,13 @@ func (k *KnowledgeApplicationService) CreateDocumentReview(ctx context.Context,
|
||||
if uid == nil {
|
||||
return nil, errorx.New(errno.ErrKnowledgePermissionCode, errorx.KV("msg", "session required"))
|
||||
}
|
||||
if req.GetDatasetID() > 0 {
|
||||
err := k.checkPermission(ctx, uid, nil, nil, ptr.Of(req.GetDatasetID()), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
createResp, err := k.DomainSVC.CreateDocumentReview(ctx, convertCreateDocReviewReq(req))
|
||||
if err != nil {
|
||||
logs.CtxErrorf(ctx, "create document review failed, err: %v", err)
|
||||
@ -838,6 +1084,12 @@ func (k *KnowledgeApplicationService) MGetDocumentReview(ctx context.Context, re
|
||||
logs.CtxErrorf(ctx, "parse int failed, err: %v", err)
|
||||
return dataset.NewMGetDocumentReviewResponse(), err
|
||||
}
|
||||
|
||||
err = k.checkPermission(ctx, uid, nil, nil, ptr.Of(req.GetDatasetID()), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mGetResp, err := k.DomainSVC.MGetDocumentReview(ctx, &service.MGetDocumentReviewRequest{
|
||||
KnowledgeID: req.GetDatasetID(),
|
||||
ReviewIDs: reviewIDs,
|
||||
@ -867,7 +1119,12 @@ func (k *KnowledgeApplicationService) SaveDocumentReview(ctx context.Context, re
|
||||
if uid == nil {
|
||||
return nil, errorx.New(errno.ErrKnowledgePermissionCode, errorx.KV("msg", "session required"))
|
||||
}
|
||||
err := k.DomainSVC.SaveDocumentReview(ctx, &service.SaveDocumentReviewRequest{
|
||||
err := k.checkPermission(ctx, uid, nil, nil, ptr.Of(req.GetDatasetID()), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = k.DomainSVC.SaveDocumentReview(ctx, &service.SaveDocumentReviewRequest{
|
||||
KnowledgeID: req.GetDatasetID(),
|
||||
DocTreeJson: req.GetDocTreeJSON(),
|
||||
ReviewID: req.GetReviewID(),
|
||||
@ -953,6 +1210,12 @@ func (k *KnowledgeApplicationService) UpdatePhotoCaption(ctx context.Context, re
|
||||
if uid == nil {
|
||||
return nil, errorx.New(errno.ErrKnowledgePermissionCode, errorx.KV("msg", "session required"))
|
||||
}
|
||||
|
||||
err := k.checkPermission(ctx, uid, nil, []int64{req.DocumentID}, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := dataset.NewUpdatePhotoCaptionResponse()
|
||||
listResp, err := k.DomainSVC.ListSlice(ctx, &service.ListSliceRequest{DocumentID: ptr.Of(req.DocumentID)})
|
||||
if err != nil {
|
||||
@ -999,8 +1262,17 @@ func (k *KnowledgeApplicationService) MoveKnowledgeToLibrary(ctx context.Context
|
||||
return nil
|
||||
}
|
||||
func (k *KnowledgeApplicationService) ListPhoto(ctx context.Context, req *dataset.ListPhotoRequest) (*dataset.ListPhotoResponse, error) {
|
||||
uid := ctxutil.GetUIDFromCtx(ctx)
|
||||
if uid == nil {
|
||||
return nil, errorx.New(errno.ErrKnowledgePermissionCode, errorx.KV("msg", "session required"))
|
||||
}
|
||||
|
||||
err := k.checkPermission(ctx, uid, nil, nil, ptr.Of(req.GetDatasetID()), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := dataset.NewListPhotoResponse()
|
||||
var err error
|
||||
var offset int
|
||||
if req.GetPage() >= 1 {
|
||||
offset = int(req.GetSize() * (req.GetPage() - 1))
|
||||
@ -1071,6 +1343,17 @@ func (k *KnowledgeApplicationService) PhotoDetail(ctx context.Context, req *data
|
||||
resp.Msg = "document ids is empty"
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
uid := ctxutil.GetUIDFromCtx(ctx)
|
||||
if uid == nil {
|
||||
return nil, errorx.New(errno.ErrKnowledgePermissionCode, errorx.KV("msg", "session required"))
|
||||
}
|
||||
|
||||
err := k.checkPermission(ctx, uid, nil, nil, ptr.Of(req.GetDatasetID()), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
docIDs, err := slices.TransformWithErrorCheck(req.GetDocumentIds(), func(s string) (int64, error) {
|
||||
id, err := strconv.ParseInt(s, 10, 64)
|
||||
return id, err
|
||||
@ -1084,15 +1367,12 @@ func (k *KnowledgeApplicationService) PhotoDetail(ctx context.Context, req *data
|
||||
logs.CtxErrorf(ctx, "list photo slice failed, err: %v", err)
|
||||
return resp, err
|
||||
}
|
||||
listDocResp, err := k.DomainSVC.ListDocument(ctx, &service.ListDocumentRequest{DocumentIDs: docIDs, SelectAll: true})
|
||||
if err != nil {
|
||||
logs.CtxErrorf(ctx, "get documents by slice ids failed, err: %v", err)
|
||||
return resp, err
|
||||
}
|
||||
listDocResp, err := k.DomainSVC.ListDocument(ctx, &service.ListDocumentRequest{DocumentIDs: docIDs, SelectAll: true, KnowledgeID: req.GetDatasetID()})
|
||||
if err != nil {
|
||||
logs.CtxErrorf(ctx, "get documents by slice ids failed, err: %v", err)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
photos := k.packPhotoInfo(listResp.Slices, listDocResp.Documents)
|
||||
sort.SliceStable(photos, func(i, j int) bool {
|
||||
return photos[i].UpdateTime > photos[j].UpdateTime
|
||||
@ -1110,6 +1390,16 @@ func (k *KnowledgeApplicationService) ExtractPhotoCaption(ctx context.Context, r
|
||||
resp.Msg = "document id is empty"
|
||||
return resp, nil
|
||||
}
|
||||
uid := ctxutil.GetUIDFromCtx(ctx)
|
||||
if uid == nil {
|
||||
return nil, errorx.New(errno.ErrKnowledgePermissionCode, errorx.KV("msg", "session required"))
|
||||
}
|
||||
|
||||
err := k.checkPermission(ctx, uid, nil, []int64{req.GetDocumentID()}, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
extractResp, err := k.DomainSVC.ExtractPhotoCaption(ctx, &service.ExtractPhotoCaptionRequest{DocumentID: req.GetDocumentID()})
|
||||
if err != nil {
|
||||
return resp, err
|
||||
|
||||
@ -103,6 +103,11 @@ func (d *DatabaseApplicationService) ListDatabase(ctx context.Context, req *tabl
|
||||
}
|
||||
|
||||
func (d *DatabaseApplicationService) GetDatabaseByID(ctx context.Context, req *table.SingleDatabaseRequest) (*table.SingleDatabaseResponse, error) {
|
||||
uid := ctxutil.GetUIDFromCtx(ctx)
|
||||
if uid == nil {
|
||||
return nil, errorx.New(errno.ErrMemoryPermissionCode, errorx.KV("msg", "session required"))
|
||||
}
|
||||
|
||||
basics := make([]*model.DatabaseBasic, 1)
|
||||
b := &model.DatabaseBasic{
|
||||
ID: req.ID,
|
||||
@ -127,6 +132,10 @@ func (d *DatabaseApplicationService) GetDatabaseByID(ctx context.Context, req *t
|
||||
return nil, fmt.Errorf("database %d not found", req.GetID())
|
||||
}
|
||||
|
||||
if res.Databases[0].CreatorID != *uid {
|
||||
return nil, errorx.New(errno.ErrMemoryPermissionCode, errorx.KV("msg", "creator id is invalid"))
|
||||
}
|
||||
|
||||
return ConvertDatabaseRes(res.Databases[0]), nil
|
||||
}
|
||||
|
||||
@ -353,6 +362,11 @@ func (d *DatabaseApplicationService) UpdateDatabaseRecords(ctx context.Context,
|
||||
}
|
||||
|
||||
func (d *DatabaseApplicationService) GetOnlineDatabaseId(ctx context.Context, req *table.GetOnlineDatabaseIdRequest) (*table.GetOnlineDatabaseIdResponse, error) {
|
||||
uid := ctxutil.GetUIDFromCtx(ctx)
|
||||
if uid == nil {
|
||||
return nil, errorx.New(errno.ErrMemoryPermissionCode, errorx.KV("msg", "session required"))
|
||||
}
|
||||
|
||||
basics := make([]*model.DatabaseBasic, 1)
|
||||
basics[0] = &model.DatabaseBasic{
|
||||
ID: req.ID,
|
||||
@ -370,6 +384,10 @@ func (d *DatabaseApplicationService) GetOnlineDatabaseId(ctx context.Context, re
|
||||
return nil, fmt.Errorf("database %d not found", req.ID)
|
||||
}
|
||||
|
||||
if res.Databases[0].CreatorID != *uid {
|
||||
return nil, errorx.New(errno.ErrMemoryPermissionCode, errorx.KV("msg", "creator id is invalid"))
|
||||
}
|
||||
|
||||
return &table.GetOnlineDatabaseIdResponse{
|
||||
ID: res.Databases[0].OnlineID,
|
||||
|
||||
@ -544,6 +562,12 @@ func (d *DatabaseApplicationService) GetConnectorName(ctx context.Context, req *
|
||||
}
|
||||
|
||||
func (d *DatabaseApplicationService) GetBotDatabase(ctx context.Context, req *table.GetBotTableRequest) (*table.GetBotTableResponse, error) {
|
||||
|
||||
uid := ctxutil.GetUIDFromCtx(ctx)
|
||||
if uid == nil {
|
||||
return nil, errorx.New(errno.ErrMemoryPermissionCode, errorx.KV("msg", "session required"))
|
||||
}
|
||||
|
||||
relationResp, err := d.DomainSVC.MGetRelationsByAgentID(ctx, &database.MGetRelationsByAgentIDRequest{
|
||||
AgentID: req.GetBotID(),
|
||||
TableType: req.GetTableType(),
|
||||
@ -564,6 +588,11 @@ func (d *DatabaseApplicationService) GetBotDatabase(ctx context.Context, req *ta
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, db := range resp.Databases {
|
||||
if db.CreatorID != *uid {
|
||||
return nil, errorx.New(errno.ErrMemoryPermissionCode, errorx.KV("msg", "creator id is invalid"))
|
||||
}
|
||||
}
|
||||
|
||||
return &table.GetBotTableResponse{
|
||||
BotTableList: convertToBotTableList(resp.Databases, req.GetBotID(), relationMap),
|
||||
@ -650,6 +679,11 @@ func (d *DatabaseApplicationService) GetDatabaseTableSchema(ctx context.Context,
|
||||
tableType = req.GetTableDataType()
|
||||
}
|
||||
|
||||
err := d.ValidateAccess(ctx, req.GetDatabaseID(), table.TableType_OnlineTable)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
schema, err := d.DomainSVC.GetDatabaseTableSchema(ctx, &database.GetDatabaseTableSchemaRequest{
|
||||
DatabaseID: req.GetDatabaseID(),
|
||||
UserID: *uid,
|
||||
|
||||
@ -26,9 +26,11 @@ import (
|
||||
"github.com/coze-dev/coze-studio/backend/api/model/data/variable/kvmemory"
|
||||
"github.com/coze-dev/coze-studio/backend/api/model/data/variable/project_memory"
|
||||
"github.com/coze-dev/coze-studio/backend/application/base/ctxutil"
|
||||
crosspermission "github.com/coze-dev/coze-studio/backend/crossdomain/permission"
|
||||
model "github.com/coze-dev/coze-studio/backend/crossdomain/variables/model"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/memory/variables/entity"
|
||||
variables "github.com/coze-dev/coze-studio/backend/domain/memory/variables/service"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/permission"
|
||||
"github.com/coze-dev/coze-studio/backend/pkg/errorx"
|
||||
"github.com/coze-dev/coze-studio/backend/pkg/i18n"
|
||||
"github.com/coze-dev/coze-studio/backend/pkg/lang/ternary"
|
||||
@ -213,7 +215,28 @@ func (v *VariableApplicationService) UpdateProjectVariable(ctx context.Context,
|
||||
req.UserID = *uid
|
||||
}
|
||||
|
||||
// TODO: project owner check
|
||||
projectID, err := strconv.ParseInt(req.ProjectID, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
checkResult, err := crosspermission.DefaultSVC().CheckAuthz(ctx, &permission.CheckAuthzData{
|
||||
OperatorID: *uid,
|
||||
ResourceIdentifier: []*permission.ResourceIdentifier{
|
||||
{
|
||||
Type: permission.ResourceTypeApp,
|
||||
ID: []int64{projectID},
|
||||
Action: permission.ActionRead,
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if checkResult.Decision != permission.Allow {
|
||||
return nil, errorx.New(errno.ErrMemoryPermissionCode, errorx.KV("msg", "no permission"))
|
||||
}
|
||||
|
||||
sysVars := v.DomainSVC.GetSysVariableConf(ctx).ToVariables()
|
||||
|
||||
@ -259,7 +282,7 @@ func (v *VariableApplicationService) UpdateProjectVariable(ctx context.Context,
|
||||
}
|
||||
}
|
||||
|
||||
_, err := v.DomainSVC.UpsertProjectMeta(ctx, req.ProjectID, "", req.UserID, entity.NewVariables(list))
|
||||
_, err = v.DomainSVC.UpsertProjectMeta(ctx, req.ProjectID, "", req.UserID, entity.NewVariables(list))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -327,6 +350,15 @@ func (v *VariableApplicationService) GetPlayGroundMemory(ctx context.Context, re
|
||||
connectId := ternary.IFElse(req.ConnectorID == nil, consts.CozeConnectorID, req.GetConnectorID())
|
||||
connectorUID := ternary.IFElse(req.UserID == 0, *uid, req.UserID)
|
||||
|
||||
resourceID, err := strconv.ParseInt(bizID, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = v.checkPermission(ctx, uid, isProjectKV, resourceID, permission.ActionRead)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
e := entity.NewUserVariableMeta(&model.UserVariableMeta{
|
||||
BizType: bizType,
|
||||
BizID: bizID,
|
||||
@ -345,6 +377,32 @@ func (v *VariableApplicationService) GetPlayGroundMemory(ctx context.Context, re
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (v *VariableApplicationService) checkPermission(ctx context.Context, uid *int64, isProjectKV bool, resourceID int64, action permission.Action) error {
|
||||
checkResult, err := crosspermission.DefaultSVC().CheckAuthz(ctx, &permission.CheckAuthzData{
|
||||
OperatorID: *uid,
|
||||
ResourceIdentifier: []*permission.ResourceIdentifier{
|
||||
{
|
||||
Type: func() permission.ResourceType {
|
||||
if isProjectKV {
|
||||
return permission.ResourceTypeApp
|
||||
}
|
||||
return permission.ResourceTypeAgent
|
||||
}(),
|
||||
ID: []int64{resourceID},
|
||||
Action: action,
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if checkResult.Decision != permission.Allow {
|
||||
return errorx.New(errno.ErrMemoryPermissionCode, errorx.KV("msg", "no permission"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *VariableApplicationService) SetVariableInstance(ctx context.Context, req *kvmemory.SetKvMemoryReq) (*kvmemory.SetKvMemoryResp, error) {
|
||||
uid := ctxutil.GetUIDFromCtx(ctx)
|
||||
if uid == nil {
|
||||
@ -363,6 +421,15 @@ func (v *VariableApplicationService) SetVariableInstance(ctx context.Context, re
|
||||
connectId := ternary.IFElse(req.ConnectorID == nil, consts.CozeConnectorID, req.GetConnectorID())
|
||||
connectorUID := ternary.IFElse(req.GetUserID() == 0, *uid, req.GetUserID())
|
||||
|
||||
resourceID, err := strconv.ParseInt(bizID, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = v.checkPermission(ctx, uid, isProjectKV, resourceID, permission.ActionRead)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
e := entity.NewUserVariableMeta(&model.UserVariableMeta{
|
||||
BizType: bizType,
|
||||
BizID: bizID,
|
||||
|
||||
36
backend/application/permission/init.go
Normal file
36
backend/application/permission/init.go
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package permission
|
||||
|
||||
import (
|
||||
"github.com/coze-dev/coze-studio/backend/domain/permission"
|
||||
)
|
||||
|
||||
type ServiceComponents struct {
|
||||
}
|
||||
|
||||
type PermissionApplicationService struct {
|
||||
DomainSVC permission.Permission
|
||||
}
|
||||
|
||||
func InitService(components *ServiceComponents) *PermissionApplicationService {
|
||||
domainSVC := permission.NewService()
|
||||
|
||||
return &PermissionApplicationService{
|
||||
DomainSVC: domainSVC,
|
||||
}
|
||||
}
|
||||
@ -23,6 +23,7 @@ import (
|
||||
pluginAPI "github.com/coze-dev/coze-studio/backend/api/model/plugin_develop"
|
||||
common "github.com/coze-dev/coze-studio/backend/api/model/plugin_develop/common"
|
||||
resCommon "github.com/coze-dev/coze-studio/backend/api/model/resource/common"
|
||||
"github.com/coze-dev/coze-studio/backend/application/base/ctxutil"
|
||||
"github.com/coze-dev/coze-studio/backend/crossdomain/plugin/model"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/plugin/dto"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/plugin/entity"
|
||||
@ -30,6 +31,7 @@ import (
|
||||
"github.com/coze-dev/coze-studio/backend/pkg/errorx"
|
||||
"github.com/coze-dev/coze-studio/backend/pkg/lang/ptr"
|
||||
"github.com/coze-dev/coze-studio/backend/pkg/logs"
|
||||
"github.com/coze-dev/coze-studio/backend/types/errno"
|
||||
)
|
||||
|
||||
func (p *PluginApplicationService) PublishPlugin(ctx context.Context, req *pluginAPI.PublishPluginRequest) (resp *pluginAPI.PublishPluginResponse, err error) {
|
||||
@ -110,6 +112,12 @@ func (p *PluginApplicationService) GetPluginNextVersion(ctx context.Context, req
|
||||
}
|
||||
|
||||
func (p *PluginApplicationService) GetDevPluginList(ctx context.Context, req *pluginAPI.GetDevPluginListRequest) (resp *pluginAPI.GetDevPluginListResponse, err error) {
|
||||
|
||||
uid := ctxutil.GetUIDFromCtx(ctx)
|
||||
if uid == nil {
|
||||
return nil, errorx.New(errno.ErrPluginPermissionCode, errorx.KV(errno.PluginMsgKey, "session is required"))
|
||||
}
|
||||
|
||||
pageInfo := dto.PageInfo{
|
||||
Name: req.Name,
|
||||
Page: int(req.GetPage()),
|
||||
@ -133,6 +141,10 @@ func (p *PluginApplicationService) GetDevPluginList(ctx context.Context, req *pl
|
||||
|
||||
pluginList := make([]*common.PluginInfoForPlayground, 0, len(res.Plugins))
|
||||
for _, pl := range res.Plugins {
|
||||
|
||||
if pl.DeveloperID > 0 && pl.DeveloperID != *uid {
|
||||
return nil, errorx.New(errno.ErrPluginPermissionCode, errorx.KV(errno.PluginMsgKey, "plugin developer is not current user"))
|
||||
}
|
||||
tools, err := p.toolRepo.GetPluginAllDraftTools(ctx, pl.ID)
|
||||
if err != nil {
|
||||
return nil, errorx.Wrapf(err, "GetPluginAllDraftTools failed, pluginID=%d", pl.ID)
|
||||
|
||||
@ -97,10 +97,19 @@ func (p *PromptApplicationService) UpsertPromptResource(ctx context.Context, req
|
||||
func (p *PromptApplicationService) GetPromptResourceInfo(ctx context.Context, req *playground.GetPromptResourceInfoRequest) (
|
||||
resp *playground.GetPromptResourceInfoResponse, err error,
|
||||
) {
|
||||
|
||||
uid := ctxutil.GetUIDFromCtx(ctx)
|
||||
if uid == nil {
|
||||
return nil, errorx.New(errno.ErrPromptPermissionCode, errorx.KV("msg", "no session data provided"))
|
||||
}
|
||||
|
||||
promptInfo, err := p.DomainSVC.GetPromptResource(ctx, req.GetPromptResourceID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if promptInfo.CreatorID != *uid {
|
||||
return nil, errorx.New(errno.ErrPromptPermissionCode, errorx.KV("msg", "no permission"))
|
||||
}
|
||||
|
||||
return &playground.GetPromptResourceInfoResponse{
|
||||
Data: promptInfoDo2To(promptInfo),
|
||||
|
||||
@ -124,6 +124,9 @@ func (s *SearchApplicationService) LibraryResourceList(ctx context.Context, req
|
||||
if res == nil {
|
||||
continue
|
||||
}
|
||||
if res.CreatorID != nil && *res.CreatorID != *userID {
|
||||
return nil, errorx.New(errno.ErrSearchPermissionCode, errorx.KV("msg", "user can't search resources created by themselves"))
|
||||
}
|
||||
filterResource = append(filterResource, res)
|
||||
}
|
||||
|
||||
@ -276,6 +279,12 @@ func (s *SearchApplicationService) getAPPAllResources(ctx context.Context, appID
|
||||
}
|
||||
|
||||
func (s *SearchApplicationService) packAPPResources(ctx context.Context, resources []*entity.ResourceDocument) ([]*common.ProjectResourceGroup, error) {
|
||||
|
||||
uid := ctxutil.GetUIDFromCtx(ctx)
|
||||
if uid == nil {
|
||||
return nil, errorx.New(errno.ErrSearchPermissionCode, errorx.KV(errno.PluginMsgKey, "session is required"))
|
||||
}
|
||||
|
||||
workflowGroup := &common.ProjectResourceGroup{
|
||||
GroupType: common.ProjectResourceGroupType_Workflow,
|
||||
ResourceList: []*common.ProjectResourceInfo{},
|
||||
@ -294,6 +303,10 @@ func (s *SearchApplicationService) packAPPResources(ctx context.Context, resourc
|
||||
for idx := range resources {
|
||||
v := resources[idx]
|
||||
|
||||
if v.OwnerID != nil && *v.OwnerID != *uid {
|
||||
return nil, errorx.New(errno.ErrSearchPermissionCode, errorx.KV("msg", "user can't search resources created by others"))
|
||||
}
|
||||
|
||||
tasks.Go(func() error {
|
||||
ri, err := s.packProjectResource(ctx, v)
|
||||
if err != nil {
|
||||
|
||||
@ -18,10 +18,13 @@ package shortcutcmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/coze-dev/coze-studio/backend/api/model/playground"
|
||||
"github.com/coze-dev/coze-studio/backend/application/base/ctxutil"
|
||||
|
||||
"github.com/coze-dev/coze-studio/backend/domain/permission"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/shortcutcmd/entity"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/shortcutcmd/service"
|
||||
"github.com/coze-dev/coze-studio/backend/pkg/lang/conv"
|
||||
@ -31,13 +34,54 @@ type ShortcutCmdApplicationService struct {
|
||||
ShortCutDomainSVC service.ShortcutCmd
|
||||
}
|
||||
|
||||
func checkPermission(ctx context.Context, uid int64, spaceID int64, workflowID int64) error {
|
||||
// Use permission service to check workspace access
|
||||
|
||||
rd := []*permission.ResourceIdentifier{
|
||||
{
|
||||
Type: permission.ResourceTypeWorkspace,
|
||||
ID: []int64{spaceID},
|
||||
Action: permission.ActionRead,
|
||||
},
|
||||
}
|
||||
if workflowID > 0 {
|
||||
rd = append(rd, &permission.ResourceIdentifier{
|
||||
Type: permission.ResourceTypeWorkflow,
|
||||
ID: []int64{workflowID},
|
||||
Action: permission.ActionRead,
|
||||
})
|
||||
}
|
||||
|
||||
result, err := permission.DefaultSVC().CheckAuthz(ctx, &permission.CheckAuthzData{
|
||||
ResourceIdentifier: rd,
|
||||
OperatorID: uid,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check workspace permission: %w", err)
|
||||
}
|
||||
|
||||
if result.Decision != permission.Allow {
|
||||
return fmt.Errorf("user %d does not have access to space %d", uid, spaceID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ShortcutCmdApplicationService) Handler(ctx context.Context, req *playground.CreateUpdateShortcutCommandRequest) (*playground.ShortcutCommand, error) {
|
||||
|
||||
var err error
|
||||
uid := ctxutil.MustGetUIDFromCtx(ctx)
|
||||
|
||||
cr, buildErr := s.buildReq(ctx, req)
|
||||
if buildErr != nil {
|
||||
return nil, buildErr
|
||||
}
|
||||
var err error
|
||||
|
||||
err = checkPermission(ctx, uid, req.GetSpaceID(), cr.WorkFlowID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var cmdDO *entity.ShortcutCmd
|
||||
if cr.CommandID > 0 {
|
||||
cmdDO, err = s.ShortCutDomainSVC.UpdateCMD(ctx, cr)
|
||||
|
||||
@ -26,6 +26,7 @@ import (
|
||||
"github.com/coze-dev/coze-studio/backend/api/model/playground"
|
||||
"github.com/coze-dev/coze-studio/backend/api/model/plugin_develop/common"
|
||||
"github.com/coze-dev/coze-studio/backend/api/model/workflow"
|
||||
"github.com/coze-dev/coze-studio/backend/application/base/ctxutil"
|
||||
"github.com/coze-dev/coze-studio/backend/bizpkg/config"
|
||||
"github.com/coze-dev/coze-studio/backend/bizpkg/config/modelmgr"
|
||||
knowledgeModel "github.com/coze-dev/coze-studio/backend/crossdomain/knowledge/model"
|
||||
@ -47,6 +48,9 @@ import (
|
||||
)
|
||||
|
||||
func (s *SingleAgentApplicationService) GetAgentBotInfo(ctx context.Context, req *playground.GetDraftBotInfoAgwRequest) (*playground.GetDraftBotInfoAgwResponse, error) {
|
||||
|
||||
uid := ctxutil.MustGetUIDFromCtx(ctx)
|
||||
|
||||
agentInfo, err := s.DomainSVC.GetSingleAgent(ctx, req.GetBotID(), req.GetVersion())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -56,6 +60,14 @@ func (s *SingleAgentApplicationService) GetAgentBotInfo(ctx context.Context, req
|
||||
return nil, errorx.New(errno.ErrAgentInvalidParamCode, errorx.KVf("msg", "agent %d not found", req.GetBotID()))
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if agentInfo.CreatorID != uid {
|
||||
return nil, errorx.New(errno.ErrAgentInvalidParamCode, errorx.KVf("msg", "agent %d not found", req.GetBotID()))
|
||||
}
|
||||
|
||||
vo, err := s.singleAgentDraftDo2Vo(ctx, agentInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@ -36,6 +36,7 @@ import (
|
||||
crossdatabase "github.com/coze-dev/coze-studio/backend/crossdomain/database"
|
||||
database "github.com/coze-dev/coze-studio/backend/crossdomain/database/model"
|
||||
pluginConsts "github.com/coze-dev/coze-studio/backend/crossdomain/plugin/consts"
|
||||
crossuser "github.com/coze-dev/coze-studio/backend/crossdomain/user"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/agent/singleagent/entity"
|
||||
singleagent "github.com/coze-dev/coze-studio/backend/domain/agent/singleagent/service"
|
||||
variableEntity "github.com/coze-dev/coze-studio/backend/domain/memory/variables/entity"
|
||||
@ -624,7 +625,36 @@ func (s *SingleAgentApplicationService) ListAgentPublishHistory(ctx context.Cont
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func checkUserSpace(ctx context.Context, uid int64, spaceID int64) error {
|
||||
spaces, err := crossuser.DefaultSVC().GetUserSpaceList(ctx, uid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var match bool
|
||||
for _, s := range spaces {
|
||||
if s.ID == spaceID {
|
||||
match = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !match {
|
||||
return fmt.Errorf("user %d does not have access to space %d", uid, spaceID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SingleAgentApplicationService) ReportUserBehavior(ctx context.Context, req *playground.ReportUserBehaviorRequest) (resp *playground.ReportUserBehaviorResponse, err error) {
|
||||
|
||||
uid := ctxutil.MustGetUIDFromCtx(ctx)
|
||||
|
||||
err = checkUserSpace(ctx, uid, req.GetSpaceID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = s.appContext.EventBus.PublishProject(ctx, &searchEntity.ProjectDomainEvent{
|
||||
OpType: searchEntity.Updated,
|
||||
Project: &searchEntity.ProjectDocument{
|
||||
|
||||
@ -33,12 +33,14 @@ import (
|
||||
"github.com/coze-dev/coze-studio/backend/application/base/ctxutil"
|
||||
"github.com/coze-dev/coze-studio/backend/bizpkg/debugutil"
|
||||
crossagentrun "github.com/coze-dev/coze-studio/backend/crossdomain/agentrun"
|
||||
|
||||
crossconversation "github.com/coze-dev/coze-studio/backend/crossdomain/conversation"
|
||||
crossmessage "github.com/coze-dev/coze-studio/backend/crossdomain/message"
|
||||
message "github.com/coze-dev/coze-studio/backend/crossdomain/message/model"
|
||||
crossupload "github.com/coze-dev/coze-studio/backend/crossdomain/upload"
|
||||
workflowModel "github.com/coze-dev/coze-studio/backend/crossdomain/workflow/model"
|
||||
agententity "github.com/coze-dev/coze-studio/backend/domain/conversation/agentrun/entity"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/permission"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/upload/service"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/workflow/entity"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/workflow/entity/vo"
|
||||
@ -469,6 +471,41 @@ func (w *ApplicationService) ListApplicationConversationDef(ctx context.Context,
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
func checkPermission(ctx context.Context, userID int64, workflowID int64, appID *int64, agentID *int64) error {
|
||||
rd := []*permission.ResourceIdentifier{
|
||||
{
|
||||
Type: permission.ResourceTypeWorkflow,
|
||||
ID: []int64{workflowID},
|
||||
Action: permission.ActionRead,
|
||||
},
|
||||
}
|
||||
if appID != nil {
|
||||
rd = append(rd, &permission.ResourceIdentifier{
|
||||
Type: permission.ResourceTypeApp,
|
||||
ID: []int64{*appID},
|
||||
Action: permission.ActionRead,
|
||||
})
|
||||
}
|
||||
if agentID != nil {
|
||||
rd = append(rd, &permission.ResourceIdentifier{
|
||||
Type: permission.ResourceTypeAgent,
|
||||
ID: []int64{*agentID},
|
||||
Action: permission.ActionRead,
|
||||
})
|
||||
}
|
||||
|
||||
checkResult, err := permission.DefaultSVC().CheckAuthz(ctx, &permission.CheckAuthzData{
|
||||
OperatorID: userID,
|
||||
ResourceIdentifier: rd,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if checkResult.Decision != permission.Allow {
|
||||
return errorx.New(errno.ErrMemoryPermissionCode, errorx.KV("msg", "no permission"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *ApplicationService) OpenAPIChatFlowRun(ctx context.Context, req *workflow.ChatFlowRunRequest) (
|
||||
_ *schema.StreamReader[[]*workflow.ChatFlowRunResponse], err error) {
|
||||
@ -520,6 +557,11 @@ func (w *ApplicationService) OpenAPIChatFlowRun(ctx context.Context, req *workfl
|
||||
connectorID = mustParseInt64(req.GetConnectorID())
|
||||
}
|
||||
|
||||
err = checkPermission(ctx, userID, workflowID, appID, agentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if req.IsSetAppID() {
|
||||
appID = ptr.Of(mustParseInt64(req.GetAppID()))
|
||||
bizID = mustParseInt64(req.GetAppID())
|
||||
@ -1222,7 +1264,22 @@ func (w *ApplicationService) OpenAPICreateConversation(ctx context.Context, req
|
||||
//_ = spaceID
|
||||
)
|
||||
|
||||
// todo check permission
|
||||
checkResult, err := permission.DefaultSVC().CheckAuthz(ctx, &permission.CheckAuthzData{
|
||||
OperatorID: userID,
|
||||
ResourceIdentifier: []*permission.ResourceIdentifier{
|
||||
{
|
||||
Type: permission.ResourceTypeWorkflow,
|
||||
ID: []int64{mustParseInt64(req.GetWorkflowID())},
|
||||
Action: permission.ActionRead,
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if checkResult.Decision != permission.Allow {
|
||||
return nil, errorx.New(errno.ErrMemoryPermissionCode, errorx.KV("msg", "no permission"))
|
||||
}
|
||||
|
||||
if !req.GetGetOrCreate() {
|
||||
cID, err = GetWorkflowDomainSVC().UpdateConversation(ctx, env, appID, req.GetConnectorId(), userID, req.GetConversationMame())
|
||||
|
||||
@ -45,9 +45,11 @@ import (
|
||||
"github.com/coze-dev/coze-studio/backend/bizpkg/debugutil"
|
||||
crossknowledge "github.com/coze-dev/coze-studio/backend/crossdomain/knowledge"
|
||||
model "github.com/coze-dev/coze-studio/backend/crossdomain/knowledge/model"
|
||||
crosspermission "github.com/coze-dev/coze-studio/backend/crossdomain/permission"
|
||||
pluginConsts "github.com/coze-dev/coze-studio/backend/crossdomain/plugin/consts"
|
||||
crossuser "github.com/coze-dev/coze-studio/backend/crossdomain/user"
|
||||
workflowModel "github.com/coze-dev/coze-studio/backend/crossdomain/workflow/model"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/permission"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/plugin/dto"
|
||||
search "github.com/coze-dev/coze-studio/backend/domain/search/entity"
|
||||
domainWorkflow "github.com/coze-dev/coze-studio/backend/domain/workflow"
|
||||
@ -1623,6 +1625,25 @@ func (w *ApplicationService) OpenAPIStreamResume(ctx context.Context, req *workf
|
||||
apiKeyInfo := ctxutil.GetApiAuthFromCtx(ctx)
|
||||
userID := apiKeyInfo.UserID
|
||||
|
||||
checkResult, err := crosspermission.DefaultSVC().CheckAuthz(ctx, &permission.CheckAuthzData{
|
||||
OperatorID: userID,
|
||||
ResourceIdentifier: []*permission.ResourceIdentifier{
|
||||
{
|
||||
Type: permission.ResourceTypeWorkflow,
|
||||
ID: []int64{workflowID},
|
||||
Action: permission.ActionRead,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if checkResult.Decision != permission.Allow {
|
||||
return nil, errorx.New(errno.ErrMemoryPermissionCode, errorx.KV("msg", "no permission"))
|
||||
}
|
||||
|
||||
var connectorID int64
|
||||
if req.IsSetConnectorID() {
|
||||
connectorID = mustParseInt64(req.GetConnectorID())
|
||||
|
||||
@ -33,6 +33,7 @@ type SingleAgent interface {
|
||||
StreamExecute(ctx context.Context,
|
||||
agentRuntime *AgentRuntime) (*schema.StreamReader[*model.AgentEvent], error)
|
||||
ObtainAgentByIdentity(ctx context.Context, identity *model.AgentIdentity) (*model.SingleAgent, error)
|
||||
GetSingleAgentDraft(ctx context.Context, agentID int64) (agentInfo *model.SingleAgent, err error)
|
||||
}
|
||||
|
||||
type AgentRuntime struct {
|
||||
|
||||
@ -106,3 +106,14 @@ func (c *impl) ObtainAgentByIdentity(ctx context.Context, identity *model.AgentI
|
||||
}
|
||||
return agentInfo.SingleAgent, nil
|
||||
}
|
||||
|
||||
func (c *impl) GetSingleAgentDraft(ctx context.Context, agentID int64) (*model.SingleAgent, error) {
|
||||
agentInfo, err := c.DomainSVC.GetSingleAgentDraft(ctx, agentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if agentInfo == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return agentInfo.SingleAgent, nil
|
||||
}
|
||||
|
||||
37
backend/crossdomain/app/contract.go
Normal file
37
backend/crossdomain/app/contract.go
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/coze-dev/coze-studio/backend/domain/app/entity"
|
||||
)
|
||||
|
||||
var defaultSVC AppService
|
||||
|
||||
func DefaultSVC() AppService {
|
||||
return defaultSVC
|
||||
}
|
||||
|
||||
func SetDefaultSVC(c AppService) {
|
||||
defaultSVC = c
|
||||
}
|
||||
|
||||
type AppService interface {
|
||||
GetDraftAPP(ctx context.Context, appID int64) (app *entity.APP, err error)
|
||||
}
|
||||
39
backend/crossdomain/app/impl/app.go
Normal file
39
backend/crossdomain/app/impl/app.go
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package crossapp
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
crossapp "github.com/coze-dev/coze-studio/backend/crossdomain/app"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/app/entity"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/app/service"
|
||||
)
|
||||
|
||||
type appServiceImpl struct {
|
||||
DomainSVC service.AppService
|
||||
}
|
||||
|
||||
func InitDomainService(domainSVC service.AppService) crossapp.AppService {
|
||||
return &appServiceImpl{
|
||||
DomainSVC: domainSVC,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *appServiceImpl) GetDraftAPP(ctx context.Context, appID int64) (app *entity.APP, err error) {
|
||||
return a.DomainSVC.GetDraftAPP(ctx, appID)
|
||||
}
|
||||
129
backend/crossdomain/app/model/app.go
Normal file
129
backend/crossdomain/app/model/app.go
Normal file
@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/coze-dev/coze-studio/backend/api/model/app/intelligence/common"
|
||||
publishAPI "github.com/coze-dev/coze-studio/backend/api/model/app/intelligence/publish"
|
||||
resourceCommon "github.com/coze-dev/coze-studio/backend/api/model/resource/common"
|
||||
"github.com/coze-dev/coze-studio/backend/pkg/lang/ptr"
|
||||
)
|
||||
|
||||
type APP struct {
|
||||
ID int64
|
||||
SpaceID int64
|
||||
IconURI *string
|
||||
Name *string
|
||||
Desc *string
|
||||
OwnerID int64
|
||||
|
||||
ConnectorIDs []int64
|
||||
Version *string
|
||||
VersionDesc *string
|
||||
PublishRecordID *int64
|
||||
PublishStatus *PublishStatus
|
||||
PublishExtraInfo *PublishRecordExtraInfo
|
||||
|
||||
CreatedAtMS int64
|
||||
UpdatedAtMS int64
|
||||
PublishedAtMS *int64
|
||||
}
|
||||
|
||||
func (a APP) Published() bool {
|
||||
return a.PublishStatus != nil && *a.PublishStatus == PublishStatusOfPublishDone
|
||||
}
|
||||
|
||||
func (a APP) GetPublishedAtMS() int64 {
|
||||
return ptr.FromOrDefault(a.PublishedAtMS, 0)
|
||||
}
|
||||
|
||||
func (a APP) GetVersion() string {
|
||||
return ptr.FromOrDefault(a.Version, "")
|
||||
}
|
||||
|
||||
func (a APP) GetName() string {
|
||||
return ptr.FromOrDefault(a.Name, "")
|
||||
}
|
||||
|
||||
func (a APP) GetDesc() string {
|
||||
return ptr.FromOrDefault(a.Desc, "")
|
||||
}
|
||||
|
||||
func (a APP) GetVersionDesc() string {
|
||||
return ptr.FromOrDefault(a.VersionDesc, "")
|
||||
}
|
||||
|
||||
func (a APP) GetIconURI() string {
|
||||
return ptr.FromOrDefault(a.IconURI, "")
|
||||
}
|
||||
|
||||
func (a APP) GetPublishStatus() PublishStatus {
|
||||
return ptr.FromOrDefault(a.PublishStatus, 0)
|
||||
}
|
||||
|
||||
func (a APP) GetPublishRecordID() int64 {
|
||||
return ptr.FromOrDefault(a.PublishRecordID, 0)
|
||||
}
|
||||
|
||||
type PublishRecord struct {
|
||||
APP *APP
|
||||
ConnectorPublishRecords []*ConnectorPublishRecord
|
||||
}
|
||||
|
||||
type PublishRecordExtraInfo struct {
|
||||
PackFailedInfo []*PackResourceFailedInfo `json:"pack_failed_info,omitempty"`
|
||||
}
|
||||
|
||||
func (p *PublishRecordExtraInfo) ToVO() *publishAPI.PublishRecordStatusDetail {
|
||||
if p == nil || len(p.PackFailedInfo) == 0 {
|
||||
return &publishAPI.PublishRecordStatusDetail{}
|
||||
}
|
||||
|
||||
packFailedDetail := make([]*publishAPI.PackFailedDetail, 0, len(p.PackFailedInfo))
|
||||
for _, info := range p.PackFailedInfo {
|
||||
packFailedDetail = append(packFailedDetail, &publishAPI.PackFailedDetail{
|
||||
EntityID: info.ResID,
|
||||
EntityType: common.ResourceType(info.ResType),
|
||||
EntityName: info.ResName,
|
||||
})
|
||||
}
|
||||
|
||||
return &publishAPI.PublishRecordStatusDetail{
|
||||
PackFailedDetail: packFailedDetail,
|
||||
}
|
||||
}
|
||||
|
||||
type PackResourceFailedInfo struct {
|
||||
ResID int64 `json:"res_id"`
|
||||
ResType resourceCommon.ResType `json:"res_type"`
|
||||
ResName string `json:"res_name"`
|
||||
}
|
||||
|
||||
type ResourceCopyResult struct {
|
||||
ResID int64 `json:"res_id"`
|
||||
ResType ResourceType `json:"res_type"`
|
||||
ResName string `json:"res_name"`
|
||||
CopyStatus ResourceCopyStatus `json:"copy_status"`
|
||||
CopyScene resourceCommon.ResourceCopyScene `json:"copy_scene"`
|
||||
FailedReason string `json:"reason"`
|
||||
}
|
||||
|
||||
type Resource struct {
|
||||
ResID int64
|
||||
ResType ResourceType
|
||||
ResName string
|
||||
}
|
||||
61
backend/crossdomain/app/model/connector.go
Normal file
61
backend/crossdomain/app/model/connector.go
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
publishAPI "github.com/coze-dev/coze-studio/backend/api/model/app/intelligence/publish"
|
||||
"github.com/coze-dev/coze-studio/backend/types/consts"
|
||||
)
|
||||
|
||||
var ConnectorIDWhiteList = []int64{
|
||||
consts.WebSDKConnectorID,
|
||||
consts.APIConnectorID,
|
||||
}
|
||||
|
||||
type ConnectorPublishRecord struct {
|
||||
ConnectorID int64 `json:"connector_id"`
|
||||
PublishStatus ConnectorPublishStatus `json:"publish_status"`
|
||||
PublishConfig PublishConfig `json:"publish_config"`
|
||||
}
|
||||
|
||||
type PublishConfig struct {
|
||||
SelectedWorkflows []*SelectedWorkflow `json:"selected_workflows,omitempty"`
|
||||
}
|
||||
|
||||
func (p *PublishConfig) ToVO() *publishAPI.ConnectorPublishConfig {
|
||||
config := &publishAPI.ConnectorPublishConfig{
|
||||
SelectedWorkflows: make([]*publishAPI.SelectedWorkflow, 0, len(p.SelectedWorkflows)),
|
||||
}
|
||||
|
||||
if p == nil {
|
||||
return config
|
||||
}
|
||||
|
||||
for _, w := range p.SelectedWorkflows {
|
||||
config.SelectedWorkflows = append(config.SelectedWorkflows, &publishAPI.SelectedWorkflow{
|
||||
WorkflowID: w.WorkflowID,
|
||||
WorkflowName: w.WorkflowName,
|
||||
})
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
type SelectedWorkflow struct {
|
||||
WorkflowID int64 `json:"workflow_id"`
|
||||
WorkflowName string `json:"workflow_name"`
|
||||
}
|
||||
55
backend/crossdomain/app/model/consts.go
Normal file
55
backend/crossdomain/app/model/consts.go
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package model
|
||||
|
||||
type PublishStatus int
|
||||
|
||||
const (
|
||||
PublishStatusOfPacking PublishStatus = 0
|
||||
PublishStatusOfPackFailed PublishStatus = 1
|
||||
PublishStatusOfAuditing PublishStatus = 2
|
||||
PublishStatusOfAuditNotPass PublishStatus = 3
|
||||
PublishStatusOfConnectorPublishing PublishStatus = 4
|
||||
PublishStatusOfPublishDone PublishStatus = 5
|
||||
)
|
||||
|
||||
type ConnectorPublishStatus int
|
||||
|
||||
const (
|
||||
ConnectorPublishStatusOfDefault ConnectorPublishStatus = 0
|
||||
ConnectorPublishStatusOfAuditing ConnectorPublishStatus = 1
|
||||
ConnectorPublishStatusOfSuccess ConnectorPublishStatus = 2
|
||||
ConnectorPublishStatusOfFailed ConnectorPublishStatus = 3
|
||||
ConnectorPublishStatusOfDisable ConnectorPublishStatus = 4
|
||||
)
|
||||
|
||||
type ResourceType string
|
||||
|
||||
const (
|
||||
ResourceTypeOfPlugin ResourceType = "plugin"
|
||||
ResourceTypeOfWorkflow ResourceType = "workflow"
|
||||
ResourceTypeOfKnowledge ResourceType = "knowledge"
|
||||
ResourceTypeOfDatabase ResourceType = "database"
|
||||
)
|
||||
|
||||
type ResourceCopyStatus int
|
||||
|
||||
const (
|
||||
ResourceCopyStatusOfSuccess ResourceCopyStatus = 1
|
||||
ResourceCopyStatusOfProcessing ResourceCopyStatus = 2
|
||||
ResourceCopyStatusOfFailed ResourceCopyStatus = 3
|
||||
)
|
||||
@ -32,6 +32,8 @@ type Knowledge interface {
|
||||
Store(ctx context.Context, document *model.CreateDocumentRequest) (*model.CreateDocumentResponse, error)
|
||||
Delete(ctx context.Context, r *model.DeleteDocumentRequest) (*model.DeleteDocumentResponse, error)
|
||||
ListKnowledgeDetail(ctx context.Context, req *model.ListKnowledgeDetailRequest) (*model.ListKnowledgeDetailResponse, error)
|
||||
MGetSlice(ctx context.Context, request *model.MGetSliceRequest) (response *model.MGetSliceResponse, err error)
|
||||
MGetDocument(ctx context.Context, request *model.MGetDocumentRequest) (response *model.MGetDocumentResponse, err error)
|
||||
}
|
||||
|
||||
var defaultSVC Knowledge
|
||||
|
||||
@ -181,6 +181,49 @@ func (i *impl) ListKnowledgeDetail(ctx context.Context, req *model.ListKnowledge
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (i *impl) MGetSlice(ctx context.Context, request *model.MGetSliceRequest) (response *model.MGetSliceResponse, err error) {
|
||||
resp, err := i.DomainSVC.MGetSlice(ctx, &service.MGetSliceRequest{
|
||||
SliceIDs: request.SliceIDs,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &model.MGetSliceResponse{
|
||||
Slices: slices.Transform(resp.Slices, func(a *entity.Slice) *model.Slice {
|
||||
return &model.Slice{
|
||||
Info: model.Info{
|
||||
ID: a.ID,
|
||||
CreatorID: a.CreatorID,
|
||||
SpaceID: a.SpaceID,
|
||||
},
|
||||
KnowledgeID: a.KnowledgeID,
|
||||
DocumentID: a.DocumentID,
|
||||
}
|
||||
}),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (i *impl) MGetDocument(ctx context.Context, request *model.MGetDocumentRequest) (response *model.MGetDocumentResponse, err error) {
|
||||
resp, err := i.DomainSVC.MGetDocument(ctx, &service.MGetDocumentRequest{
|
||||
DocumentIDs: request.DocumentIDs,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &model.MGetDocumentResponse{
|
||||
Documents: slices.Transform(resp.Documents, func(a *entity.Document) *model.Document {
|
||||
return &model.Document{
|
||||
ID: a.ID,
|
||||
Name: a.Name,
|
||||
CreatorID: a.CreatorID,
|
||||
SpaceID: a.SpaceID,
|
||||
}
|
||||
}),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func toChunkType(typ model.ChunkType) (parser.ChunkType, error) {
|
||||
switch typ {
|
||||
case model.ChunkTypeDefault:
|
||||
|
||||
@ -115,6 +115,21 @@ func (mr *MockKnowledgeMockRecorder) ListKnowledgeDetail(ctx, req any) *gomock.C
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListKnowledgeDetail", reflect.TypeOf((*MockKnowledge)(nil).ListKnowledgeDetail), ctx, req)
|
||||
}
|
||||
|
||||
// MGetDocument mocks base method.
|
||||
func (m *MockKnowledge) MGetDocument(ctx context.Context, request *knowledge.MGetDocumentRequest) (*knowledge.MGetDocumentResponse, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "MGetDocument", ctx, request)
|
||||
ret0, _ := ret[0].(*knowledge.MGetDocumentResponse)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// MGetDocument indicates an expected call of MGetDocument.
|
||||
func (mr *MockKnowledgeMockRecorder) MGetDocument(ctx, request any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MGetDocument", reflect.TypeOf((*MockKnowledge)(nil).MGetDocument), ctx, request)
|
||||
}
|
||||
|
||||
// MGetKnowledgeByID mocks base method.
|
||||
func (m *MockKnowledge) MGetKnowledgeByID(ctx context.Context, request *knowledge.MGetKnowledgeByIDRequest) (*knowledge.MGetKnowledgeByIDResponse, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@ -130,6 +145,21 @@ func (mr *MockKnowledgeMockRecorder) MGetKnowledgeByID(ctx, request any) *gomock
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MGetKnowledgeByID", reflect.TypeOf((*MockKnowledge)(nil).MGetKnowledgeByID), ctx, request)
|
||||
}
|
||||
|
||||
// MGetSlice mocks base method.
|
||||
func (m *MockKnowledge) MGetSlice(ctx context.Context, request *knowledge.MGetSliceRequest) (*knowledge.MGetSliceResponse, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "MGetSlice", ctx, request)
|
||||
ret0, _ := ret[0].(*knowledge.MGetSliceResponse)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// MGetSlice indicates an expected call of MGetSlice.
|
||||
func (mr *MockKnowledgeMockRecorder) MGetSlice(ctx, request any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MGetSlice", reflect.TypeOf((*MockKnowledge)(nil).MGetSlice), ctx, request)
|
||||
}
|
||||
|
||||
// Retrieve mocks base method.
|
||||
func (m *MockKnowledge) Retrieve(ctx context.Context, req *knowledge.RetrieveRequest) (*knowledge.RetrieveResponse, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
||||
@ -347,3 +347,26 @@ type ListKnowledgeDetailRequest struct {
|
||||
type ListKnowledgeDetailResponse struct {
|
||||
KnowledgeDetails []*KnowledgeDetail
|
||||
}
|
||||
|
||||
type MGetSliceRequest struct {
|
||||
SliceIDs []int64
|
||||
}
|
||||
|
||||
type MGetSliceResponse struct {
|
||||
Slices []*Slice
|
||||
}
|
||||
|
||||
type MGetDocumentRequest struct {
|
||||
DocumentIDs []int64
|
||||
}
|
||||
|
||||
type Document struct {
|
||||
ID int64
|
||||
Name string
|
||||
CreatorID int64
|
||||
SpaceID int64
|
||||
}
|
||||
|
||||
type MGetDocumentResponse struct {
|
||||
Documents []*Document
|
||||
}
|
||||
|
||||
37
backend/crossdomain/permission/contract.go
Normal file
37
backend/crossdomain/permission/contract.go
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package permission
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/coze-dev/coze-studio/backend/crossdomain/permission/model"
|
||||
)
|
||||
|
||||
var defaultSVC Permission
|
||||
|
||||
func DefaultSVC() Permission {
|
||||
return defaultSVC
|
||||
}
|
||||
|
||||
func SetDefaultSVC(c Permission) {
|
||||
defaultSVC = c
|
||||
}
|
||||
|
||||
type Permission interface {
|
||||
CheckAuthz(ctx context.Context, req *model.CheckAuthzData) (*model.CheckAuthzResult, error)
|
||||
}
|
||||
39
backend/crossdomain/permission/impl/permission.go
Normal file
39
backend/crossdomain/permission/impl/permission.go
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package crosspermission
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
crosspermission "github.com/coze-dev/coze-studio/backend/crossdomain/permission"
|
||||
"github.com/coze-dev/coze-studio/backend/crossdomain/permission/model"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/permission"
|
||||
)
|
||||
|
||||
type impl struct {
|
||||
DomainSVC permission.Permission
|
||||
}
|
||||
|
||||
func InitDomainService(domainSVC permission.Permission) crosspermission.Permission {
|
||||
return &impl{
|
||||
DomainSVC: domainSVC,
|
||||
}
|
||||
}
|
||||
|
||||
func (i *impl) CheckAuthz(ctx context.Context, req *model.CheckAuthzData) (*model.CheckAuthzResult, error) {
|
||||
return i.DomainSVC.CheckAuthz(ctx, req)
|
||||
}
|
||||
22
backend/crossdomain/permission/model/permission.go
Normal file
22
backend/crossdomain/permission/model/permission.go
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package model
|
||||
|
||||
import "github.com/coze-dev/coze-studio/backend/domain/permission"
|
||||
|
||||
type CheckAuthzData = permission.CheckAuthzData
|
||||
type CheckAuthzResult = permission.CheckAuthzResult
|
||||
@ -0,0 +1,57 @@
|
||||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: contract.go
|
||||
//
|
||||
// Generated by this command:
|
||||
//
|
||||
// mockgen -destination permissionmock/permission_mock.go --package permissionmock -source contract.go
|
||||
//
|
||||
|
||||
// Package permissionmock is a generated GoMock package.
|
||||
package permissionmock
|
||||
|
||||
import (
|
||||
context "context"
|
||||
reflect "reflect"
|
||||
|
||||
model "github.com/coze-dev/coze-studio/backend/crossdomain/permission/model"
|
||||
gomock "go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
// MockPermission is a mock of Permission interface.
|
||||
type MockPermission struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockPermissionMockRecorder
|
||||
isgomock struct{}
|
||||
}
|
||||
|
||||
// MockPermissionMockRecorder is the mock recorder for MockPermission.
|
||||
type MockPermissionMockRecorder struct {
|
||||
mock *MockPermission
|
||||
}
|
||||
|
||||
// NewMockPermission creates a new mock instance.
|
||||
func NewMockPermission(ctrl *gomock.Controller) *MockPermission {
|
||||
mock := &MockPermission{ctrl: ctrl}
|
||||
mock.recorder = &MockPermissionMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockPermission) EXPECT() *MockPermissionMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// CheckAuthz mocks base method.
|
||||
func (m *MockPermission) CheckAuthz(ctx context.Context, req *model.CheckAuthzData) (*model.CheckAuthzResult, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "CheckAuthz", ctx, req)
|
||||
ret0, _ := ret[0].(*model.CheckAuthzResult)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// CheckAuthz indicates an expected call of CheckAuthz.
|
||||
func (mr *MockPermissionMockRecorder) CheckAuthz(ctx, req any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckAuthz", reflect.TypeOf((*MockPermission)(nil).CheckAuthz), ctx, req)
|
||||
}
|
||||
@ -24,9 +24,10 @@ import (
|
||||
|
||||
type EntitySpace = entity.Space
|
||||
|
||||
//go:generate mockgen -destination ../../../internal/mock/crossdomain/crossuser/crossuser.go --package mockCrossUser -source crossuser.go
|
||||
//go:generate mockgen -destination ../../internal/mock/crossdomain/crossuser/crossuser.go --package mockCrossUser -source contract.go
|
||||
type User interface {
|
||||
GetUserSpaceList(ctx context.Context, userID int64) (spaces []*EntitySpace, err error)
|
||||
GetUserSpaceBySpaceID(ctx context.Context, spaceID []int64) (space []*EntitySpace, err error)
|
||||
}
|
||||
|
||||
var defaultSVC User
|
||||
|
||||
@ -40,3 +40,7 @@ func InitDomainService(u service.User) crossuser.User {
|
||||
func (u *impl) GetUserSpaceList(ctx context.Context, userID int64) (spaces []*entity.Space, err error) {
|
||||
return u.DomainSVC.GetUserSpaceList(ctx, userID)
|
||||
}
|
||||
|
||||
func (u *impl) GetUserSpaceBySpaceID(ctx context.Context, spaceID []int64) (space []*entity.Space, err error) {
|
||||
return u.DomainSVC.GetUserSpaceBySpaceID(ctx, spaceID)
|
||||
}
|
||||
|
||||
@ -45,6 +45,7 @@ type Workflow interface {
|
||||
WithMessagePipe() (compose.Option, *schema.StreamReader[*entity.Message], func())
|
||||
StreamResume(ctx context.Context, req *entity.ResumeRequest, config workflowModel.ExecuteConfig) (*schema.StreamReader[*entity.Message], error)
|
||||
InitApplicationDefaultConversationTemplate(ctx context.Context, spaceID int64, appID int64, userID int64) error
|
||||
MGet(ctx context.Context, policy *vo.MGetPolicy) ([]*entity.Workflow, int64, error)
|
||||
}
|
||||
|
||||
type ExecuteConfig = workflowModel.ExecuteConfig
|
||||
|
||||
@ -95,3 +95,7 @@ func (i *impl) GetWorkflowIDsByAppID(ctx context.Context, appID int64) ([]int64,
|
||||
return a.ID
|
||||
}), err
|
||||
}
|
||||
|
||||
func (i *impl) MGet(ctx context.Context, policy *vo.MGetPolicy) ([]*entity.Workflow, int64, error) {
|
||||
return i.DomainSVC.MGet(ctx, policy)
|
||||
}
|
||||
|
||||
@ -17,113 +17,17 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"github.com/coze-dev/coze-studio/backend/api/model/app/intelligence/common"
|
||||
publishAPI "github.com/coze-dev/coze-studio/backend/api/model/app/intelligence/publish"
|
||||
resourceCommon "github.com/coze-dev/coze-studio/backend/api/model/resource/common"
|
||||
"github.com/coze-dev/coze-studio/backend/pkg/lang/ptr"
|
||||
"github.com/coze-dev/coze-studio/backend/crossdomain/app/model"
|
||||
)
|
||||
|
||||
type APP struct {
|
||||
ID int64
|
||||
SpaceID int64
|
||||
IconURI *string
|
||||
Name *string
|
||||
Desc *string
|
||||
OwnerID int64
|
||||
type APP = model.APP
|
||||
|
||||
ConnectorIDs []int64
|
||||
Version *string
|
||||
VersionDesc *string
|
||||
PublishRecordID *int64
|
||||
PublishStatus *PublishStatus
|
||||
PublishExtraInfo *PublishRecordExtraInfo
|
||||
type PublishRecord = model.PublishRecord
|
||||
|
||||
CreatedAtMS int64
|
||||
UpdatedAtMS int64
|
||||
PublishedAtMS *int64
|
||||
}
|
||||
type PublishRecordExtraInfo = model.PublishRecordExtraInfo
|
||||
|
||||
func (a APP) Published() bool {
|
||||
return a.PublishStatus != nil && *a.PublishStatus == PublishStatusOfPublishDone
|
||||
}
|
||||
type PackResourceFailedInfo = model.PackResourceFailedInfo
|
||||
|
||||
func (a APP) GetPublishedAtMS() int64 {
|
||||
return ptr.FromOrDefault(a.PublishedAtMS, 0)
|
||||
}
|
||||
type ResourceCopyResult = model.ResourceCopyResult
|
||||
|
||||
func (a APP) GetVersion() string {
|
||||
return ptr.FromOrDefault(a.Version, "")
|
||||
}
|
||||
|
||||
func (a APP) GetName() string {
|
||||
return ptr.FromOrDefault(a.Name, "")
|
||||
}
|
||||
|
||||
func (a APP) GetDesc() string {
|
||||
return ptr.FromOrDefault(a.Desc, "")
|
||||
}
|
||||
|
||||
func (a APP) GetVersionDesc() string {
|
||||
return ptr.FromOrDefault(a.VersionDesc, "")
|
||||
}
|
||||
|
||||
func (a APP) GetIconURI() string {
|
||||
return ptr.FromOrDefault(a.IconURI, "")
|
||||
}
|
||||
|
||||
func (a APP) GetPublishStatus() PublishStatus {
|
||||
return ptr.FromOrDefault(a.PublishStatus, 0)
|
||||
}
|
||||
|
||||
func (a APP) GetPublishRecordID() int64 {
|
||||
return ptr.FromOrDefault(a.PublishRecordID, 0)
|
||||
}
|
||||
|
||||
type PublishRecord struct {
|
||||
APP *APP
|
||||
ConnectorPublishRecords []*ConnectorPublishRecord
|
||||
}
|
||||
|
||||
type PublishRecordExtraInfo struct {
|
||||
PackFailedInfo []*PackResourceFailedInfo `json:"pack_failed_info,omitempty"`
|
||||
}
|
||||
|
||||
func (p *PublishRecordExtraInfo) ToVO() *publishAPI.PublishRecordStatusDetail {
|
||||
if p == nil || len(p.PackFailedInfo) == 0 {
|
||||
return &publishAPI.PublishRecordStatusDetail{}
|
||||
}
|
||||
|
||||
packFailedDetail := make([]*publishAPI.PackFailedDetail, 0, len(p.PackFailedInfo))
|
||||
for _, info := range p.PackFailedInfo {
|
||||
packFailedDetail = append(packFailedDetail, &publishAPI.PackFailedDetail{
|
||||
EntityID: info.ResID,
|
||||
EntityType: common.ResourceType(info.ResType),
|
||||
EntityName: info.ResName,
|
||||
})
|
||||
}
|
||||
|
||||
return &publishAPI.PublishRecordStatusDetail{
|
||||
PackFailedDetail: packFailedDetail,
|
||||
}
|
||||
}
|
||||
|
||||
type PackResourceFailedInfo struct {
|
||||
ResID int64 `json:"res_id"`
|
||||
ResType resourceCommon.ResType `json:"res_type"`
|
||||
ResName string `json:"res_name"`
|
||||
}
|
||||
|
||||
type ResourceCopyResult struct {
|
||||
ResID int64 `json:"res_id"`
|
||||
ResType ResourceType `json:"res_type"`
|
||||
ResName string `json:"res_name"`
|
||||
CopyStatus ResourceCopyStatus `json:"copy_status"`
|
||||
CopyScene resourceCommon.ResourceCopyScene `json:"copy_scene"`
|
||||
FailedReason string `json:"reason"`
|
||||
}
|
||||
|
||||
type Resource struct {
|
||||
ResID int64
|
||||
ResType ResourceType
|
||||
ResName string
|
||||
}
|
||||
type Resource = model.Resource
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
publishAPI "github.com/coze-dev/coze-studio/backend/api/model/app/intelligence/publish"
|
||||
"github.com/coze-dev/coze-studio/backend/crossdomain/app/model"
|
||||
"github.com/coze-dev/coze-studio/backend/types/consts"
|
||||
)
|
||||
|
||||
@ -26,36 +26,6 @@ var ConnectorIDWhiteList = []int64{
|
||||
consts.APIConnectorID,
|
||||
}
|
||||
|
||||
type ConnectorPublishRecord struct {
|
||||
ConnectorID int64 `json:"connector_id"`
|
||||
PublishStatus ConnectorPublishStatus `json:"publish_status"`
|
||||
PublishConfig PublishConfig `json:"publish_config"`
|
||||
}
|
||||
|
||||
type PublishConfig struct {
|
||||
SelectedWorkflows []*SelectedWorkflow `json:"selected_workflows,omitempty"`
|
||||
}
|
||||
|
||||
func (p *PublishConfig) ToVO() *publishAPI.ConnectorPublishConfig {
|
||||
config := &publishAPI.ConnectorPublishConfig{
|
||||
SelectedWorkflows: make([]*publishAPI.SelectedWorkflow, 0, len(p.SelectedWorkflows)),
|
||||
}
|
||||
|
||||
if p == nil {
|
||||
return config
|
||||
}
|
||||
|
||||
for _, w := range p.SelectedWorkflows {
|
||||
config.SelectedWorkflows = append(config.SelectedWorkflows, &publishAPI.SelectedWorkflow{
|
||||
WorkflowID: w.WorkflowID,
|
||||
WorkflowName: w.WorkflowName,
|
||||
})
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
type SelectedWorkflow struct {
|
||||
WorkflowID int64 `json:"workflow_id"`
|
||||
WorkflowName string `json:"workflow_name"`
|
||||
}
|
||||
type ConnectorPublishRecord = model.ConnectorPublishRecord
|
||||
type PublishConfig = model.PublishConfig
|
||||
type SelectedWorkflow = model.SelectedWorkflow
|
||||
|
||||
@ -16,7 +16,9 @@
|
||||
|
||||
package entity
|
||||
|
||||
type PublishStatus int
|
||||
import "github.com/coze-dev/coze-studio/backend/crossdomain/app/model"
|
||||
|
||||
type PublishStatus = model.PublishStatus
|
||||
|
||||
const (
|
||||
PublishStatusOfPacking PublishStatus = 0
|
||||
@ -27,7 +29,7 @@ const (
|
||||
PublishStatusOfPublishDone PublishStatus = 5
|
||||
)
|
||||
|
||||
type ConnectorPublishStatus int
|
||||
type ConnectorPublishStatus = model.ConnectorPublishStatus
|
||||
|
||||
const (
|
||||
ConnectorPublishStatusOfDefault ConnectorPublishStatus = 0
|
||||
@ -37,7 +39,7 @@ const (
|
||||
ConnectorPublishStatusOfDisable ConnectorPublishStatus = 4
|
||||
)
|
||||
|
||||
type ResourceType string
|
||||
type ResourceType = model.ResourceType
|
||||
|
||||
const (
|
||||
ResourceTypeOfPlugin ResourceType = "plugin"
|
||||
@ -46,7 +48,7 @@ const (
|
||||
ResourceTypeOfDatabase ResourceType = "database"
|
||||
)
|
||||
|
||||
type ResourceCopyStatus int
|
||||
type ResourceCopyStatus = model.ResourceCopyStatus
|
||||
|
||||
const (
|
||||
ResourceCopyStatusOfSuccess ResourceCopyStatus = 1
|
||||
|
||||
@ -57,6 +57,8 @@ type Knowledge interface {
|
||||
ListSlice(ctx context.Context, request *ListSliceRequest) (response *ListSliceResponse, err error)
|
||||
ListPhotoSlice(ctx context.Context, request *ListPhotoSliceRequest) (response *ListPhotoSliceResponse, err error)
|
||||
GetSlice(ctx context.Context, request *GetSliceRequest) (response *GetSliceResponse, err error)
|
||||
MGetSlice(ctx context.Context, request *MGetSliceRequest) (response *MGetSliceResponse, err error)
|
||||
MGetDocument(ctx context.Context, request *MGetDocumentRequest) (response *MGetDocumentResponse, err error)
|
||||
Retrieve(ctx context.Context, request *RetrieveRequest) (response *RetrieveResponse, err error)
|
||||
CreateDocumentReview(ctx context.Context, request *CreateDocumentReviewRequest) (response *CreateDocumentReviewResponse, err error)
|
||||
MGetDocumentReview(ctx context.Context, request *MGetDocumentReviewRequest) (response *MGetDocumentReviewResponse, err error)
|
||||
@ -354,5 +356,21 @@ type ExtractPhotoCaptionRequest struct {
|
||||
type ExtractPhotoCaptionResponse struct {
|
||||
Caption string
|
||||
}
|
||||
|
||||
type MGetSliceRequest struct {
|
||||
SliceIDs []int64
|
||||
}
|
||||
|
||||
type MGetSliceResponse struct {
|
||||
Slices []*entity.Slice
|
||||
}
|
||||
|
||||
type MGetDocumentRequest struct {
|
||||
DocumentIDs []int64
|
||||
}
|
||||
|
||||
type MGetDocumentResponse struct {
|
||||
Documents []*entity.Document
|
||||
}
|
||||
type MGetKnowledgeByIDRequest = knowledge.MGetKnowledgeByIDRequest
|
||||
type MGetKnowledgeByIDResponse = knowledge.MGetKnowledgeByIDResponse
|
||||
|
||||
@ -1503,3 +1503,45 @@ func (k *knowledgeSVC) genMultiIDs(ctx context.Context, counts int) ([]int64, er
|
||||
}
|
||||
return allIDs, nil
|
||||
}
|
||||
|
||||
func (k *knowledgeSVC) MGetSlice(ctx context.Context, request *MGetSliceRequest) (response *MGetSliceResponse, err error) {
|
||||
slices, err := k.sliceRepo.MGetSlices(ctx, request.SliceIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result []*entity.Slice
|
||||
for _, slice := range slices {
|
||||
if slice != nil {
|
||||
result = append(result, k.fromModelSlice(ctx, slice))
|
||||
}
|
||||
}
|
||||
|
||||
return &MGetSliceResponse{
|
||||
Slices: result,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (k *knowledgeSVC) MGetDocument(ctx context.Context, request *MGetDocumentRequest) (response *MGetDocumentResponse, err error) {
|
||||
documents, err := k.documentRepo.MGetByID(ctx, request.DocumentIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result []*entity.Document
|
||||
for _, doc := range documents {
|
||||
if doc != nil {
|
||||
docEntity, err := k.fromModelDocument(ctx, doc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if docEntity != nil {
|
||||
result = append(result, docEntity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &MGetDocumentResponse{
|
||||
Documents: result,
|
||||
}, nil
|
||||
}
|
||||
|
||||
124
backend/domain/permission/authz_checker.go
Normal file
124
backend/domain/permission/authz_checker.go
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package permission
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type ResourcePermissionRequest struct {
|
||||
ResourceType ResourceType
|
||||
ResourceIDs []int64
|
||||
Action Action
|
||||
OperatorID int64
|
||||
IsDraft *bool
|
||||
}
|
||||
|
||||
type ResourceInfo struct {
|
||||
ID int64
|
||||
CreatorID int64
|
||||
SpaceID *int64
|
||||
}
|
||||
|
||||
type ResourceQueryer interface {
|
||||
QueryResourceInfo(ctx context.Context, resourceIDs []int64, isDraft *bool) ([]*ResourceInfo, error)
|
||||
|
||||
GetResourceType() ResourceType
|
||||
}
|
||||
|
||||
type AuthzChecker struct {
|
||||
resourceQueryers map[ResourceType]ResourceQueryer
|
||||
}
|
||||
|
||||
func NewAuthzChecker() *AuthzChecker {
|
||||
checker := &AuthzChecker{
|
||||
resourceQueryers: make(map[ResourceType]ResourceQueryer),
|
||||
}
|
||||
|
||||
checker.registerResourceQueryers()
|
||||
|
||||
return checker
|
||||
}
|
||||
|
||||
func (c *AuthzChecker) registerResourceQueryers() {
|
||||
|
||||
c.resourceQueryers[ResourceTypeWorkspace] = NewWorkspaceResourceQueryer()
|
||||
|
||||
c.resourceQueryers[ResourceTypeAgent] = NewAgentResourceQueryer()
|
||||
|
||||
c.resourceQueryers[ResourceTypePlugin] = NewPluginResourceQueryer()
|
||||
|
||||
c.resourceQueryers[ResourceTypeWorkflow] = NewWorkflowResourceQueryer()
|
||||
|
||||
c.resourceQueryers[ResourceTypeKnowledge] = NewKnowledgeResourceQueryer()
|
||||
c.resourceQueryers[ResourceTypeKnowledgeSlice] = NewKnowledgeSliceResourceQueryer()
|
||||
c.resourceQueryers[ResourceTypeKnowledgeDocument] = NewKnowledgeDocumentResourceQueryer()
|
||||
|
||||
c.resourceQueryers[ResourceTypeDatabase] = NewDatabaseResourceQueryer()
|
||||
|
||||
c.resourceQueryers[ResourceTypeApp] = NewAppResourceQueryer()
|
||||
|
||||
}
|
||||
|
||||
func (c *AuthzChecker) CheckResourcePermission(ctx context.Context, req *ResourcePermissionRequest) (bool, error) {
|
||||
|
||||
queryeer, exists := c.resourceQueryers[req.ResourceType]
|
||||
if !exists {
|
||||
return false, fmt.Errorf("unsupported resource type: %d", req.ResourceType)
|
||||
}
|
||||
|
||||
resourceInfos, err := queryeer.QueryResourceInfo(ctx, req.ResourceIDs, req.IsDraft)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to query resource info: %w", err)
|
||||
}
|
||||
|
||||
for _, resourceInfo := range resourceInfos {
|
||||
allowed := c.checkSingleResourcePermission(req.OperatorID, resourceInfo, req.Action)
|
||||
if !allowed {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (c *AuthzChecker) checkSingleResourcePermission(operatorID int64, resourceInfo *ResourceInfo, action Action) bool {
|
||||
|
||||
if operatorID == resourceInfo.CreatorID {
|
||||
return true
|
||||
}
|
||||
|
||||
switch action {
|
||||
case ActionRead:
|
||||
return c.checkReadPermission(operatorID, resourceInfo)
|
||||
case ActionWrite:
|
||||
return c.checkWritePermission(operatorID, resourceInfo)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AuthzChecker) checkReadPermission(operatorID int64, resourceInfo *ResourceInfo) bool {
|
||||
|
||||
return operatorID == resourceInfo.CreatorID
|
||||
}
|
||||
|
||||
func (c *AuthzChecker) checkWritePermission(operatorID int64, resourceInfo *ResourceInfo) bool {
|
||||
|
||||
return operatorID == resourceInfo.CreatorID
|
||||
}
|
||||
@ -16,11 +16,17 @@
|
||||
|
||||
package permission
|
||||
|
||||
type (
|
||||
ResourceType int
|
||||
Decision int
|
||||
Action string
|
||||
)
|
||||
|
||||
const (
|
||||
ResourceTypeAccount ResourceType = 1
|
||||
ResourceTypeWorkspace = 2
|
||||
ResourceTypeApp = 3
|
||||
ResourceTypeBot = 4
|
||||
ResourceTypeAgent = 4
|
||||
ResourceTypePlugin = 5
|
||||
ResourceTypeWorkflow = 6
|
||||
ResourceTypeKnowledge = 7
|
||||
@ -42,6 +48,8 @@ const (
|
||||
ResourceTypeDatabase = 23
|
||||
ResourceTypeOceanProject = 24
|
||||
ResourceTypeFinetuneTask = 25
|
||||
ResourceTypeKnowledgeDocument = 26
|
||||
ResourceTypeKnowledgeSlice = 27
|
||||
)
|
||||
|
||||
const (
|
||||
@ -50,3 +58,8 @@ const (
|
||||
// Deny represents permission denied
|
||||
Deny Decision = 2
|
||||
)
|
||||
|
||||
const (
|
||||
ActionRead Action = "read"
|
||||
ActionWrite Action = "write"
|
||||
)
|
||||
|
||||
@ -20,33 +20,26 @@ import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type (
|
||||
ResourceType int
|
||||
Decision int
|
||||
)
|
||||
|
||||
type ResourceIdentifier struct {
|
||||
Type ResourceType
|
||||
ID string
|
||||
Type ResourceType
|
||||
ID []int64
|
||||
Action Action
|
||||
}
|
||||
|
||||
type ActionAndResource struct {
|
||||
Action string
|
||||
Action Action
|
||||
ResourceIdentifier ResourceIdentifier
|
||||
}
|
||||
|
||||
type CheckPermissionRequest struct {
|
||||
IdentityTicket string
|
||||
ActionAndResources []ActionAndResource
|
||||
type CheckAuthzData struct {
|
||||
ResourceIdentifier []*ResourceIdentifier
|
||||
OperatorID int64
|
||||
IsDraft *bool
|
||||
}
|
||||
|
||||
type CheckPermissionResponse struct {
|
||||
type CheckAuthzResult struct {
|
||||
Decision Decision
|
||||
}
|
||||
|
||||
type Permission interface {
|
||||
CheckPermission(ctx context.Context, req *CheckPermissionRequest) (*CheckPermissionResponse, error)
|
||||
CheckSingleAgentOperatePermission(ctx context.Context, botID, spaceID int64) (bool, error)
|
||||
CheckSpaceOperatePermission(ctx context.Context, spaceID int64, path, ticket string) (bool, error)
|
||||
UserSpaceCheck(ctx context.Context, spaceId, userId int64) (bool, error)
|
||||
CheckAuthz(ctx context.Context, req *CheckAuthzData) (*CheckAuthzResult, error)
|
||||
}
|
||||
|
||||
@ -26,18 +26,30 @@ func NewService() Permission {
|
||||
return &permissionImpl{}
|
||||
}
|
||||
|
||||
func (p *permissionImpl) CheckPermission(ctx context.Context, req *CheckPermissionRequest) (*CheckPermissionResponse, error) {
|
||||
return &CheckPermissionResponse{Decision: 0}, nil
|
||||
func DefaultSVC() Permission {
|
||||
return NewService()
|
||||
}
|
||||
|
||||
func (p *permissionImpl) CheckSingleAgentOperatePermission(ctx context.Context, botID, spaceID int64) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
func (p *permissionImpl) CheckAuthz(ctx context.Context, req *CheckAuthzData) (*CheckAuthzResult, error) {
|
||||
|
||||
func (p *permissionImpl) CheckSpaceOperatePermission(ctx context.Context, spaceID int64, path, ticket string) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
authzChecker := NewAuthzChecker()
|
||||
|
||||
func (p *permissionImpl) UserSpaceCheck(ctx context.Context, spaceId, userId int64) (bool, error) {
|
||||
return true, nil
|
||||
for _, resourceIdentifier := range req.ResourceIdentifier {
|
||||
allowed, err := authzChecker.CheckResourcePermission(ctx, &ResourcePermissionRequest{
|
||||
ResourceType: resourceIdentifier.Type,
|
||||
ResourceIDs: resourceIdentifier.ID,
|
||||
Action: resourceIdentifier.Action,
|
||||
OperatorID: req.OperatorID,
|
||||
IsDraft: req.IsDraft,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !allowed {
|
||||
return &CheckAuthzResult{Decision: Deny}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return &CheckAuthzResult{Decision: Allow}, nil
|
||||
}
|
||||
|
||||
369
backend/domain/permission/resource_queryiers.go
Normal file
369
backend/domain/permission/resource_queryiers.go
Normal file
@ -0,0 +1,369 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package permission
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/coze-dev/coze-studio/backend/api/model/data/database/table"
|
||||
"github.com/coze-dev/coze-studio/backend/crossdomain/agent"
|
||||
"github.com/coze-dev/coze-studio/backend/crossdomain/app"
|
||||
"github.com/coze-dev/coze-studio/backend/crossdomain/database"
|
||||
"github.com/coze-dev/coze-studio/backend/crossdomain/knowledge"
|
||||
"github.com/coze-dev/coze-studio/backend/crossdomain/plugin"
|
||||
"github.com/coze-dev/coze-studio/backend/crossdomain/user"
|
||||
"github.com/coze-dev/coze-studio/backend/crossdomain/workflow"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/workflow/entity/vo"
|
||||
|
||||
databaseModel "github.com/coze-dev/coze-studio/backend/crossdomain/database/model"
|
||||
knowledgeModel "github.com/coze-dev/coze-studio/backend/crossdomain/knowledge/model"
|
||||
)
|
||||
|
||||
type AgentResourceQueryer struct {
|
||||
agentService agent.SingleAgent
|
||||
}
|
||||
|
||||
func NewAgentResourceQueryer() *AgentResourceQueryer {
|
||||
return &AgentResourceQueryer{
|
||||
agentService: agent.DefaultSVC(),
|
||||
}
|
||||
}
|
||||
|
||||
func (q *AgentResourceQueryer) QueryResourceInfo(ctx context.Context, resourceIDs []int64, isDraft *bool) ([]*ResourceInfo, error) {
|
||||
var result []*ResourceInfo
|
||||
|
||||
for _, id := range resourceIDs {
|
||||
agentInfo, err := q.agentService.GetSingleAgentDraft(ctx, id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query bot %d: %w", id, err)
|
||||
}
|
||||
|
||||
if agentInfo != nil {
|
||||
result = append(result, &ResourceInfo{
|
||||
ID: id,
|
||||
CreatorID: agentInfo.CreatorID,
|
||||
SpaceID: &agentInfo.SpaceID,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (q *AgentResourceQueryer) GetResourceType() ResourceType {
|
||||
return ResourceTypeAgent
|
||||
}
|
||||
|
||||
type PluginResourceQueryer struct {
|
||||
pluginService plugin.PluginService
|
||||
}
|
||||
|
||||
func NewPluginResourceQueryer() *PluginResourceQueryer {
|
||||
return &PluginResourceQueryer{
|
||||
pluginService: plugin.DefaultSVC(),
|
||||
}
|
||||
}
|
||||
|
||||
func (q *PluginResourceQueryer) QueryResourceInfo(ctx context.Context, resourceIDs []int64, isDraft *bool) ([]*ResourceInfo, error) {
|
||||
|
||||
plugins, err := q.pluginService.MGetDraftPlugins(ctx, resourceIDs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query draft plugins: %w", err)
|
||||
}
|
||||
var result []*ResourceInfo
|
||||
for _, plugin := range plugins {
|
||||
result = append(result, &ResourceInfo{
|
||||
ID: plugin.ID,
|
||||
CreatorID: plugin.DeveloperID,
|
||||
SpaceID: &plugin.SpaceID,
|
||||
})
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (q *PluginResourceQueryer) GetResourceType() ResourceType {
|
||||
return ResourceTypePlugin
|
||||
}
|
||||
|
||||
type WorkflowResourceQueryer struct {
|
||||
workflowService crossworkflow.Workflow
|
||||
}
|
||||
|
||||
func NewWorkflowResourceQueryer() *WorkflowResourceQueryer {
|
||||
return &WorkflowResourceQueryer{
|
||||
workflowService: crossworkflow.DefaultSVC(),
|
||||
}
|
||||
}
|
||||
|
||||
func (q *WorkflowResourceQueryer) QueryResourceInfo(ctx context.Context, resourceIDs []int64, isDraft *bool) ([]*ResourceInfo, error) {
|
||||
|
||||
workflows, _, err := q.workflowService.MGet(ctx, &vo.MGetPolicy{
|
||||
|
||||
QType: crossworkflow.FromDraft,
|
||||
MetaOnly: true,
|
||||
MetaQuery: vo.MetaQuery{
|
||||
IDs: resourceIDs,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query workflows: %w", err)
|
||||
}
|
||||
var result []*ResourceInfo
|
||||
for _, workflow := range workflows {
|
||||
result = append(result, &ResourceInfo{
|
||||
ID: workflow.ID,
|
||||
CreatorID: workflow.CreatorID,
|
||||
SpaceID: &workflow.SpaceID,
|
||||
})
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (q *WorkflowResourceQueryer) GetResourceType() ResourceType {
|
||||
return ResourceTypeWorkflow
|
||||
}
|
||||
|
||||
type KnowledgeResourceQueryer struct {
|
||||
knowledgeService knowledge.Knowledge
|
||||
}
|
||||
|
||||
func NewKnowledgeResourceQueryer() *KnowledgeResourceQueryer {
|
||||
return &KnowledgeResourceQueryer{
|
||||
knowledgeService: knowledge.DefaultSVC(),
|
||||
}
|
||||
}
|
||||
|
||||
func (q *KnowledgeResourceQueryer) QueryResourceInfo(ctx context.Context, resourceIDs []int64, isDraft *bool) ([]*ResourceInfo, error) {
|
||||
|
||||
resp, err := q.knowledgeService.MGetKnowledgeByID(ctx, &knowledgeModel.MGetKnowledgeByIDRequest{
|
||||
KnowledgeIDs: resourceIDs,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query knowledge: %w", err)
|
||||
}
|
||||
|
||||
var result []*ResourceInfo
|
||||
for _, knowledgeInfo := range resp.Knowledge {
|
||||
if knowledgeInfo != nil {
|
||||
result = append(result, &ResourceInfo{
|
||||
ID: knowledgeInfo.ID,
|
||||
CreatorID: knowledgeInfo.CreatorID,
|
||||
SpaceID: &knowledgeInfo.SpaceID,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (q *KnowledgeResourceQueryer) GetResourceType() ResourceType {
|
||||
return ResourceTypeKnowledge
|
||||
}
|
||||
|
||||
type DatabaseResourceQueryer struct {
|
||||
databaseService database.Database
|
||||
}
|
||||
|
||||
func NewDatabaseResourceQueryer() *DatabaseResourceQueryer {
|
||||
return &DatabaseResourceQueryer{
|
||||
databaseService: database.DefaultSVC(),
|
||||
}
|
||||
}
|
||||
|
||||
func (q *DatabaseResourceQueryer) QueryResourceInfo(ctx context.Context, resourceIDs []int64, isDraft *bool) ([]*ResourceInfo, error) {
|
||||
var basics []*databaseModel.DatabaseBasic
|
||||
for _, id := range resourceIDs {
|
||||
basic := &databaseModel.DatabaseBasic{
|
||||
ID: id,
|
||||
TableType: table.TableType_DraftTable,
|
||||
}
|
||||
if isDraft != nil && !*isDraft {
|
||||
basic.TableType = table.TableType_OnlineTable
|
||||
}
|
||||
basics = append(basics, basic)
|
||||
}
|
||||
|
||||
resp, err := q.databaseService.MGetDatabase(ctx, &databaseModel.MGetDatabaseRequest{
|
||||
Basics: basics,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query database: %w", err)
|
||||
}
|
||||
|
||||
var result []*ResourceInfo
|
||||
for _, dbInfo := range resp.Databases {
|
||||
if dbInfo != nil {
|
||||
result = append(result, &ResourceInfo{
|
||||
ID: dbInfo.ID,
|
||||
CreatorID: dbInfo.CreatorID,
|
||||
SpaceID: &dbInfo.SpaceID,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (q *DatabaseResourceQueryer) GetResourceType() ResourceType {
|
||||
return ResourceTypeDatabase
|
||||
}
|
||||
|
||||
type AppResourceQueryer struct {
|
||||
appService app.AppService
|
||||
}
|
||||
|
||||
func NewAppResourceQueryer() *AppResourceQueryer {
|
||||
return &AppResourceQueryer{
|
||||
appService: app.DefaultSVC(),
|
||||
}
|
||||
}
|
||||
|
||||
func (q *AppResourceQueryer) QueryResourceInfo(ctx context.Context, resourceIDs []int64, isDraft *bool) ([]*ResourceInfo, error) {
|
||||
var result []*ResourceInfo
|
||||
|
||||
for _, id := range resourceIDs {
|
||||
appInfo, err := q.appService.GetDraftAPP(ctx, id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query app %d: %w", id, err)
|
||||
}
|
||||
|
||||
if appInfo != nil {
|
||||
result = append(result, &ResourceInfo{
|
||||
ID: id,
|
||||
CreatorID: appInfo.OwnerID,
|
||||
SpaceID: &appInfo.SpaceID,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (q *AppResourceQueryer) GetResourceType() ResourceType {
|
||||
return ResourceTypeApp
|
||||
}
|
||||
|
||||
type KnowledgeSliceResourceQueryer struct {
|
||||
knowledgeService knowledge.Knowledge
|
||||
}
|
||||
|
||||
func NewKnowledgeSliceResourceQueryer() *KnowledgeSliceResourceQueryer {
|
||||
return &KnowledgeSliceResourceQueryer{
|
||||
knowledgeService: knowledge.DefaultSVC(),
|
||||
}
|
||||
}
|
||||
|
||||
func (q *KnowledgeSliceResourceQueryer) QueryResourceInfo(ctx context.Context, resourceIDs []int64, isDraft *bool) ([]*ResourceInfo, error) {
|
||||
resp, err := q.knowledgeService.MGetSlice(ctx, &knowledgeModel.MGetSliceRequest{
|
||||
SliceIDs: resourceIDs,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query knowledge slice: %w", err)
|
||||
}
|
||||
|
||||
var result []*ResourceInfo
|
||||
for _, slice := range resp.Slices {
|
||||
if slice != nil {
|
||||
result = append(result, &ResourceInfo{
|
||||
ID: slice.ID,
|
||||
CreatorID: slice.CreatorID,
|
||||
SpaceID: &slice.SpaceID,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (q *KnowledgeSliceResourceQueryer) GetResourceType() ResourceType {
|
||||
return ResourceTypeKnowledgeSlice
|
||||
}
|
||||
|
||||
type KnowledgeDocumentResourceQueryer struct {
|
||||
knowledgeService knowledge.Knowledge
|
||||
}
|
||||
|
||||
func NewKnowledgeDocumentResourceQueryer() *KnowledgeDocumentResourceQueryer {
|
||||
return &KnowledgeDocumentResourceQueryer{
|
||||
knowledgeService: knowledge.DefaultSVC(),
|
||||
}
|
||||
}
|
||||
|
||||
func (q *KnowledgeDocumentResourceQueryer) QueryResourceInfo(ctx context.Context, resourceIDs []int64, isDraft *bool) ([]*ResourceInfo, error) {
|
||||
resp, err := q.knowledgeService.MGetDocument(ctx, &knowledgeModel.MGetDocumentRequest{
|
||||
DocumentIDs: resourceIDs,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query knowledge document: %w", err)
|
||||
}
|
||||
|
||||
var result []*ResourceInfo
|
||||
for _, document := range resp.Documents {
|
||||
if document != nil {
|
||||
result = append(result, &ResourceInfo{
|
||||
ID: document.ID,
|
||||
CreatorID: document.CreatorID,
|
||||
SpaceID: &document.SpaceID,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (q *KnowledgeDocumentResourceQueryer) GetResourceType() ResourceType {
|
||||
return ResourceTypeKnowledgeDocument
|
||||
}
|
||||
|
||||
type WorkspaceResourceQueryer struct {
|
||||
userService crossuser.User
|
||||
}
|
||||
|
||||
func NewWorkspaceResourceQueryer() *WorkspaceResourceQueryer {
|
||||
return &WorkspaceResourceQueryer{
|
||||
userService: crossuser.DefaultSVC(),
|
||||
}
|
||||
}
|
||||
|
||||
func (q *WorkspaceResourceQueryer) QueryResourceInfo(ctx context.Context, resourceIDs []int64, isDraft *bool) ([]*ResourceInfo, error) {
|
||||
// For workspace resources, we need to get space information for each user
|
||||
var result []*ResourceInfo
|
||||
|
||||
spaces, err := q.userService.GetUserSpaceBySpaceID(ctx, resourceIDs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get user space list for space %v: %w", resourceIDs, err)
|
||||
}
|
||||
|
||||
for _, space := range spaces {
|
||||
if space != nil {
|
||||
result = append(result, &ResourceInfo{
|
||||
ID: space.ID,
|
||||
CreatorID: space.CreatorID,
|
||||
SpaceID: &space.ID,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (q *WorkspaceResourceQueryer) GetResourceType() ResourceType {
|
||||
return ResourceTypeWorkspace
|
||||
}
|
||||
@ -78,6 +78,7 @@ type User interface {
|
||||
MGetUserProfiles(ctx context.Context, userIDs []int64) (users []*entity.User, err error)
|
||||
ValidateSession(ctx context.Context, sessionKey string) (session *entity.Session, exist bool, err error)
|
||||
GetUserSpaceList(ctx context.Context, userID int64) (spaces []*entity.Space, err error)
|
||||
GetUserSpaceBySpaceID(ctx context.Context, spaceID []int64) (space []*entity.Space, err error)
|
||||
}
|
||||
|
||||
type SaasUserProvider interface {
|
||||
|
||||
@ -465,6 +465,40 @@ func (u *userImpl) GetUserSpaceList(ctx context.Context, userID int64) (spaces [
|
||||
}), nil
|
||||
}
|
||||
|
||||
func (u *userImpl) GetUserSpaceBySpaceID(ctx context.Context, spaceID []int64) (spaces []*userEntity.Space, err error) {
|
||||
if len(spaceID) == 0 {
|
||||
return nil, errorx.New(errno.ErrUserInvalidParamCode, errorx.KV("msg", "spaceID cannot be empty"))
|
||||
}
|
||||
|
||||
spaceModels, err := u.SpaceRepo.GetSpaceByIDs(ctx, spaceID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(spaceModels) == 0 {
|
||||
return []*userEntity.Space{}, nil
|
||||
}
|
||||
|
||||
uris := slices.ToMap(spaceModels, func(sm *model.Space) (string, bool) {
|
||||
return sm.IconURI, false
|
||||
})
|
||||
|
||||
urls := make(map[string]string, len(uris))
|
||||
for uri := range uris {
|
||||
if uri != "" {
|
||||
url, err := u.IconOSS.GetObjectUrl(ctx, uri)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
urls[uri] = url
|
||||
}
|
||||
}
|
||||
|
||||
return slices.Transform(spaceModels, func(sm *model.Space) *userEntity.Space {
|
||||
return spacePo2Do(sm, urls[sm.IconURI])
|
||||
}), nil
|
||||
}
|
||||
|
||||
func spacePo2Do(space *model.Space, iconUrl string) *userEntity.Space {
|
||||
return &userEntity.Space{
|
||||
ID: space.ID,
|
||||
|
||||
@ -1,9 +1,25 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: crossuser.go
|
||||
// Source: contract.go
|
||||
//
|
||||
// Generated by this command:
|
||||
//
|
||||
// mockgen -destination ../../../internal/mock/crossdomain/crossuser/crossuser.go --package mockCrossUser -source crossuser.go
|
||||
// mockgen -destination ../../internal/mock/crossdomain/crossuser/crossuser.go --package mockCrossUser -source contract.go
|
||||
//
|
||||
|
||||
// Package mockCrossUser is a generated GoMock package.
|
||||
@ -13,9 +29,8 @@ import (
|
||||
context "context"
|
||||
reflect "reflect"
|
||||
|
||||
gomock "go.uber.org/mock/gomock"
|
||||
|
||||
crossuser "github.com/coze-dev/coze-studio/backend/crossdomain/user"
|
||||
gomock "go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
// MockUser is a mock of User interface.
|
||||
@ -42,6 +57,21 @@ func (m *MockUser) EXPECT() *MockUserMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// GetUserSpaceBySpaceID mocks base method.
|
||||
func (m *MockUser) GetUserSpaceBySpaceID(ctx context.Context, spaceID []int64) ([]*crossuser.EntitySpace, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetUserSpaceBySpaceID", ctx, spaceID)
|
||||
ret0, _ := ret[0].([]*crossuser.EntitySpace)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetUserSpaceBySpaceID indicates an expected call of GetUserSpaceBySpaceID.
|
||||
func (mr *MockUserMockRecorder) GetUserSpaceBySpaceID(ctx, spaceID any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserSpaceBySpaceID", reflect.TypeOf((*MockUser)(nil).GetUserSpaceBySpaceID), ctx, spaceID)
|
||||
}
|
||||
|
||||
// GetUserSpaceList mocks base method.
|
||||
func (m *MockUser) GetUserSpaceList(ctx context.Context, userID int64) ([]*crossuser.EntitySpace, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
||||
Reference in New Issue
Block a user