Alerting: Remove option to return settings from api/v1/receivers and restrict provisioning action access (#90861)

* Remove provisioning action access to v1/receivers api

* Separate ListOnly functionality to its own method without decryption
This commit is contained in:
Matthew Jacobson 2024-08-05 11:49:23 -04:00 committed by GitHub
parent 4d23382497
commit 53cfdf0ef8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 145 additions and 105 deletions

View File

@ -20,7 +20,7 @@ type NotificationSrv struct {
type ReceiverService interface {
GetReceiver(ctx context.Context, q models.GetReceiverQuery, u identity.Requester) (definitions.GettableApiReceiver, error)
GetReceivers(ctx context.Context, q models.GetReceiversQuery, u identity.Requester) ([]definitions.GettableApiReceiver, error)
ListReceivers(ctx context.Context, q models.ListReceiversQuery, user identity.Requester) ([]definitions.GettableApiReceiver, error)
}
func (srv *NotificationSrv) RouteGetTimeInterval(c *contextmodel.ReqContext, name string) response.Response {
@ -55,15 +55,14 @@ func (srv *NotificationSrv) RouteGetReceiver(c *contextmodel.ReqContext, name st
}
func (srv *NotificationSrv) RouteGetReceivers(c *contextmodel.ReqContext) response.Response {
q := models.GetReceiversQuery{
OrgID: c.SignedInUser.OrgID,
Names: c.QueryStrings("names"),
Limit: c.QueryInt("limit"),
Offset: c.QueryInt("offset"),
Decrypt: c.QueryBool("decrypt"),
q := models.ListReceiversQuery{
OrgID: c.SignedInUser.OrgID,
Names: c.QueryStrings("names"),
Limit: c.QueryInt("limit"),
Offset: c.QueryInt("offset"),
}
receivers, err := srv.receiverService.GetReceivers(c.Req.Context(), q, c.SignedInUser)
receivers, err := srv.receiverService.ListReceivers(c.Req.Context(), q, c.SignedInUser)
if err != nil {
return response.ErrOrFallback(http.StatusInternalServerError, "failed to get receiver groups", err)
}

View File

@ -120,7 +120,7 @@ func TestRouteGetReceivers(t *testing.T) {
},
},
}
fakeReceiverSvc.GetReceiversFn = func(ctx context.Context, q models.GetReceiversQuery, u identity.Requester) ([]definitions.GettableApiReceiver, error) {
fakeReceiverSvc.ListReceiversFn = func(ctx context.Context, q models.ListReceiversQuery, u identity.Requester) ([]definitions.GettableApiReceiver, error) {
return expected, nil
}
handler := NewNotificationsApi(newNotificationSrv(fakeReceiverSvc))
@ -134,7 +134,7 @@ func TestRouteGetReceivers(t *testing.T) {
})
t.Run("builds query from request context", func(t *testing.T) {
fakeReceiverSvc.GetReceiversFn = func(ctx context.Context, q models.GetReceiversQuery, u identity.Requester) ([]definitions.GettableApiReceiver, error) {
fakeReceiverSvc.ListReceiversFn = func(ctx context.Context, q models.ListReceiversQuery, u identity.Requester) ([]definitions.GettableApiReceiver, error) {
return []definitions.GettableApiReceiver{}, nil
}
handler := NewNotificationsApi(newNotificationSrv(fakeReceiverSvc))
@ -148,19 +148,18 @@ func TestRouteGetReceivers(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Status())
call := fakeReceiverSvc.PopMethodCall()
require.Equal(t, "GetReceivers", call.Method)
expectedQ := models.GetReceiversQuery{
Names: []string{"receiver1", "receiver2"},
Limit: 1,
Offset: 2,
Decrypt: true,
OrgID: 1,
require.Equal(t, "ListReceivers", call.Method)
expectedQ := models.ListReceiversQuery{
Names: []string{"receiver1", "receiver2"},
Limit: 1,
Offset: 2,
OrgID: 1,
}
require.Equal(t, expectedQ, call.Args[1])
})
t.Run("should pass along permission denied response", func(t *testing.T) {
fakeReceiverSvc.GetReceiversFn = func(ctx context.Context, q models.GetReceiversQuery, u identity.Requester) ([]definitions.GettableApiReceiver, error) {
fakeReceiverSvc.ListReceiversFn = func(ctx context.Context, q models.ListReceiversQuery, u identity.Requester) ([]definitions.GettableApiReceiver, error) {
return nil, ac.ErrAuthorizationBase.Errorf("")
}
handler := NewNotificationsApi(newNotificationSrv(fakeReceiverSvc))
@ -197,33 +196,9 @@ func TestRouteGetReceiversResponses(t *testing.T) {
require.Equal(t, 200, response.Status())
})
t.Run("decrypt true without alert.provisioning.secrets:read permissions returns 403", func(t *testing.T) {
recPermCheck := false
env := createTestEnv(t, testConfig)
env.ac = &recordingAccessControlFake{
Callback: func(user *user.SignedInUser, evaluator accesscontrol.Evaluator) (bool, error) {
if strings.Contains(evaluator.String(), accesscontrol.ActionAlertingReceiversReadSecrets) {
recPermCheck = true
}
return false, nil
},
}
sut := createNotificationSrvSutFromEnv(t, &env)
rc := createTestRequestCtx()
rc.Context.Req.Form.Set("decrypt", "true")
response := sut.RouteGetReceivers(&rc)
require.True(t, recPermCheck)
require.Equal(t, 403, response.Status())
})
t.Run("json body content is as expected", func(t *testing.T) {
expectedDecryptedResponse := `[{"name":"grafana-default-email","grafana_managed_receiver_configs":[{"uid":"ad95bd8a-49ed-4adc-bf89-1b444fa1aa5b","name":"grafana-default-email","type":"email","disableResolveMessage":false,"settings":{"addresses":"\u003cexample@email.com\u003e"},"secureFields":{}}]},{"name":"multiple integrations","grafana_managed_receiver_configs":[{"uid":"c2090fda-f824-4add-b545-5a4d5c2ef082","name":"multiple integrations","type":"prometheus-alertmanager","disableResolveMessage":true,"settings":{"basicAuthPassword":"testpass","basicAuthUser":"test","url":"http://localhost:9093"},"secureFields":{"basicAuthPassword":true}},{"uid":"c84539ec-f87e-4fc5-9a91-7a687d34bbd1","name":"multiple integrations","type":"discord","disableResolveMessage":false,"settings":{"avatar_url":"some avatar","url":"some url","use_discord_username":true},"secureFields":{}}]},{"name":"pagerduty test","grafana_managed_receiver_configs":[{"uid":"b9bf06f8-bde2-4438-9d4a-bba0522dcd4d","name":"pagerduty test","type":"pagerduty","disableResolveMessage":false,"settings":{"client":"some client","integrationKey":"some key","severity":"criticalish"},"secureFields":{"integrationKey":true}}]},{"name":"slack test","grafana_managed_receiver_configs":[{"uid":"cbfd0976-8228-4126-b672-4419f30a9e50","name":"slack test","type":"slack","disableResolveMessage":true,"settings":{"text":"title body test","title":"title test","url":"some secure slack webhook"},"secureFields":{"url":true}}]}]`
expectedRedactedResponse := `[{"name":"grafana-default-email","grafana_managed_receiver_configs":[{"uid":"ad95bd8a-49ed-4adc-bf89-1b444fa1aa5b","name":"grafana-default-email","type":"email","disableResolveMessage":false,"settings":{"addresses":"\u003cexample@email.com\u003e"},"secureFields":{}}]},{"name":"multiple integrations","grafana_managed_receiver_configs":[{"uid":"c2090fda-f824-4add-b545-5a4d5c2ef082","name":"multiple integrations","type":"prometheus-alertmanager","disableResolveMessage":true,"settings":{"basicAuthPassword":"[REDACTED]","basicAuthUser":"test","url":"http://localhost:9093"},"secureFields":{"basicAuthPassword":true}},{"uid":"c84539ec-f87e-4fc5-9a91-7a687d34bbd1","name":"multiple integrations","type":"discord","disableResolveMessage":false,"settings":{"avatar_url":"some avatar","url":"some url","use_discord_username":true},"secureFields":{}}]},{"name":"pagerduty test","grafana_managed_receiver_configs":[{"uid":"b9bf06f8-bde2-4438-9d4a-bba0522dcd4d","name":"pagerduty test","type":"pagerduty","disableResolveMessage":false,"settings":{"client":"some client","integrationKey":"[REDACTED]","severity":"criticalish"},"secureFields":{"integrationKey":true}}]},{"name":"slack test","grafana_managed_receiver_configs":[{"uid":"cbfd0976-8228-4126-b672-4419f30a9e50","name":"slack test","type":"slack","disableResolveMessage":true,"settings":{"text":"title body test","title":"title test","url":"[REDACTED]"},"secureFields":{"url":true}}]}]`
expectedListResponse := `[{"name":"grafana-default-email","grafana_managed_receiver_configs":[{"uid":"ad95bd8a-49ed-4adc-bf89-1b444fa1aa5b","name":"grafana-default-email","type":"email","disableResolveMessage":false,"secureFields":null}]},{"name":"multiple integrations","grafana_managed_receiver_configs":[{"uid":"c2090fda-f824-4add-b545-5a4d5c2ef082","name":"multiple integrations","type":"prometheus-alertmanager","disableResolveMessage":false,"secureFields":null},{"uid":"c84539ec-f87e-4fc5-9a91-7a687d34bbd1","name":"multiple integrations","type":"discord","disableResolveMessage":false,"secureFields":null}]},{"name":"pagerduty test","grafana_managed_receiver_configs":[{"uid":"b9bf06f8-bde2-4438-9d4a-bba0522dcd4d","name":"pagerduty test","type":"pagerduty","disableResolveMessage":false,"secureFields":null}]},{"name":"slack test","grafana_managed_receiver_configs":[{"uid":"cbfd0976-8228-4126-b672-4419f30a9e50","name":"slack test","type":"slack","disableResolveMessage":false,"secureFields":null}]}]`
expectedListResponse := `[{"name":"grafana-default-email","grafana_managed_receiver_configs":[{"uid":"ad95bd8a-49ed-4adc-bf89-1b444fa1aa5b","name":"grafana-default-email","type":"email","disableResolveMessage":false,"secureFields":{}}]},{"name":"multiple integrations","grafana_managed_receiver_configs":[{"uid":"c2090fda-f824-4add-b545-5a4d5c2ef082","name":"multiple integrations","type":"prometheus-alertmanager","disableResolveMessage":false,"secureFields":{}},{"uid":"c84539ec-f87e-4fc5-9a91-7a687d34bbd1","name":"multiple integrations","type":"discord","disableResolveMessage":false,"secureFields":{}}]},{"name":"pagerduty test","grafana_managed_receiver_configs":[{"uid":"b9bf06f8-bde2-4438-9d4a-bba0522dcd4d","name":"pagerduty test","type":"pagerduty","disableResolveMessage":false,"secureFields":{}}]},{"name":"slack test","grafana_managed_receiver_configs":[{"uid":"cbfd0976-8228-4126-b672-4419f30a9e50","name":"slack test","type":"slack","disableResolveMessage":false,"secureFields":{}}]}]`
t.Run("limit offset", func(t *testing.T) {
env := createTestEnv(t, testContactPointConfig)
sut := createNotificationSrvSutFromEnv(t, &env)
@ -233,7 +208,7 @@ func TestRouteGetReceiversResponses(t *testing.T) {
rc.Context.Req.Form.Set("decrypt", "false")
var expected []definitions.GettableApiReceiver
err := json.Unmarshal([]byte(expectedRedactedResponse), &expected)
err := json.Unmarshal([]byte(expectedListResponse), &expected)
require.NoError(t, err)
type testcase struct {
limit int
@ -266,7 +241,7 @@ func TestRouteGetReceiversResponses(t *testing.T) {
})
}
})
t.Run("decrypt false with read permissions is redacted", func(t *testing.T) {
t.Run("decrypt false with read permissions, does not have settings", func(t *testing.T) {
env := createTestEnv(t, testContactPointConfig)
sut := createNotificationSrvSutFromEnv(t, &env)
rc := createTestRequestCtx()
@ -277,7 +252,7 @@ func TestRouteGetReceiversResponses(t *testing.T) {
response := sut.RouteGetReceivers(&rc)
require.Equal(t, 200, response.Status())
require.Equal(t, expectedRedactedResponse, string(response.Body())) // TODO: Should this endpoint ever return settings?
require.Equal(t, expectedListResponse, string(response.Body()))
})
t.Run("decrypt false with only list permissions, does not have settings", func(t *testing.T) {
env := createTestEnv(t, testContactPointConfig)
@ -300,7 +275,7 @@ func TestRouteGetReceiversResponses(t *testing.T) {
require.Equal(t, 200, response.Status())
require.Equal(t, expectedListResponse, string(response.Body()))
})
t.Run("decrypt true with all permissions, contains decrypted settings", func(t *testing.T) {
t.Run("decrypt true with all permissions, does not have settings", func(t *testing.T) {
env := createTestEnv(t, testContactPointConfig)
env.ac = &recordingAccessControlFake{
Callback: func(user *user.SignedInUser, evaluator accesscontrol.Evaluator) (bool, error) {
@ -316,7 +291,7 @@ func TestRouteGetReceiversResponses(t *testing.T) {
response := sut.RouteGetReceivers(&rc)
require.Equal(t, 200, response.Status())
require.Equal(t, expectedDecryptedResponse, string(response.Body())) // TODO: Should this endpoint ever return settings?
require.Equal(t, expectedListResponse, string(response.Body()))
})
})
})

View File

@ -18,6 +18,14 @@ type GetReceiversQuery struct {
Decrypt bool
}
// ListReceiversQuery represents a query for listing receiver groups.
type ListReceiversQuery struct {
OrgID int64
Names []string
Limit int
Offset int
}
// Receiver is the domain model representation of a receiver / contact point.
type Receiver struct {
UID string

View File

@ -412,7 +412,7 @@ func (ng *AlertNG) init() error {
configStore := legacy_storage.NewAlertmanagerConfigStore(ng.store)
receiverService := notifier.NewReceiverService(
ac.NewReceiverAccess[*models.Receiver](ng.accesscontrol, true), // TODO: Remove provisioning actions from regular API.
ac.NewReceiverAccess[*models.Receiver](ng.accesscontrol, false),
configStore,
ng.store,
ng.SecretsService,

View File

@ -48,48 +48,52 @@ func PostableApiAlertingConfigToApiReceivers(c apimodels.PostableApiAlertingConf
type DecryptFn = func(value string) string
func PostableToGettableGrafanaReceiver(r *apimodels.PostableGrafanaReceiver, provenance *models.Provenance, decryptFn DecryptFn, listOnly bool) (apimodels.GettableGrafanaReceiver, error) {
func PostableToGettableGrafanaReceiver(r *apimodels.PostableGrafanaReceiver, provenance *models.Provenance, decryptFn DecryptFn) (apimodels.GettableGrafanaReceiver, error) {
out := apimodels.GettableGrafanaReceiver{
UID: r.UID,
Name: r.Name,
Type: r.Type,
UID: r.UID,
Name: r.Name,
Type: r.Type,
DisableResolveMessage: r.DisableResolveMessage,
SecureFields: make(map[string]bool, len(r.SecureSettings)),
}
if provenance != nil {
out.Provenance = apimodels.Provenance(*provenance)
}
// if we aren't only listing, include the settings in the output
if !listOnly {
secureFields := make(map[string]bool, len(r.SecureSettings))
settings, err := simplejson.NewJson([]byte(r.Settings))
if err != nil {
return apimodels.GettableGrafanaReceiver{}, err
}
for k, v := range r.SecureSettings {
decryptedValue := decryptFn(v)
if decryptedValue == "" {
continue
} else {
settings.Set(k, decryptedValue)
}
secureFields[k] = true
}
jsonBytes, err := settings.MarshalJSON()
if err != nil {
return apimodels.GettableGrafanaReceiver{}, err
}
out.Settings = jsonBytes
out.SecureFields = secureFields
out.DisableResolveMessage = r.DisableResolveMessage
if r.Settings == nil && r.SecureSettings == nil {
return out, nil
}
settings := simplejson.New()
if r.Settings != nil {
var err error
settings, err = simplejson.NewJson(r.Settings)
if err != nil {
return apimodels.GettableGrafanaReceiver{}, err
}
}
for k, v := range r.SecureSettings {
decryptedValue := decryptFn(v)
if decryptedValue == "" {
continue
} else {
settings.Set(k, decryptedValue)
}
out.SecureFields[k] = true
}
jsonBytes, err := settings.MarshalJSON()
if err != nil {
return apimodels.GettableGrafanaReceiver{}, err
}
out.Settings = jsonBytes
return out, nil
}
func PostableToGettableApiReceiver(r *apimodels.PostableApiReceiver, provenances map[string]models.Provenance, decryptFn DecryptFn, listOnly bool) (apimodels.GettableApiReceiver, error) {
func PostableToGettableApiReceiver(r *apimodels.PostableApiReceiver, provenances map[string]models.Provenance, decryptFn DecryptFn) (apimodels.GettableApiReceiver, error) {
out := apimodels.GettableApiReceiver{
Receiver: config.Receiver{
Name: r.Receiver.Name,
@ -102,7 +106,7 @@ func PostableToGettableApiReceiver(r *apimodels.PostableApiReceiver, provenances
prov = &p
}
gettable, err := PostableToGettableGrafanaReceiver(gr, prov, decryptFn, listOnly)
gettable, err := PostableToGettableGrafanaReceiver(gr, prov, decryptFn)
if err != nil {
return apimodels.GettableApiReceiver{}, err
}

View File

@ -107,7 +107,7 @@ func (rs *ReceiverService) GetReceiver(ctx context.Context, q models.GetReceiver
return definitions.GettableApiReceiver{}, err
}
return PostableToGettableApiReceiver(postable, storedProvenances, decryptFn, false)
return PostableToGettableApiReceiver(postable, storedProvenances, decryptFn)
}
// GetReceivers returns a list of receivers a user has access to.
@ -139,11 +139,64 @@ func (rs *ReceiverService) GetReceivers(ctx context.Context, q models.GetReceive
return nil, err
}
// User doesn't have any permissions on the receivers.
// This is mostly a safeguard as it should not be possible with current API endpoints + middleware authentication.
if !readRedactedAccess {
return nil, nil
}
var output []definitions.GettableApiReceiver
for i := q.Offset; i < len(postables); i++ {
r := postables[i]
decryptFn := rs.decryptOrRedact(ctx, decrypt, r.Name, "")
res, err := PostableToGettableApiReceiver(r, storedProvenances, decryptFn)
if err != nil {
return nil, err
}
output = append(output, res)
// stop if we have reached the limit or we have found all the requested receivers
if (len(output) == q.Limit && q.Limit > 0) || (len(output) == len(q.Names)) {
break
}
}
return output, nil
}
// ListReceivers returns a list of receivers a user has access to.
// Receivers can be filtered by name.
// This offers an looser permissions compared to GetReceivers. When a user doesn't have read access it will check for list access instead of returning an empty list.
// If the users has list access, all receiver settings will be removed from the response. This option is for backwards compatibility with the v1/receivers endpoint
// and should be removed when FGAC is fully implemented.
func (rs *ReceiverService) ListReceivers(ctx context.Context, q models.ListReceiversQuery, user identity.Requester) ([]definitions.GettableApiReceiver, error) { // TODO: Remove this method with FGAC.
listAccess, err := rs.authz.HasList(ctx, user)
if err != nil {
return nil, err
}
readRedactedAccess, err := rs.authz.HasReadAll(ctx, user)
if err != nil {
return nil, err
}
uids := make([]string, 0, len(q.Names))
for _, name := range q.Names {
uids = append(uids, legacy_storage.NameToUid(name))
}
revision, err := rs.cfgStore.Get(ctx, q.OrgID)
if err != nil {
return nil, err
}
postables := revision.GetReceivers(uids)
storedProvenances, err := rs.provisioningStore.GetProvenances(ctx, q.OrgID, (&definitions.EmbeddedContactPoint{}).ResourceType())
if err != nil {
return nil, err
}
// User doesn't have any permissions on the receivers.
// This is mostly a safeguard as it should not be possible with current API endpoints + middleware authentication.
if !listAccess && !readRedactedAccess {
@ -154,14 +207,15 @@ func (rs *ReceiverService) GetReceivers(ctx context.Context, q models.GetReceive
for i := q.Offset; i < len(postables); i++ {
r := postables[i]
decryptFn := rs.decryptOrRedact(ctx, decrypt, r.Name, "")
// Remove settings.
for _, integration := range r.GrafanaManagedReceivers {
integration.Settings = nil
integration.SecureSettings = nil
integration.DisableResolveMessage = false
}
// Only has permission to list. This reduces from:
// - Has List permission
// - Doesn't have ReadRedacted (or ReadDecrypted permission since it's a subset).
listOnly := !readRedactedAccess
res, err := PostableToGettableApiReceiver(r, storedProvenances, decryptFn, listOnly)
decryptFn := rs.decryptOrRedact(ctx, false, r.Name, "")
res, err := PostableToGettableApiReceiver(r, storedProvenances, decryptFn)
if err != nil {
return nil, err
}

View File

@ -33,7 +33,7 @@ func TestReceiverService_GetReceiver(t *testing.T) {
redactedUser := &user.SignedInUser{OrgID: 1, Permissions: map[int64]map[string][]string{
1: {
accesscontrol.ActionAlertingProvisioningRead: nil,
accesscontrol.ActionAlertingNotificationsRead: nil,
},
}}
@ -61,7 +61,7 @@ func TestReceiverService_GetReceivers(t *testing.T) {
redactedUser := &user.SignedInUser{OrgID: 1, Permissions: map[int64]map[string][]string{
1: {
accesscontrol.ActionAlertingProvisioningRead: nil,
accesscontrol.ActionAlertingNotificationsRead: nil,
},
}}
@ -94,7 +94,7 @@ func TestReceiverService_DecryptRedact(t *testing.T) {
readUser := &user.SignedInUser{
OrgID: 1,
Permissions: map[int64]map[string][]string{
1: {accesscontrol.ActionAlertingProvisioningRead: nil},
1: {accesscontrol.ActionAlertingNotificationsRead: nil},
},
}
@ -102,8 +102,8 @@ func TestReceiverService_DecryptRedact(t *testing.T) {
OrgID: 1,
Permissions: map[int64]map[string][]string{
1: {
accesscontrol.ActionAlertingProvisioningRead: nil,
accesscontrol.ActionAlertingProvisioningReadSecrets: nil,
accesscontrol.ActionAlertingNotificationsRead: nil,
accesscontrol.ActionAlertingReceiversReadSecrets: nil,
},
},
}
@ -190,7 +190,7 @@ func createReceiverServiceSut(t *testing.T, encryptSvc secrets.Service) *Receive
provisioningStore := fakes.NewFakeProvisioningStore()
return NewReceiverService(
ac.NewReceiverAccess[*models.Receiver](acimpl.ProvideAccessControl(featuremgmt.WithFeatures(), zanzana.NewNoopClient()), true),
ac.NewReceiverAccess[*models.Receiver](acimpl.ProvideAccessControl(featuremgmt.WithFeatures(), zanzana.NewNoopClient()), false),
legacy_storage.NewAlertmanagerConfigStore(store),
provisioningStore,
encryptSvc,

View File

@ -14,15 +14,15 @@ type ReceiverServiceMethodCall struct {
}
type FakeReceiverService struct {
MethodCalls []ReceiverServiceMethodCall
GetReceiverFn func(ctx context.Context, q models.GetReceiverQuery, u identity.Requester) (definitions.GettableApiReceiver, error)
GetReceiversFn func(ctx context.Context, q models.GetReceiversQuery, u identity.Requester) ([]definitions.GettableApiReceiver, error)
MethodCalls []ReceiverServiceMethodCall
GetReceiverFn func(ctx context.Context, q models.GetReceiverQuery, u identity.Requester) (definitions.GettableApiReceiver, error)
ListReceiversFn func(ctx context.Context, q models.ListReceiversQuery, u identity.Requester) ([]definitions.GettableApiReceiver, error)
}
func NewFakeReceiverService() *FakeReceiverService {
return &FakeReceiverService{
GetReceiverFn: defaultReceiverFn,
GetReceiversFn: defaultReceiversFn,
GetReceiverFn: defaultReceiverFn,
ListReceiversFn: defaultReceiversFn,
}
}
@ -31,9 +31,9 @@ func (f *FakeReceiverService) GetReceiver(ctx context.Context, q models.GetRecei
return f.GetReceiverFn(ctx, q, u)
}
func (f *FakeReceiverService) GetReceivers(ctx context.Context, q models.GetReceiversQuery, u identity.Requester) ([]definitions.GettableApiReceiver, error) {
f.MethodCalls = append(f.MethodCalls, ReceiverServiceMethodCall{Method: "GetReceivers", Args: []interface{}{ctx, q}})
return f.GetReceiversFn(ctx, q, u)
func (f *FakeReceiverService) ListReceivers(ctx context.Context, q models.ListReceiversQuery, u identity.Requester) ([]definitions.GettableApiReceiver, error) {
f.MethodCalls = append(f.MethodCalls, ReceiverServiceMethodCall{Method: "ListReceivers", Args: []interface{}{ctx, q}})
return f.ListReceiversFn(ctx, q, u)
}
func (f *FakeReceiverService) PopMethodCall() ReceiverServiceMethodCall {
@ -48,13 +48,13 @@ func (f *FakeReceiverService) PopMethodCall() ReceiverServiceMethodCall {
func (f *FakeReceiverService) Reset() {
f.MethodCalls = nil
f.GetReceiverFn = defaultReceiverFn
f.GetReceiversFn = defaultReceiversFn
f.ListReceiversFn = defaultReceiversFn
}
func defaultReceiverFn(ctx context.Context, q models.GetReceiverQuery, u identity.Requester) (definitions.GettableApiReceiver, error) {
return definitions.GettableApiReceiver{}, nil
}
func defaultReceiversFn(ctx context.Context, q models.GetReceiversQuery, u identity.Requester) ([]definitions.GettableApiReceiver, error) {
func defaultReceiversFn(ctx context.Context, q models.ListReceiversQuery, u identity.Requester) ([]definitions.GettableApiReceiver, error) {
return nil, nil
}