Files
ai-agent/internal/services/subject_service.go
T
t 2bbf42b741 refactor(auth): delegate access control to be-system
Remove Agent Desk users, roles, login sessions, tokens, and local permission persistence. Expose the backend as an embeddable ai-agent module with host-provided subject lookup and operation authorization callbacks, and complete the frontend/backend repository split.
2026-08-21 00:41:07 +08:00

112 lines
2.8 KiB
Go

package services
import (
"context"
"errors"
"fmt"
"log/slog"
"sync"
"code.tczkiot.com/wlw/ai-agent/identity"
"code.tczkiot.com/wlw/ai-agent/internal/pkg/enums"
"code.tczkiot.com/wlw/ai-agent/internal/pkg/openidentity"
)
var SubjectService = &subjectService{}
type subjectService struct {
mu sync.RWMutex
query identity.QuerySubjectsFunc
authorize identity.AuthorizeFunc
}
func SetQuerySubjects(query identity.QuerySubjectsFunc) {
SubjectService.mu.Lock()
defer SubjectService.mu.Unlock()
SubjectService.query = query
}
func SetAuthorize(authorize identity.AuthorizeFunc) {
SubjectService.mu.Lock()
defer SubjectService.mu.Unlock()
SubjectService.authorize = authorize
}
func (s *subjectService) Authorize(ctx context.Context, operation string) error {
s.mu.RLock()
fn := s.authorize
s.mu.RUnlock()
if fn == nil {
return errors.New("agent-desk: Authorize is not initialized")
}
return fn(ctx, operation)
}
func (s *subjectService) Query(ctx context.Context, query identity.Query) ([]identity.Subject, error) {
s.mu.RLock()
fn := s.query
s.mu.RUnlock()
if fn == nil {
return nil, errors.New("agent-desk: QuerySubjects is not initialized")
}
return fn(ctx, query)
}
func (s *subjectService) Current(ctx context.Context) (*identity.Subject, error) {
items, err := s.Query(ctx, identity.Query{Current: true, EnabledOnly: true})
if err != nil {
return nil, err
}
if len(items) == 0 {
return nil, errors.New("agent-desk: current subject not found")
}
return &items[0], nil
}
func (s *subjectService) CurrentExternal(ctx context.Context) (*openidentity.ExternalUser, error) {
subject, err := s.Current(ctx)
if err != nil {
return nil, err
}
if subject.Category != identity.CategoryUser || !subject.Enabled {
return nil, errors.New("agent-desk: current subject is not a customer identity")
}
return &openidentity.ExternalUser{
ExternalSource: enums.ExternalSourceUser,
ExternalID: fmt.Sprintf("%s:%d", subject.Type, subject.ID),
ExternalName: subject.Name,
}, nil
}
func (s *subjectService) Get(id int64) *identity.Subject {
items, err := s.Query(context.Background(), identity.Query{
Types: []identity.SubjectType{identity.SubjectAgent},
IDs: []int64{id},
EnabledOnly: true,
})
if err != nil {
slog.Warn("query external subject failed", "id", id, "error", err)
return nil
}
if len(items) == 0 {
return nil
}
return &items[0]
}
func (s *subjectService) FindByIDs(ids []int64) []identity.Subject {
if len(ids) == 0 {
return nil
}
items, err := s.Query(context.Background(), identity.Query{
Types: []identity.SubjectType{identity.SubjectAgent},
IDs: ids,
EnabledOnly: true,
})
if err != nil {
slog.Warn("query external subjects failed", "error", err)
return nil
}
return items
}