WeKnora deployment source with the reviewed wiki identity, summary and recovery fixes
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
package im
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/Tencent/WeKnora/internal/types"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
type verifiedAgentExecutionKey struct{}
|
||||
type verifiedAgentExecution struct {
|
||||
TenantID uint64
|
||||
AgentID, OwnerID, ChannelID string
|
||||
PrincipalID string
|
||||
}
|
||||
|
||||
// IsVerifiedAgentExecution accepts only a proof minted inside the IM runtime
|
||||
// after reading an enabled channel and its exact Agent from durable storage.
|
||||
// A synthetic user, IM principal or request parameter alone never grants it.
|
||||
func IsVerifiedAgentExecution(ctx context.Context, agent *types.CustomAgent) bool {
|
||||
proof, ok := ctx.Value(verifiedAgentExecutionKey{}).(verifiedAgentExecution)
|
||||
if !ok || agent == nil || proof.ChannelID == "" || proof.AgentID != agent.ID || proof.TenantID != agent.TenantID || proof.OwnerID != agent.CreatedBy {
|
||||
return false
|
||||
}
|
||||
tenant, hasTenant := types.TenantIDFromContext(ctx)
|
||||
user, hasUser := types.UserIDFromContext(ctx)
|
||||
principal, hasPrincipal := types.PrincipalFromContext(ctx)
|
||||
return hasTenant && hasUser && tenant == proof.TenantID && user == fmt.Sprintf("system-%d", tenant) &&
|
||||
hasPrincipal && principal.Type == types.PrincipalIMUser && principal.ID == proof.PrincipalID
|
||||
}
|
||||
|
||||
func (s *Service) resolveConfiguredChannelExecution(ctx context.Context, channel *IMChannel) (context.Context, *types.CustomAgent, error) {
|
||||
agent, err := s.resolveConfiguredChannelAgent(ctx, channel)
|
||||
if err != nil {
|
||||
return ctx, nil, err
|
||||
}
|
||||
principal, hasPrincipal := types.PrincipalFromContext(ctx)
|
||||
tenant, hasTenant := types.TenantIDFromContext(ctx)
|
||||
user, hasUser := types.UserIDFromContext(ctx)
|
||||
if !hasPrincipal || principal.Type != types.PrincipalIMUser || !hasTenant || tenant != channel.TenantID || !hasUser || user != fmt.Sprintf("system-%d", tenant) {
|
||||
return ctx, nil, fmt.Errorf("verified IM identity is required for channel execution")
|
||||
}
|
||||
proof := verifiedAgentExecution{TenantID: channel.TenantID, AgentID: agent.ID, OwnerID: agent.CreatedBy, ChannelID: channel.ID, PrincipalID: principal.ID}
|
||||
return context.WithValue(ctx, verifiedAgentExecutionKey{}, proof), agent, nil
|
||||
}
|
||||
|
||||
// InvalidateDeletedChannels is called only after durable deletion commits.
|
||||
func (s *Service) InvalidateDeletedChannels(ids []string) {
|
||||
for _, id := range ids {
|
||||
s.StopChannel(id)
|
||||
s.publishChannelConfigChange(id)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) cancelChannelRequests(id string) {
|
||||
s.inflight.Range(func(key, value any) bool {
|
||||
userKey, ok := key.(string)
|
||||
if ok && strings.HasPrefix(userKey, id+":") {
|
||||
if entry, ok := value.(*inflightEntry); ok && entry.cancel != nil {
|
||||
entry.cancel()
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// Serialize authoring with agent deletion so a concurrent channel create or
|
||||
// reassignment cannot leave a new channel pointing at a deleted custom agent.
|
||||
func lockChannelAgent(tx *gorm.DB, channel *IMChannel) error {
|
||||
if types.IsBuiltinAgentID(channel.AgentID) {
|
||||
return nil // Built-in agents cannot be deleted.
|
||||
}
|
||||
var agent types.CustomAgent
|
||||
return tx.Clauses(clause.Locking{Strength: "UPDATE"}).Select("id").
|
||||
Where("id = ? AND tenant_id = ?", channel.AgentID, channel.TenantID).First(&agent).Error
|
||||
}
|
||||
|
||||
// resolveConfiguredChannelAgent is a trusted runtime path, not a user Agent
|
||||
// lookup. A persisted, enabled channel is the explicit authorization to execute
|
||||
// exactly its configured Agent, including an owner's private digital human.
|
||||
func (s *Service) resolveConfiguredChannelAgent(ctx context.Context, channel *IMChannel) (*types.CustomAgent, error) {
|
||||
if channel == nil || channel.ID == "" || channel.AgentID == "" {
|
||||
return nil, fmt.Errorf("channel has no configured agent")
|
||||
}
|
||||
var fresh IMChannel
|
||||
if err := s.db.WithContext(ctx).Where("id = ? AND tenant_id = ? AND agent_id = ? AND enabled = ?", channel.ID, channel.TenantID, channel.AgentID, true).First(&fresh).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var agent types.CustomAgent
|
||||
err := s.db.WithContext(ctx).Where("id = ? AND tenant_id = ?", fresh.AgentID, fresh.TenantID).First(&agent).Error
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) && types.IsBuiltinAgentID(fresh.AgentID) {
|
||||
return s.agentService.GetAgentByID(ctx, fresh.AgentID)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
agent.EnsureDefaults()
|
||||
return &agent, nil
|
||||
}
|
||||
Reference in New Issue
Block a user