permission.go 3.0 KB

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