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