Files
goclaw/internal/http/skills_access_effective.go

112 lines
3.0 KiB
Go

package http
import (
"context"
"fmt"
"github.com/google/uuid"
"github.com/nextlevelbuilder/goclaw/internal/skills"
"github.com/nextlevelbuilder/goclaw/internal/store"
)
type effectiveAccessIndex struct {
actorID string
accessibleBySlug map[string]bool
agentGrantByID map[string]store.SkillWithGrantStatus
agentGrantBySlug map[string]store.SkillWithGrantStatus
}
func (h *SkillsHandler) buildEffectiveAccessIndex(r interface{ Context() context.Context }, agentID uuid.UUID, userID string) (effectiveAccessIndex, error) {
ctx := r.Context()
accessStore, ok := h.skills.(store.SkillAccessStore)
if !ok {
return effectiveAccessIndex{}, fmt.Errorf("skill access store not available")
}
accessible, err := accessStore.ListAccessible(ctx, agentID, userID)
if err != nil {
return effectiveAccessIndex{}, err
}
grantStatus, err := h.skills.ListWithGrantStatus(ctx, agentID)
if err != nil {
return effectiveAccessIndex{}, err
}
idx := effectiveAccessIndex{
actorID: store.ActorIDFromContext(ctx),
accessibleBySlug: make(map[string]bool, len(accessible)),
agentGrantByID: make(map[string]store.SkillWithGrantStatus, len(grantStatus)),
agentGrantBySlug: make(map[string]store.SkillWithGrantStatus, len(grantStatus)),
}
if idx.actorID == "" {
idx.actorID = userID
}
for _, sk := range accessible {
idx.accessibleBySlug[sk.Slug] = true
}
for _, grant := range grantStatus {
idx.agentGrantByID[grant.ID.String()] = grant
idx.agentGrantBySlug[grant.Slug] = grant
}
return idx, nil
}
func effectiveAccessForSkill(ctx context.Context, sk store.SkillInfo, idx effectiveAccessIndex, userID string) skillEffectiveAccessResponse {
resp := skillEffectiveAccessResponse{Skill: refForSkill(sk), Reason: "none"}
if sk.Status != "active" || !sk.Enabled {
resp.Reason = "inactive"
return resp
}
if !idx.accessible(sk) {
return resp
}
if sk.IsSystem {
resp.Accessible = true
resp.Reason = "system"
return resp
}
if sk.Visibility == skills.VisibilityPublic {
resp.Accessible = true
resp.Reason = "public"
return resp
}
actorID := idx.actorID
if actorID == "" {
actorID = store.ActorIDFromContext(ctx)
}
if actorID == "" {
actorID = userID
}
if sk.Visibility == skills.VisibilityPrivate && (sk.OwnerID == userID || sk.OwnerID == actorID) {
resp.Accessible = true
resp.Reason = "owner"
return resp
}
if sk.Visibility != skills.VisibilityInternal {
return resp
}
if grant, ok := idx.agentGrant(sk); ok && grant.Granted {
resp.Accessible = true
resp.Reason = "agent_grant"
resp.CanManage = grant.CanManage
resp.PinnedVersion = grant.PinnedVer
return resp
}
resp.Accessible = true
resp.Reason = "user_grant"
return resp
}
func (idx effectiveAccessIndex) accessible(sk store.SkillInfo) bool {
return idx.accessibleBySlug[sk.Slug]
}
func (idx effectiveAccessIndex) agentGrant(sk store.SkillInfo) (store.SkillWithGrantStatus, bool) {
if sk.ID != "" {
if grant, ok := idx.agentGrantByID[sk.ID]; ok {
return grant, true
}
}
grant, ok := idx.agentGrantBySlug[sk.Slug]
return grant, ok
}