Skip to content

Commit 6ee3469

Browse files
committed
permissions in service layer
1 parent e8632a5 commit 6ee3469

14 files changed

Lines changed: 1058 additions & 126 deletions

internal/domain/errors.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,31 @@ func NewValidationError(message string) error {
5757
Message: message,
5858
}
5959
}
60+
61+
// PermissionError represents insufficient permissions for an operation
62+
type PermissionError struct {
63+
Resource PermissionResource `json:"resource"`
64+
Permission PermissionType `json:"permission"`
65+
Message string `json:"message"`
66+
}
67+
68+
// Error implements the error interface
69+
func (e *PermissionError) Error() string {
70+
return e.Message
71+
}
72+
73+
// NewPermissionError creates a new permission error
74+
func NewPermissionError(resource PermissionResource, permission PermissionType, message string) *PermissionError {
75+
return &PermissionError{
76+
Resource: resource,
77+
Permission: permission,
78+
Message: message,
79+
}
80+
}
81+
82+
// ErrInsufficientPermissions is the default insufficient permissions error
83+
var ErrInsufficientPermissions = NewPermissionError(
84+
PermissionResourceWorkspace,
85+
PermissionTypeRead,
86+
"Insufficient permissions",
87+
)

internal/service/broadcast_service.go

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,21 @@ func (s *BroadcastService) SetTaskService(taskService domain.TaskService) {
6969
func (s *BroadcastService) CreateBroadcast(ctx context.Context, request *domain.CreateBroadcastRequest) (*domain.Broadcast, error) {
7070
// Authenticate user for workspace
7171
var err error
72-
ctx, _, _, err = s.authService.AuthenticateUserForWorkspace(ctx, request.WorkspaceID)
72+
ctx, _, userWorkspace, err := s.authService.AuthenticateUserForWorkspace(ctx, request.WorkspaceID)
7373
if err != nil {
7474
s.logger.Error("Failed to authenticate user for workspace")
7575
return nil, fmt.Errorf("failed to authenticate user: %w", err)
7676
}
7777

78+
// Check permission for writing broadcasts
79+
if !userWorkspace.HasPermission(domain.PermissionResourceBroadcasts, domain.PermissionTypeWrite) {
80+
return nil, domain.NewPermissionError(
81+
domain.PermissionResourceBroadcasts,
82+
domain.PermissionTypeWrite,
83+
"Insufficient permissions: write access to broadcasts required",
84+
)
85+
}
86+
7887
// Validate the request
7988
broadcast, err := request.Validate()
8089
if err != nil {
@@ -121,12 +130,21 @@ func (s *BroadcastService) CreateBroadcast(ctx context.Context, request *domain.
121130
func (s *BroadcastService) GetBroadcast(ctx context.Context, workspaceID, broadcastID string) (*domain.Broadcast, error) {
122131
// Authenticate user for workspace
123132
var err error
124-
ctx, _, _, err = s.authService.AuthenticateUserForWorkspace(ctx, workspaceID)
133+
ctx, _, userWorkspace, err := s.authService.AuthenticateUserForWorkspace(ctx, workspaceID)
125134
if err != nil {
126135
s.logger.WithField("broadcast_id", broadcastID).Error("Failed to authenticate user for workspace")
127136
return nil, fmt.Errorf("failed to authenticate user: %w", err)
128137
}
129138

139+
// Check permission for reading broadcasts
140+
if !userWorkspace.HasPermission(domain.PermissionResourceBroadcasts, domain.PermissionTypeRead) {
141+
return nil, domain.NewPermissionError(
142+
domain.PermissionResourceBroadcasts,
143+
domain.PermissionTypeRead,
144+
"Insufficient permissions: read access to broadcasts required",
145+
)
146+
}
147+
130148
// Fetch the broadcast from the repository
131149
return s.repo.GetBroadcast(ctx, workspaceID, broadcastID)
132150
}

internal/service/broadcast_service_test.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,15 @@ func setupBroadcastSvc(t *testing.T) *broadcastSvcDeps {
105105
}
106106

107107
func authOK(auth *domainmocks.MockAuthService, ctx context.Context, workspaceID string) {
108-
auth.EXPECT().AuthenticateUserForWorkspace(ctx, workspaceID).Return(ctx, &domain.User{ID: "user1"}, nil, nil)
108+
userWorkspace := &domain.UserWorkspace{
109+
UserID: "user1",
110+
WorkspaceID: workspaceID,
111+
Role: "member",
112+
Permissions: domain.UserPermissions{
113+
domain.PermissionResourceBroadcasts: {Read: true, Write: true},
114+
},
115+
}
116+
auth.EXPECT().AuthenticateUserForWorkspace(ctx, workspaceID).Return(ctx, &domain.User{ID: "user1"}, userWorkspace, nil)
109117
}
110118

111119
func TestBroadcastService_CreateBroadcast_Success(t *testing.T) {

internal/service/contact_service.go

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,20 @@ func NewContactService(
3333

3434
func (s *ContactService) GetContactByEmail(ctx context.Context, workspaceID string, email string) (*domain.Contact, error) {
3535
var err error
36-
ctx, _, _, err = s.authService.AuthenticateUserForWorkspace(ctx, workspaceID)
36+
ctx, _, userWorkspace, err := s.authService.AuthenticateUserForWorkspace(ctx, workspaceID)
3737
if err != nil {
3838
return nil, fmt.Errorf("failed to authenticate user: %w", err)
3939
}
4040

41+
// Check permission for reading contacts
42+
if !userWorkspace.HasPermission(domain.PermissionResourceContacts, domain.PermissionTypeRead) {
43+
return nil, domain.NewPermissionError(
44+
domain.PermissionResourceContacts,
45+
domain.PermissionTypeRead,
46+
"Insufficient permissions: read access to contacts required",
47+
)
48+
}
49+
4150
contact, err := s.repo.GetContactByEmail(ctx, workspaceID, email)
4251
if err != nil {
4352
if strings.Contains(err.Error(), "contact not found") {
@@ -52,11 +61,20 @@ func (s *ContactService) GetContactByEmail(ctx context.Context, workspaceID stri
5261

5362
func (s *ContactService) GetContactByExternalID(ctx context.Context, externalID string, workspaceID string) (*domain.Contact, error) {
5463
var err error
55-
ctx, _, _, err = s.authService.AuthenticateUserForWorkspace(ctx, workspaceID)
64+
ctx, _, userWorkspace, err := s.authService.AuthenticateUserForWorkspace(ctx, workspaceID)
5665
if err != nil {
5766
return nil, fmt.Errorf("failed to authenticate user: %w", err)
5867
}
5968

69+
// Check permission for reading contacts
70+
if !userWorkspace.HasPermission(domain.PermissionResourceContacts, domain.PermissionTypeRead) {
71+
return nil, domain.NewPermissionError(
72+
domain.PermissionResourceContacts,
73+
domain.PermissionTypeRead,
74+
"Insufficient permissions: read access to contacts required",
75+
)
76+
}
77+
6078
contact, err := s.repo.GetContactByExternalID(ctx, externalID, workspaceID)
6179
if err != nil {
6280
if strings.Contains(err.Error(), "contact not found") {
@@ -71,11 +89,20 @@ func (s *ContactService) GetContactByExternalID(ctx context.Context, externalID
7189

7290
func (s *ContactService) GetContacts(ctx context.Context, req *domain.GetContactsRequest) (*domain.GetContactsResponse, error) {
7391
var err error
74-
ctx, _, _, err = s.authService.AuthenticateUserForWorkspace(ctx, req.WorkspaceID)
92+
ctx, _, userWorkspace, err := s.authService.AuthenticateUserForWorkspace(ctx, req.WorkspaceID)
7593
if err != nil {
7694
return nil, fmt.Errorf("failed to authenticate user: %w", err)
7795
}
7896

97+
// Check permission for reading contacts
98+
if !userWorkspace.HasPermission(domain.PermissionResourceContacts, domain.PermissionTypeRead) {
99+
return nil, domain.NewPermissionError(
100+
domain.PermissionResourceContacts,
101+
domain.PermissionTypeRead,
102+
"Insufficient permissions: read access to contacts required",
103+
)
104+
}
105+
79106
response, err := s.repo.GetContacts(ctx, req)
80107
if err != nil {
81108
s.logger.Error(fmt.Sprintf("Failed to get contacts: %v", err))
@@ -87,11 +114,20 @@ func (s *ContactService) GetContacts(ctx context.Context, req *domain.GetContact
87114

88115
func (s *ContactService) DeleteContact(ctx context.Context, email string, workspaceID string) error {
89116
var err error
90-
ctx, _, _, err = s.authService.AuthenticateUserForWorkspace(ctx, workspaceID)
117+
ctx, _, userWorkspace, err := s.authService.AuthenticateUserForWorkspace(ctx, workspaceID)
91118
if err != nil {
92119
return fmt.Errorf("failed to authenticate user: %w", err)
93120
}
94121

122+
// Check permission for writing contacts
123+
if !userWorkspace.HasPermission(domain.PermissionResourceContacts, domain.PermissionTypeWrite) {
124+
return domain.NewPermissionError(
125+
domain.PermissionResourceContacts,
126+
domain.PermissionTypeWrite,
127+
"Insufficient permissions: write access to contacts required",
128+
)
129+
}
130+
95131
if err := s.repo.DeleteContact(ctx, email, workspaceID); err != nil {
96132
s.logger.WithField("email", email).Error(fmt.Sprintf("Failed to delete contact: %v", err))
97133
return fmt.Errorf("failed to delete contact: %w", err)
@@ -106,12 +142,18 @@ func (s *ContactService) BatchImportContacts(ctx context.Context, workspaceID st
106142
}
107143

108144
var err error
109-
ctx, _, _, err = s.authService.AuthenticateUserForWorkspace(ctx, workspaceID)
145+
ctx, _, userWorkspace, err := s.authService.AuthenticateUserForWorkspace(ctx, workspaceID)
110146
if err != nil {
111147
response.Error = fmt.Sprintf("failed to authenticate user: %v", err)
112148
return response
113149
}
114150

151+
// Check permission for writing contacts
152+
if !userWorkspace.HasPermission(domain.PermissionResourceContacts, domain.PermissionTypeWrite) {
153+
response.Error = "Insufficient permissions: write access to contacts required"
154+
return response
155+
}
156+
115157
// Validate and upsert
116158
for i, contact := range contacts {
117159
now := time.Now().UTC()
@@ -159,14 +201,22 @@ func (s *ContactService) UpsertContact(ctx context.Context, workspaceID string,
159201
}
160202

161203
var err error
162-
ctx, _, _, err = s.authService.AuthenticateUserForWorkspace(ctx, workspaceID)
204+
ctx, _, userWorkspace, err := s.authService.AuthenticateUserForWorkspace(ctx, workspaceID)
163205
if err != nil {
164206
operation.Action = domain.UpsertContactOperationError
165207
operation.Error = err.Error()
166208
s.logger.WithField("email", contact.Email).Error(fmt.Sprintf("Failed to authenticate user: %v", err))
167209
return operation
168210
}
169211

212+
// Check permission for writing contacts
213+
if !userWorkspace.HasPermission(domain.PermissionResourceContacts, domain.PermissionTypeWrite) {
214+
operation.Action = domain.UpsertContactOperationError
215+
operation.Error = "Insufficient permissions: write access to contacts required"
216+
s.logger.WithField("email", contact.Email).Error("Insufficient permissions: write access to contacts required")
217+
return operation
218+
}
219+
170220
if err := contact.Validate(); err != nil {
171221
operation.Action = domain.UpsertContactOperationError
172222
operation.Error = err.Error()

0 commit comments

Comments
 (0)