From c394391b8c73b8628126b10842e58f52a1025122 Mon Sep 17 00:00:00 2001 From: Kelvin Wang Date: Mon, 12 Aug 2019 13:14:04 -0400 Subject: [PATCH] feat(authorizer): add notification endpoint --- authorizer/notification_endpoint.go | 185 +++++ authorizer/notification_endpoint_test.go | 874 +++++++++++++++++++++++ 2 files changed, 1059 insertions(+) create mode 100644 authorizer/notification_endpoint.go create mode 100644 authorizer/notification_endpoint_test.go diff --git a/authorizer/notification_endpoint.go b/authorizer/notification_endpoint.go new file mode 100644 index 0000000000..913eacdf6d --- /dev/null +++ b/authorizer/notification_endpoint.go @@ -0,0 +1,185 @@ +package authorizer + +import ( + "context" + + "github.com/influxdata/influxdb" +) + +var _ influxdb.NotificationEndpointService = (*NotificationEndpointService)(nil) + +// NotificationEndpointService wraps a influxdb.NotificationEndpointService and authorizes actions +// against it appropriately. +type NotificationEndpointService struct { + s influxdb.NotificationEndpointService + influxdb.UserResourceMappingService + influxdb.OrganizationService + influxdb.SecretService +} + +// NewNotificationEndpointService constructs an instance of an authorizing notification endpoint serivce. +func NewNotificationEndpointService( + s influxdb.NotificationEndpointService, + urm influxdb.UserResourceMappingService, + org influxdb.OrganizationService, + srt influxdb.SecretService, +) *NotificationEndpointService { + return &NotificationEndpointService{ + s: s, + UserResourceMappingService: urm, + OrganizationService: org, + SecretService: srt, + } +} + +func newNotificationEndpointPermission(a influxdb.Action, orgID, id influxdb.ID) (*influxdb.Permission, error) { + return influxdb.NewPermissionAtID(id, a, influxdb.NotificationEndpointResourceType, orgID) +} + +func authorizeReadNotificationEndpoint(ctx context.Context, orgID, id influxdb.ID) error { + p, err := newNotificationEndpointPermission(influxdb.ReadAction, orgID, id) + if err != nil { + return err + } + + pOrg, err := newOrgPermission(influxdb.WriteAction, orgID) + if err != nil { + return err + } + + err0 := IsAllowed(ctx, *p) + err1 := IsAllowed(ctx, *pOrg) + + if err0 != nil && err1 != nil { + return err0 + } + + return nil +} + +func authorizeWriteNotificationEndpoint(ctx context.Context, orgID, id influxdb.ID) error { + p, err := newNotificationEndpointPermission(influxdb.WriteAction, orgID, id) + if err != nil { + return err + } + + pOrg, err := newOrgPermission(influxdb.WriteAction, orgID) + if err != nil { + return err + } + + err0 := IsAllowed(ctx, *p) + err1 := IsAllowed(ctx, *pOrg) + + if err0 != nil && err1 != nil { + return err0 + } + + return nil +} + +// FindNotificationEndpointByID checks to see if the authorizer on context has read access to the id provided. +func (s *NotificationEndpointService) FindNotificationEndpointByID(ctx context.Context, id influxdb.ID) (influxdb.NotificationEndpoint, error) { + edp, err := s.s.FindNotificationEndpointByID(ctx, id) + if err != nil { + return nil, err + } + + if err := authorizeReadNotificationEndpoint(ctx, edp.GetOrgID(), edp.GetID()); err != nil { + return nil, err + } + + return edp, nil +} + +// FindNotificationEndpoints retrieves all notification endpoints that match the provided filter and then filters the list down to only the resources that are authorized. +func (s *NotificationEndpointService) FindNotificationEndpoints(ctx context.Context, filter influxdb.NotificationEndpointFilter, opt ...influxdb.FindOptions) ([]influxdb.NotificationEndpoint, int, error) { + // TODO: we'll likely want to push this operation into the database eventually since fetching the whole list of data + // will likely be expensive. + edps, _, err := s.s.FindNotificationEndpoints(ctx, filter, opt...) + if err != nil { + return nil, 0, err + } + + // This filters without allocating + // https://github.com/golang/go/wiki/SliceTricks#filtering-without-allocating + endpoints := edps[:0] + for _, edp := range edps { + err := authorizeReadNotificationEndpoint(ctx, edp.GetOrgID(), edp.GetID()) + if err != nil && influxdb.ErrorCode(err) != influxdb.EUnauthorized { + return nil, 0, err + } + + if influxdb.ErrorCode(err) == influxdb.EUnauthorized { + continue + } + + endpoints = append(endpoints, edp) + } + + return endpoints, len(endpoints), nil +} + +// CreateNotificationEndpoint checks to see if the authorizer on context has write access to the global notification endpoint resource. +func (s *NotificationEndpointService) CreateNotificationEndpoint(ctx context.Context, edp influxdb.NotificationEndpoint, userID influxdb.ID) error { + p, err := influxdb.NewPermission(influxdb.WriteAction, influxdb.NotificationEndpointResourceType, edp.GetOrgID()) + if err != nil { + return err + } + + pOrg, err := newOrgPermission(influxdb.WriteAction, edp.GetOrgID()) + if err != nil { + return err + } + + err0 := IsAllowed(ctx, *p) + err1 := IsAllowed(ctx, *pOrg) + + if err0 != nil && err1 != nil { + return err0 + } + + return s.s.CreateNotificationEndpoint(ctx, edp, userID) +} + +// UpdateNotificationEndpoint checks to see if the authorizer on context has write access to the notification endpoint provided. +func (s *NotificationEndpointService) UpdateNotificationEndpoint(ctx context.Context, id influxdb.ID, upd influxdb.NotificationEndpoint, userID influxdb.ID) (influxdb.NotificationEndpoint, error) { + edp, err := s.FindNotificationEndpointByID(ctx, id) + if err != nil { + return nil, err + } + + if err := authorizeWriteNotificationEndpoint(ctx, edp.GetOrgID(), id); err != nil { + return nil, err + } + + return s.s.UpdateNotificationEndpoint(ctx, id, upd, userID) +} + +// PatchNotificationEndpoint checks to see if the authorizer on context has write access to the notification endpoint provided. +func (s *NotificationEndpointService) PatchNotificationEndpoint(ctx context.Context, id influxdb.ID, upd influxdb.NotificationEndpointUpdate) (influxdb.NotificationEndpoint, error) { + edp, err := s.FindNotificationEndpointByID(ctx, id) + if err != nil { + return nil, err + } + + if err := authorizeWriteNotificationEndpoint(ctx, edp.GetOrgID(), id); err != nil { + return nil, err + } + + return s.s.PatchNotificationEndpoint(ctx, id, upd) +} + +// DeleteNotificationEndpoint checks to see if the authorizer on context has write access to the notification endpoint provided. +func (s *NotificationEndpointService) DeleteNotificationEndpoint(ctx context.Context, id influxdb.ID) error { + edp, err := s.FindNotificationEndpointByID(ctx, id) + if err != nil { + return err + } + + if err := authorizeWriteNotificationEndpoint(ctx, edp.GetOrgID(), id); err != nil { + return err + } + + return s.s.DeleteNotificationEndpoint(ctx, id) +} diff --git a/authorizer/notification_endpoint_test.go b/authorizer/notification_endpoint_test.go new file mode 100644 index 0000000000..4ad9478bfe --- /dev/null +++ b/authorizer/notification_endpoint_test.go @@ -0,0 +1,874 @@ +package authorizer_test + +import ( + "bytes" + "context" + "sort" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/influxdata/influxdb" + "github.com/influxdata/influxdb/authorizer" + influxdbcontext "github.com/influxdata/influxdb/context" + "github.com/influxdata/influxdb/mock" + "github.com/influxdata/influxdb/notification/endpoint" + influxdbtesting "github.com/influxdata/influxdb/testing" +) + +var notificationEndpointCmpOptions = cmp.Options{ + cmp.Comparer(func(x, y []byte) bool { + return bytes.Equal(x, y) + }), + cmp.Transformer("Sort", func(in []influxdb.NotificationEndpoint) []influxdb.NotificationEndpoint { + out := append([]influxdb.NotificationEndpoint(nil), in...) // Copy input to avoid mutating it + sort.Slice(out, func(i, j int) bool { + return out[i].GetID().String() > out[j].GetID().String() + }) + return out + }), +} + +func TestNotificationEndpointService_FindNotificationEndpointByID(t *testing.T) { + type fields struct { + NotificationEndpointService influxdb.NotificationEndpointService + } + type args struct { + permission influxdb.Permission + id influxdb.ID + } + type wants struct { + err error + } + + tests := []struct { + name string + fields fields + args args + wants wants + }{ + { + name: "authorized to access id", + fields: fields{ + NotificationEndpointService: &mock.NotificationEndpointService{ + FindNotificationEndpointByIDF: func(ctx context.Context, id influxdb.ID) (influxdb.NotificationEndpoint, error) { + return &endpoint.Slack{ + Base: endpoint.Base{ + ID: id, + OrgID: 10, + }, + }, nil + }, + }, + }, + args: args{ + permission: influxdb.Permission{ + Action: "read", + Resource: influxdb.Resource{ + Type: influxdb.NotificationEndpointResourceType, + ID: influxdbtesting.IDPtr(1), + }, + }, + id: 1, + }, + wants: wants{ + err: nil, + }, + }, + { + name: "authorized to access id with org", + fields: fields{ + NotificationEndpointService: &mock.NotificationEndpointService{ + FindNotificationEndpointByIDF: func(ctx context.Context, id influxdb.ID) (influxdb.NotificationEndpoint, error) { + return &endpoint.Slack{ + Base: endpoint.Base{ + ID: id, + OrgID: 10, + }, + }, nil + }, + }, + }, + args: args{ + permission: influxdb.Permission{ + Action: "write", + Resource: influxdb.Resource{ + Type: influxdb.OrgsResourceType, + ID: influxdbtesting.IDPtr(10), + }, + }, + id: 1, + }, + wants: wants{ + err: nil, + }, + }, + { + name: "unauthorized to access id", + fields: fields{ + NotificationEndpointService: &mock.NotificationEndpointService{ + FindNotificationEndpointByIDF: func(ctx context.Context, id influxdb.ID) (influxdb.NotificationEndpoint, error) { + return &endpoint.Slack{ + Base: endpoint.Base{ + ID: id, + OrgID: 10, + }, + }, nil + }, + }, + }, + args: args{ + permission: influxdb.Permission{ + Action: "read", + Resource: influxdb.Resource{ + Type: influxdb.NotificationEndpointResourceType, + ID: influxdbtesting.IDPtr(2), + }, + }, + id: 1, + }, + wants: wants{ + err: &influxdb.Error{ + Msg: "read:orgs/000000000000000a/notificationEndpoints/0000000000000001 is unauthorized", + Code: influxdb.EUnauthorized, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := authorizer.NewNotificationEndpointService(tt.fields.NotificationEndpointService, mock.NewUserResourceMappingService(), mock.NewOrganizationService(), mock.NewSecretService()) + + ctx := context.Background() + ctx = influxdbcontext.SetAuthorizer(ctx, &Authorizer{[]influxdb.Permission{tt.args.permission}}) + + _, err := s.FindNotificationEndpointByID(ctx, tt.args.id) + influxdbtesting.ErrorsEqual(t, err, tt.wants.err) + }) + } +} + +func TestNotificationEndpointService_FindNotificationEndpoints(t *testing.T) { + type fields struct { + NotificationEndpointService influxdb.NotificationEndpointService + } + type args struct { + permission influxdb.Permission + } + type wants struct { + err error + notificationEndpoints []influxdb.NotificationEndpoint + } + + tests := []struct { + name string + fields fields + args args + wants wants + }{ + { + name: "authorized to see all notificationEndpoints", + fields: fields{ + NotificationEndpointService: &mock.NotificationEndpointService{ + FindNotificationEndpointsF: func(ctx context.Context, filter influxdb.NotificationEndpointFilter, opt ...influxdb.FindOptions) ([]influxdb.NotificationEndpoint, int, error) { + return []influxdb.NotificationEndpoint{ + &endpoint.Slack{ + Base: endpoint.Base{ + ID: 1, + OrgID: 10, + }, + }, + &endpoint.Slack{ + Base: endpoint.Base{ + ID: 2, + OrgID: 10, + }, + }, + &endpoint.WebHook{ + Base: endpoint.Base{ + ID: 3, + OrgID: 11, + }, + }, + }, 3, nil + }, + }, + }, + args: args{ + permission: influxdb.Permission{ + Action: "read", + Resource: influxdb.Resource{ + Type: influxdb.NotificationEndpointResourceType, + }, + }, + }, + wants: wants{ + notificationEndpoints: []influxdb.NotificationEndpoint{ + &endpoint.Slack{ + Base: endpoint.Base{ + ID: 1, + OrgID: 10, + }, + }, + &endpoint.Slack{ + Base: endpoint.Base{ + ID: 2, + OrgID: 10, + }, + }, + &endpoint.WebHook{ + Base: endpoint.Base{ + ID: 3, + OrgID: 11, + }, + }, + }, + }, + }, + { + name: "authorized to access a single orgs notificationEndpoints", + fields: fields{ + NotificationEndpointService: &mock.NotificationEndpointService{ + FindNotificationEndpointsF: func(ctx context.Context, filter influxdb.NotificationEndpointFilter, opt ...influxdb.FindOptions) ([]influxdb.NotificationEndpoint, int, error) { + return []influxdb.NotificationEndpoint{ + &endpoint.Slack{ + Base: endpoint.Base{ + ID: 1, + OrgID: 10, + }, + }, + &endpoint.Slack{ + Base: endpoint.Base{ + ID: 2, + OrgID: 10, + }, + }, + &endpoint.WebHook{ + Base: endpoint.Base{ + ID: 3, + OrgID: 11, + }, + }, + }, 3, nil + }, + }, + }, + args: args{ + permission: influxdb.Permission{ + Action: "read", + Resource: influxdb.Resource{ + Type: influxdb.NotificationEndpointResourceType, + OrgID: influxdbtesting.IDPtr(10), + }, + }, + }, + wants: wants{ + notificationEndpoints: []influxdb.NotificationEndpoint{ + &endpoint.Slack{ + Base: endpoint.Base{ + ID: 1, + OrgID: 10, + }, + }, + &endpoint.Slack{ + Base: endpoint.Base{ + ID: 2, + OrgID: 10, + }, + }, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := authorizer.NewNotificationEndpointService(tt.fields.NotificationEndpointService, + mock.NewUserResourceMappingService(), + mock.NewOrganizationService(), mock.NewSecretService()) + + ctx := context.Background() + ctx = influxdbcontext.SetAuthorizer(ctx, &Authorizer{[]influxdb.Permission{tt.args.permission}}) + + edps, _, err := s.FindNotificationEndpoints(ctx, influxdb.NotificationEndpointFilter{}) + influxdbtesting.ErrorsEqual(t, err, tt.wants.err) + + if diff := cmp.Diff(edps, tt.wants.notificationEndpoints, notificationEndpointCmpOptions...); diff != "" { + t.Errorf("notificationEndpoints are different -got/+want\ndiff %s", diff) + } + }) + } +} + +func TestNotificationEndpointService_UpdateNotificationEndpoint(t *testing.T) { + type fields struct { + NotificationEndpointService influxdb.NotificationEndpointService + } + type args struct { + id influxdb.ID + permissions []influxdb.Permission + } + type wants struct { + err error + } + + tests := []struct { + name string + fields fields + args args + wants wants + }{ + { + name: "authorized to update notificationEndpoint", + fields: fields{ + NotificationEndpointService: &mock.NotificationEndpointService{ + FindNotificationEndpointByIDF: func(ctc context.Context, id influxdb.ID) (influxdb.NotificationEndpoint, error) { + return &endpoint.Slack{ + Base: endpoint.Base{ + ID: 1, + OrgID: 10, + }, + }, nil + }, + UpdateNotificationEndpointF: func(ctx context.Context, id influxdb.ID, upd influxdb.NotificationEndpoint, userID influxdb.ID) (influxdb.NotificationEndpoint, error) { + return &endpoint.Slack{ + Base: endpoint.Base{ + ID: 1, + OrgID: 10, + }, + }, nil + }, + }, + }, + args: args{ + id: 1, + permissions: []influxdb.Permission{ + { + Action: "write", + Resource: influxdb.Resource{ + Type: influxdb.NotificationEndpointResourceType, + ID: influxdbtesting.IDPtr(1), + }, + }, + { + Action: "read", + Resource: influxdb.Resource{ + Type: influxdb.NotificationEndpointResourceType, + ID: influxdbtesting.IDPtr(1), + }, + }, + }, + }, + wants: wants{ + err: nil, + }, + }, + { + name: "authorized to update notificationEndpoint with org owner", + fields: fields{ + NotificationEndpointService: &mock.NotificationEndpointService{ + FindNotificationEndpointByIDF: func(ctc context.Context, id influxdb.ID) (influxdb.NotificationEndpoint, error) { + return &endpoint.Slack{ + Base: endpoint.Base{ + ID: 1, + OrgID: 10, + }, + }, nil + }, + UpdateNotificationEndpointF: func(ctx context.Context, id influxdb.ID, upd influxdb.NotificationEndpoint, userID influxdb.ID) (influxdb.NotificationEndpoint, error) { + return &endpoint.Slack{ + Base: endpoint.Base{ + ID: 1, + OrgID: 10, + }, + }, nil + }, + }, + }, + args: args{ + id: 1, + permissions: []influxdb.Permission{ + { + Action: "write", + Resource: influxdb.Resource{ + Type: influxdb.OrgsResourceType, + ID: influxdbtesting.IDPtr(10), + }, + }, + }, + }, + wants: wants{ + err: nil, + }, + }, + { + name: "unauthorized to update notificationEndpoint", + fields: fields{ + NotificationEndpointService: &mock.NotificationEndpointService{ + FindNotificationEndpointByIDF: func(ctc context.Context, id influxdb.ID) (influxdb.NotificationEndpoint, error) { + return &endpoint.Slack{ + Base: endpoint.Base{ + ID: 1, + OrgID: 10, + }, + }, nil + }, + UpdateNotificationEndpointF: func(ctx context.Context, id influxdb.ID, upd influxdb.NotificationEndpoint, userID influxdb.ID) (influxdb.NotificationEndpoint, error) { + return &endpoint.Slack{ + Base: endpoint.Base{ + ID: 1, + OrgID: 10, + }, + }, nil + }, + }, + }, + args: args{ + id: 1, + permissions: []influxdb.Permission{ + { + Action: "read", + Resource: influxdb.Resource{ + Type: influxdb.NotificationEndpointResourceType, + ID: influxdbtesting.IDPtr(1), + }, + }, + }, + }, + wants: wants{ + err: &influxdb.Error{ + Msg: "write:orgs/000000000000000a/notificationEndpoints/0000000000000001 is unauthorized", + Code: influxdb.EUnauthorized, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := authorizer.NewNotificationEndpointService(tt.fields.NotificationEndpointService, + mock.NewUserResourceMappingService(), + mock.NewOrganizationService(), mock.NewSecretService()) + + ctx := context.Background() + ctx = influxdbcontext.SetAuthorizer(ctx, &Authorizer{tt.args.permissions}) + + _, err := s.UpdateNotificationEndpoint(ctx, tt.args.id, &endpoint.Slack{}, influxdb.ID(1)) + influxdbtesting.ErrorsEqual(t, err, tt.wants.err) + }) + } +} + +func TestNotificationEndpointService_PatchNotificationEndpoint(t *testing.T) { + type fields struct { + NotificationEndpointService influxdb.NotificationEndpointService + } + type args struct { + id influxdb.ID + permissions []influxdb.Permission + } + type wants struct { + err error + } + + tests := []struct { + name string + fields fields + args args + wants wants + }{ + { + name: "authorized to patch notificationEndpoint", + fields: fields{ + NotificationEndpointService: &mock.NotificationEndpointService{ + FindNotificationEndpointByIDF: func(ctc context.Context, id influxdb.ID) (influxdb.NotificationEndpoint, error) { + return &endpoint.Slack{ + Base: endpoint.Base{ + ID: 1, + OrgID: 10, + }, + }, nil + }, + PatchNotificationEndpointF: func(ctx context.Context, id influxdb.ID, upd influxdb.NotificationEndpointUpdate) (influxdb.NotificationEndpoint, error) { + return &endpoint.Slack{ + Base: endpoint.Base{ + ID: 1, + OrgID: 10, + }, + }, nil + }, + }, + }, + args: args{ + id: 1, + permissions: []influxdb.Permission{ + { + Action: "write", + Resource: influxdb.Resource{ + Type: influxdb.NotificationEndpointResourceType, + ID: influxdbtesting.IDPtr(1), + }, + }, + { + Action: "read", + Resource: influxdb.Resource{ + Type: influxdb.NotificationEndpointResourceType, + ID: influxdbtesting.IDPtr(1), + }, + }, + }, + }, + wants: wants{ + err: nil, + }, + }, + { + name: "authorized to patch notificationEndpoint", + fields: fields{ + NotificationEndpointService: &mock.NotificationEndpointService{ + FindNotificationEndpointByIDF: func(ctc context.Context, id influxdb.ID) (influxdb.NotificationEndpoint, error) { + return &endpoint.Slack{ + Base: endpoint.Base{ + ID: 1, + OrgID: 10, + }, + }, nil + }, + PatchNotificationEndpointF: func(ctx context.Context, id influxdb.ID, upd influxdb.NotificationEndpointUpdate) (influxdb.NotificationEndpoint, error) { + return &endpoint.Slack{ + Base: endpoint.Base{ + ID: 1, + OrgID: 10, + }, + }, nil + }, + }, + }, + args: args{ + id: 1, + permissions: []influxdb.Permission{ + { + Action: "write", + Resource: influxdb.Resource{ + Type: influxdb.OrgsResourceType, + ID: influxdbtesting.IDPtr(10), + }, + }, + }, + }, + wants: wants{ + err: nil, + }, + }, + { + name: "unauthorized to patch notificationEndpoint", + fields: fields{ + NotificationEndpointService: &mock.NotificationEndpointService{ + FindNotificationEndpointByIDF: func(ctc context.Context, id influxdb.ID) (influxdb.NotificationEndpoint, error) { + return &endpoint.Slack{ + Base: endpoint.Base{ + ID: 1, + OrgID: 10, + }, + }, nil + }, + PatchNotificationEndpointF: func(ctx context.Context, id influxdb.ID, upd influxdb.NotificationEndpointUpdate) (influxdb.NotificationEndpoint, error) { + return &endpoint.Slack{ + Base: endpoint.Base{ + ID: 1, + OrgID: 10, + }, + }, nil + }, + }, + }, + args: args{ + id: 1, + permissions: []influxdb.Permission{ + { + Action: "read", + Resource: influxdb.Resource{ + Type: influxdb.NotificationEndpointResourceType, + ID: influxdbtesting.IDPtr(1), + }, + }, + }, + }, + wants: wants{ + err: &influxdb.Error{ + Msg: "write:orgs/000000000000000a/notificationEndpoints/0000000000000001 is unauthorized", + Code: influxdb.EUnauthorized, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := authorizer.NewNotificationEndpointService(tt.fields.NotificationEndpointService, mock.NewUserResourceMappingService(), + mock.NewOrganizationService(), mock.NewSecretService()) + + ctx := context.Background() + ctx = influxdbcontext.SetAuthorizer(ctx, &Authorizer{tt.args.permissions}) + + _, err := s.PatchNotificationEndpoint(ctx, tt.args.id, influxdb.NotificationEndpointUpdate{}) + influxdbtesting.ErrorsEqual(t, err, tt.wants.err) + }) + } +} + +func TestNotificationEndpointService_DeleteNotificationEndpoint(t *testing.T) { + type fields struct { + NotificationEndpointService influxdb.NotificationEndpointService + } + type args struct { + id influxdb.ID + permissions []influxdb.Permission + } + type wants struct { + err error + } + + tests := []struct { + name string + fields fields + args args + wants wants + }{ + { + name: "authorized to delete notificationEndpoint", + fields: fields{ + NotificationEndpointService: &mock.NotificationEndpointService{ + FindNotificationEndpointByIDF: func(ctc context.Context, id influxdb.ID) (influxdb.NotificationEndpoint, error) { + return &endpoint.Slack{ + Base: endpoint.Base{ + ID: 1, + OrgID: 10, + }, + }, nil + }, + DeleteNotificationEndpointF: func(ctx context.Context, id influxdb.ID) error { + return nil + }, + }, + }, + args: args{ + id: 1, + permissions: []influxdb.Permission{ + { + Action: "write", + Resource: influxdb.Resource{ + Type: influxdb.NotificationEndpointResourceType, + ID: influxdbtesting.IDPtr(1), + }, + }, + { + Action: "read", + Resource: influxdb.Resource{ + Type: influxdb.NotificationEndpointResourceType, + ID: influxdbtesting.IDPtr(1), + }, + }, + }, + }, + wants: wants{ + err: nil, + }, + }, + { + name: "authorized to delete notificationEndpoint", + fields: fields{ + NotificationEndpointService: &mock.NotificationEndpointService{ + FindNotificationEndpointByIDF: func(ctc context.Context, id influxdb.ID) (influxdb.NotificationEndpoint, error) { + return &endpoint.Slack{ + Base: endpoint.Base{ + ID: 1, + OrgID: 10, + }, + }, nil + }, + DeleteNotificationEndpointF: func(ctx context.Context, id influxdb.ID) error { + return nil + }, + }, + }, + args: args{ + id: 1, + permissions: []influxdb.Permission{ + { + Action: "write", + Resource: influxdb.Resource{ + Type: influxdb.OrgsResourceType, + ID: influxdbtesting.IDPtr(10), + }, + }, + }, + }, + wants: wants{ + err: nil, + }, + }, + { + name: "unauthorized to delete notificationEndpoint", + fields: fields{ + NotificationEndpointService: &mock.NotificationEndpointService{ + FindNotificationEndpointByIDF: func(ctc context.Context, id influxdb.ID) (influxdb.NotificationEndpoint, error) { + return &endpoint.Slack{ + Base: endpoint.Base{ + ID: 1, + OrgID: 10, + }, + }, nil + }, + DeleteNotificationEndpointF: func(ctx context.Context, id influxdb.ID) error { + return nil + }, + }, + }, + args: args{ + id: 1, + permissions: []influxdb.Permission{ + { + Action: "read", + Resource: influxdb.Resource{ + Type: influxdb.NotificationEndpointResourceType, + ID: influxdbtesting.IDPtr(1), + }, + }, + }, + }, + wants: wants{ + err: &influxdb.Error{ + Msg: "write:orgs/000000000000000a/notificationEndpoints/0000000000000001 is unauthorized", + Code: influxdb.EUnauthorized, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := authorizer.NewNotificationEndpointService(tt.fields.NotificationEndpointService, mock.NewUserResourceMappingService(), + mock.NewOrganizationService(), + mock.NewSecretService(), + ) + + ctx := context.Background() + ctx = influxdbcontext.SetAuthorizer(ctx, &Authorizer{tt.args.permissions}) + + err := s.DeleteNotificationEndpoint(ctx, tt.args.id) + influxdbtesting.ErrorsEqual(t, err, tt.wants.err) + }) + } +} + +func TestNotificationEndpointService_CreateNotificationEndpoint(t *testing.T) { + type fields struct { + NotificationEndpointService influxdb.NotificationEndpointService + } + type args struct { + permission influxdb.Permission + orgID influxdb.ID + } + type wants struct { + err error + } + + tests := []struct { + name string + fields fields + args args + wants wants + }{ + { + name: "authorized to create notificationEndpoint", + fields: fields{ + NotificationEndpointService: &mock.NotificationEndpointService{ + CreateNotificationEndpointF: func(ctx context.Context, tc influxdb.NotificationEndpoint, userID influxdb.ID) error { + return nil + }, + }, + }, + args: args{ + orgID: 10, + permission: influxdb.Permission{ + Action: "write", + Resource: influxdb.Resource{ + Type: influxdb.NotificationEndpointResourceType, + OrgID: influxdbtesting.IDPtr(10), + }, + }, + }, + wants: wants{ + err: nil, + }, + }, + { + name: "authorized to create notificationEndpoint with org owner", + fields: fields{ + NotificationEndpointService: &mock.NotificationEndpointService{ + CreateNotificationEndpointF: func(ctx context.Context, tc influxdb.NotificationEndpoint, userID influxdb.ID) error { + return nil + }, + }, + }, + args: args{ + orgID: 10, + permission: influxdb.Permission{ + Action: "write", + Resource: influxdb.Resource{ + Type: influxdb.OrgsResourceType, + ID: influxdbtesting.IDPtr(10), + }, + }, + }, + wants: wants{ + err: nil, + }, + }, + { + name: "unauthorized to create notificationEndpoint", + fields: fields{ + NotificationEndpointService: &mock.NotificationEndpointService{ + CreateNotificationEndpointF: func(ctx context.Context, tc influxdb.NotificationEndpoint, userID influxdb.ID) error { + return nil + }, + }, + }, + args: args{ + orgID: 10, + permission: influxdb.Permission{ + Action: "write", + Resource: influxdb.Resource{ + Type: influxdb.NotificationEndpointResourceType, + ID: influxdbtesting.IDPtr(1), + }, + }, + }, + wants: wants{ + err: &influxdb.Error{ + Msg: "write:orgs/000000000000000a/notificationEndpoints is unauthorized", + Code: influxdb.EUnauthorized, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := authorizer.NewNotificationEndpointService(tt.fields.NotificationEndpointService, + mock.NewUserResourceMappingService(), + mock.NewOrganizationService(), + mock.NewSecretService()) + + ctx := context.Background() + ctx = influxdbcontext.SetAuthorizer(ctx, &Authorizer{[]influxdb.Permission{tt.args.permission}}) + + err := s.CreateNotificationEndpoint(ctx, &endpoint.Slack{ + Base: endpoint.Base{ + OrgID: tt.args.orgID}, + }, influxdb.ID(1)) + influxdbtesting.ErrorsEqual(t, err, tt.wants.err) + }) + } +}