permission.go

  1package permission
  2
  3import (
  4	"errors"
  5	"sync"
  6	"time"
  7
  8	"github.com/google/uuid"
  9	"github.com/kujtimiihoxha/termai/internal/pubsub"
 10)
 11
 12var ErrorPermissionDenied = errors.New("permission denied")
 13
 14type CreatePermissionRequest struct {
 15	ToolName    string `json:"tool_name"`
 16	Description string `json:"description"`
 17	Action      string `json:"action"`
 18	Params      any    `json:"params"`
 19	Path        string `json:"path"`
 20}
 21
 22type PermissionRequest struct {
 23	ID          string `json:"id"`
 24	SessionID   string `json:"session_id"`
 25	ToolName    string `json:"tool_name"`
 26	Description string `json:"description"`
 27	Action      string `json:"action"`
 28	Params      any    `json:"params"`
 29	Path        string `json:"path"`
 30}
 31
 32type Service interface {
 33	pubsub.Suscriber[PermissionRequest]
 34	GrantPersistant(permission PermissionRequest)
 35	Grant(permission PermissionRequest)
 36	Deny(permission PermissionRequest)
 37	Request(opts CreatePermissionRequest) bool
 38}
 39
 40type permissionService struct {
 41	*pubsub.Broker[PermissionRequest]
 42
 43	sessionPermissions []PermissionRequest
 44	pendingRequests    sync.Map
 45}
 46
 47func (s *permissionService) GrantPersistant(permission PermissionRequest) {
 48	respCh, ok := s.pendingRequests.Load(permission.ID)
 49	if ok {
 50		respCh.(chan bool) <- true
 51	}
 52	s.sessionPermissions = append(s.sessionPermissions, permission)
 53}
 54
 55func (s *permissionService) Grant(permission PermissionRequest) {
 56	respCh, ok := s.pendingRequests.Load(permission.ID)
 57	if ok {
 58		respCh.(chan bool) <- true
 59	}
 60}
 61
 62func (s *permissionService) Deny(permission PermissionRequest) {
 63	respCh, ok := s.pendingRequests.Load(permission.ID)
 64	if ok {
 65		respCh.(chan bool) <- false
 66	}
 67}
 68
 69func (s *permissionService) Request(opts CreatePermissionRequest) bool {
 70	permission := PermissionRequest{
 71		ID:          uuid.New().String(),
 72		Path:        opts.Path,
 73		ToolName:    opts.ToolName,
 74		Description: opts.Description,
 75		Action:      opts.Action,
 76		Params:      opts.Params,
 77	}
 78
 79	for _, p := range s.sessionPermissions {
 80		if p.ToolName == permission.ToolName && p.Action == permission.Action {
 81			return true
 82		}
 83	}
 84
 85	respCh := make(chan bool, 1)
 86
 87	s.pendingRequests.Store(permission.ID, respCh)
 88	defer s.pendingRequests.Delete(permission.ID)
 89
 90	s.Publish(pubsub.CreatedEvent, permission)
 91
 92	// Wait for the response with a timeout
 93	select {
 94	case resp := <-respCh:
 95		return resp
 96	case <-time.After(10 * time.Minute):
 97		return false
 98	}
 99}
100
101func NewPermissionService() Service {
102	return &permissionService{
103		Broker:             pubsub.NewBroker[PermissionRequest](),
104		sessionPermissions: make([]PermissionRequest, 0),
105	}
106}