SuperCoder/internal/permission/permission.go

120 lines
3 KiB
Go
Raw Permalink Normal View History

2025-03-25 13:04:36 +01:00
package permission
import (
"errors"
2025-04-21 14:29:03 +02:00
"path/filepath"
2025-04-21 19:48:36 +02:00
"slices"
2025-03-25 13:04:36 +01:00
"sync"
"github.com/google/uuid"
2025-04-24 18:25:52 +02:00
"github.com/opencode-ai/opencode/internal/config"
"github.com/opencode-ai/opencode/internal/pubsub"
2025-03-25 13:04:36 +01:00
)
var ErrorPermissionDenied = errors.New("permission denied")
2025-03-25 13:04:36 +01:00
type CreatePermissionRequest struct {
2025-04-21 19:48:36 +02:00
SessionID string `json:"session_id"`
2025-03-25 13:04:36 +01:00
ToolName string `json:"tool_name"`
Description string `json:"description"`
Action string `json:"action"`
Params any `json:"params"`
Path string `json:"path"`
}
2025-03-25 13:04:36 +01:00
type PermissionRequest struct {
ID string `json:"id"`
SessionID string `json:"session_id"`
ToolName string `json:"tool_name"`
Description string `json:"description"`
Action string `json:"action"`
Params any `json:"params"`
Path string `json:"path"`
}
type Service interface {
pubsub.Suscriber[PermissionRequest]
GrantPersistant(permission PermissionRequest)
Grant(permission PermissionRequest)
Deny(permission PermissionRequest)
Request(opts CreatePermissionRequest) bool
2025-04-21 19:48:36 +02:00
AutoApproveSession(sessionID string)
2025-03-25 13:04:36 +01:00
}
type permissionService struct {
*pubsub.Broker[PermissionRequest]
2025-04-21 19:48:36 +02:00
sessionPermissions []PermissionRequest
pendingRequests sync.Map
autoApproveSessions []string
2025-03-25 13:04:36 +01:00
}
func (s *permissionService) GrantPersistant(permission PermissionRequest) {
respCh, ok := s.pendingRequests.Load(permission.ID)
if ok {
respCh.(chan bool) <- true
}
s.sessionPermissions = append(s.sessionPermissions, permission)
}
func (s *permissionService) Grant(permission PermissionRequest) {
respCh, ok := s.pendingRequests.Load(permission.ID)
if ok {
respCh.(chan bool) <- true
}
}
func (s *permissionService) Deny(permission PermissionRequest) {
respCh, ok := s.pendingRequests.Load(permission.ID)
if ok {
respCh.(chan bool) <- false
}
}
func (s *permissionService) Request(opts CreatePermissionRequest) bool {
2025-04-21 19:48:36 +02:00
if slices.Contains(s.autoApproveSessions, opts.SessionID) {
return true
}
2025-04-21 14:29:03 +02:00
dir := filepath.Dir(opts.Path)
if dir == "." {
dir = config.WorkingDirectory()
}
2025-03-25 13:04:36 +01:00
permission := PermissionRequest{
ID: uuid.New().String(),
2025-04-21 14:29:03 +02:00
Path: dir,
2025-04-21 19:48:36 +02:00
SessionID: opts.SessionID,
2025-03-25 13:04:36 +01:00
ToolName: opts.ToolName,
Description: opts.Description,
Action: opts.Action,
Params: opts.Params,
}
for _, p := range s.sessionPermissions {
2025-04-21 14:29:03 +02:00
if p.ToolName == permission.ToolName && p.Action == permission.Action && p.SessionID == permission.SessionID && p.Path == permission.Path {
2025-03-25 13:04:36 +01:00
return true
}
}
respCh := make(chan bool, 1)
s.pendingRequests.Store(permission.ID, respCh)
defer s.pendingRequests.Delete(permission.ID)
s.Publish(pubsub.CreatedEvent, permission)
// Wait for the response with a timeout
2025-04-24 22:33:23 +02:00
resp := <-respCh
return resp
2025-03-25 13:04:36 +01:00
}
2025-04-21 19:48:36 +02:00
func (s *permissionService) AutoApproveSession(sessionID string) {
s.autoApproveSessions = append(s.autoApproveSessions, sessionID)
}
2025-03-25 13:04:36 +01:00
func NewPermissionService() Service {
return &permissionService{
Broker: pubsub.NewBroker[PermissionRequest](),
sessionPermissions: make([]PermissionRequest, 0),
}
}