From 8775dcd7bfa7d6154d5a29b21a8f8d187e1f8d77 Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Mon, 20 Dec 2021 14:45:25 -0500 Subject: [PATCH] Fix send-to-self for private messages, and add group query URLs Signed-off-by: Peter Broadhurst --- docs/swagger/swagger.yaml | 162 ++++++++++++++++++ internal/apiserver/route_get_group_by_id.go | 46 +++++ .../apiserver/route_get_group_by_id_test.go | 42 +++++ internal/apiserver/route_get_groups.go | 45 +++++ internal/apiserver/route_get_groups_test.go | 42 +++++ internal/apiserver/routes.go | 2 + internal/definitions/definition_handler.go | 4 +- .../definitions/definition_handler_test.go | 4 +- internal/identity/identitymanager.go | 19 +- internal/identity/identitymanager_test.go | 34 ++++ internal/orchestrator/status.go | 2 +- internal/orchestrator/status_test.go | 10 +- internal/privatemessaging/groupmanager.go | 6 +- .../privatemessaging/groupmanager_test.go | 19 ++ internal/privatemessaging/message_test.go | 12 +- internal/privatemessaging/privatemessaging.go | 5 +- .../privatemessaging/privatemessaging_test.go | 12 +- manifest.json | 16 +- mocks/definitionsmocks/definition_handlers.go | 18 +- mocks/identitymanagermocks/manager.go | 33 ++-- mocks/privatemessagingmocks/manager.go | 18 +- 21 files changed, 484 insertions(+), 67 deletions(-) create mode 100644 internal/apiserver/route_get_group_by_id.go create mode 100644 internal/apiserver/route_get_group_by_id_test.go create mode 100644 internal/apiserver/route_get_groups.go create mode 100644 internal/apiserver/route_get_groups_test.go diff --git a/docs/swagger/swagger.yaml b/docs/swagger/swagger.yaml index 3e6b4e0764..5f37b50525 100644 --- a/docs/swagger/swagger.yaml +++ b/docs/swagger/swagger.yaml @@ -2144,6 +2144,168 @@ paths: description: Success default: description: "" + /namespaces/{ns}/groups: + get: + description: 'TODO: Description' + operationId: getGroups + parameters: + - description: 'TODO: Description' + in: path + name: ns + required: true + schema: + example: default + type: string + - description: Server-side request timeout (millseconds, or set a custom suffix + like 10s) + in: header + name: Request-Timeout + schema: + default: 120s + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: created + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: description + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: hash + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: ledger + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: message + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: namespace + schema: + type: string + - description: Sort field. For multi-field sort use comma separated values (or + multiple query values) with '-' prefix for descending + in: query + name: sort + schema: + type: string + - description: Ascending sort order (overrides all fields in a multi-field sort) + in: query + name: ascending + schema: + type: string + - description: Descending sort order (overrides all fields in a multi-field + sort) + in: query + name: descending + schema: + type: string + - description: 'The number of records to skip (max: 1,000). Unsuitable for bulk + operations' + in: query + name: skip + schema: + type: string + - description: 'The maximum number of records to return (max: 1,000)' + in: query + name: limit + schema: + example: "25" + type: string + - description: Return a total count as well as items (adds extra database processing) + in: query + name: count + schema: + type: string + responses: + "200": + content: + application/json: + schema: + items: + properties: + created: {} + hash: {} + ledger: {} + members: + items: + properties: + identity: + type: string + node: {} + type: object + type: array + message: {} + name: + type: string + namespace: + type: string + type: object + type: array + description: Success + default: + description: "" + /namespaces/{ns}/groups/{groupid}: + get: + description: 'TODO: Description' + operationId: getGroupByHash + parameters: + - description: 'TODO: Description' + in: path + name: ns + required: true + schema: + example: default + type: string + - description: 'TODO: Description' + in: path + name: groupid + required: true + schema: + type: string + - description: Server-side request timeout (millseconds, or set a custom suffix + like 10s) + in: header + name: Request-Timeout + schema: + default: 120s + type: string + responses: + "200": + content: + application/json: + schema: + properties: + created: {} + hash: {} + ledger: {} + members: + items: + properties: + identity: + type: string + node: {} + type: object + type: array + message: {} + name: + type: string + namespace: + type: string + type: object + description: Success + default: + description: "" /namespaces/{ns}/messages: get: description: 'TODO: Description' diff --git a/internal/apiserver/route_get_group_by_id.go b/internal/apiserver/route_get_group_by_id.go new file mode 100644 index 0000000000..0f3986bcdf --- /dev/null +++ b/internal/apiserver/route_get_group_by_id.go @@ -0,0 +1,46 @@ +// Copyright © 2021 Kaleido, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package apiserver + +import ( + "net/http" + + "github.com/hyperledger/firefly/internal/config" + "github.com/hyperledger/firefly/internal/i18n" + "github.com/hyperledger/firefly/internal/oapispec" + "github.com/hyperledger/firefly/pkg/fftypes" +) + +var getGroupByHash = &oapispec.Route{ + Name: "getGroupByHash", + Path: "namespaces/{ns}/groups/{groupid}", + Method: http.MethodGet, + PathParams: []*oapispec.PathParam{ + {Name: "ns", ExampleFromConf: config.NamespacesDefault, Description: i18n.MsgTBD}, + {Name: "groupid", Description: i18n.MsgTBD}, + }, + QueryParams: nil, + FilterFactory: nil, + Description: i18n.MsgTBD, + JSONInputValue: nil, + JSONOutputValue: func() interface{} { return &fftypes.Group{} }, + JSONOutputCodes: []int{http.StatusOK}, + JSONHandler: func(r *oapispec.APIRequest) (output interface{}, err error) { + output, err = r.Or.PrivateMessaging().GetGroupByID(r.Ctx, r.PP["groupid"]) + return output, err + }, +} diff --git a/internal/apiserver/route_get_group_by_id_test.go b/internal/apiserver/route_get_group_by_id_test.go new file mode 100644 index 0000000000..30dbde4adf --- /dev/null +++ b/internal/apiserver/route_get_group_by_id_test.go @@ -0,0 +1,42 @@ +// Copyright © 2021 Kaleido, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package apiserver + +import ( + "net/http/httptest" + "testing" + + "github.com/hyperledger/firefly/mocks/privatemessagingmocks" + "github.com/hyperledger/firefly/pkg/fftypes" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func TestGetGroupByHash(t *testing.T) { + o, r := newTestAPIServer() + req := httptest.NewRequest("GET", "/api/v1/namespaces/mynamespace/groups/abcd12345", nil) + req.Header.Set("Content-Type", "application/json; charset=utf-8") + res := httptest.NewRecorder() + + mpm := &privatemessagingmocks.Manager{} + o.On("PrivateMessaging").Return(mpm) + mpm.On("GetGroupByID", mock.Anything, "abcd12345"). + Return(&fftypes.Group{}, nil) + r.ServeHTTP(res, req) + + assert.Equal(t, 200, res.Result().StatusCode) +} diff --git a/internal/apiserver/route_get_groups.go b/internal/apiserver/route_get_groups.go new file mode 100644 index 0000000000..bf07edfcd4 --- /dev/null +++ b/internal/apiserver/route_get_groups.go @@ -0,0 +1,45 @@ +// Copyright © 2021 Kaleido, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package apiserver + +import ( + "net/http" + + "github.com/hyperledger/firefly/internal/config" + "github.com/hyperledger/firefly/internal/i18n" + "github.com/hyperledger/firefly/internal/oapispec" + "github.com/hyperledger/firefly/pkg/database" + "github.com/hyperledger/firefly/pkg/fftypes" +) + +var getGroups = &oapispec.Route{ + Name: "getGroups", + Path: "namespaces/{ns}/groups", + Method: http.MethodGet, + PathParams: []*oapispec.PathParam{ + {Name: "ns", ExampleFromConf: config.NamespacesDefault, Description: i18n.MsgTBD}, + }, + QueryParams: nil, + FilterFactory: database.GroupQueryFactory, + Description: i18n.MsgTBD, + JSONInputValue: nil, + JSONOutputValue: func() interface{} { return []*fftypes.Group{} }, + JSONOutputCodes: []int{http.StatusOK}, + JSONHandler: func(r *oapispec.APIRequest) (output interface{}, err error) { + return filterResult(r.Or.PrivateMessaging().GetGroupsNS(r.Ctx, r.PP["ns"], r.Filter)) + }, +} diff --git a/internal/apiserver/route_get_groups_test.go b/internal/apiserver/route_get_groups_test.go new file mode 100644 index 0000000000..5797be68b3 --- /dev/null +++ b/internal/apiserver/route_get_groups_test.go @@ -0,0 +1,42 @@ +// Copyright © 2021 Kaleido, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package apiserver + +import ( + "net/http/httptest" + "testing" + + "github.com/hyperledger/firefly/mocks/privatemessagingmocks" + "github.com/hyperledger/firefly/pkg/fftypes" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func TestGetGroups(t *testing.T) { + o, r := newTestAPIServer() + req := httptest.NewRequest("GET", "/api/v1/namespaces/mynamespace/groups", nil) + req.Header.Set("Content-Type", "application/json; charset=utf-8") + res := httptest.NewRecorder() + + mpm := &privatemessagingmocks.Manager{} + o.On("PrivateMessaging").Return(mpm) + mpm.On("GetGroupsNS", mock.Anything, "mynamespace", mock.Anything). + Return([]*fftypes.Group{}, nil, nil) + r.ServeHTTP(res, req) + + assert.Equal(t, 200, res.Result().StatusCode) +} diff --git a/internal/apiserver/routes.go b/internal/apiserver/routes.go index c5f1deb68d..9d524e384b 100644 --- a/internal/apiserver/routes.go +++ b/internal/apiserver/routes.go @@ -56,6 +56,8 @@ var routes = []*oapispec.Route{ getDataMsgs, getEventByID, getEvents, + getGroups, + getGroupByHash, getMsgByID, getMsgData, getMsgEvents, diff --git a/internal/definitions/definition_handler.go b/internal/definitions/definition_handler.go index eaaac89505..d13f75e86e 100644 --- a/internal/definitions/definition_handler.go +++ b/internal/definitions/definition_handler.go @@ -74,8 +74,8 @@ func (dh *definitionHandlers) GetGroupByID(ctx context.Context, id string) (*fft return dh.messaging.GetGroupByID(ctx, id) } -func (dh *definitionHandlers) GetGroups(ctx context.Context, filter database.AndFilter) ([]*fftypes.Group, *database.FilterResult, error) { - return dh.messaging.GetGroups(ctx, filter) +func (dh *definitionHandlers) GetGroupsNS(ctx context.Context, ns string, filter database.AndFilter) ([]*fftypes.Group, *database.FilterResult, error) { + return dh.messaging.GetGroupsNS(ctx, ns, filter) } func (dh *definitionHandlers) ResolveInitGroup(ctx context.Context, msg *fftypes.Message) (*fftypes.Group, error) { diff --git a/internal/definitions/definition_handler_test.go b/internal/definitions/definition_handler_test.go index ae0c49ab1b..d9405b9a09 100644 --- a/internal/definitions/definition_handler_test.go +++ b/internal/definitions/definition_handler_test.go @@ -78,12 +78,12 @@ func TestPrivateMessagingPassthroughs(t *testing.T) { dh := newTestDefinitionHandlers(t) mpm := dh.messaging.(*privatemessagingmocks.Manager) mpm.On("GetGroupByID", ctx, mock.Anything).Return(nil, nil) - mpm.On("GetGroups", ctx, mock.Anything).Return(nil, nil, nil) + mpm.On("GetGroupsNS", ctx, "ns1", mock.Anything).Return(nil, nil, nil) mpm.On("ResolveInitGroup", ctx, mock.Anything).Return(nil, nil) mpm.On("EnsureLocalGroup", ctx, mock.Anything).Return(false, nil) _, _ = dh.GetGroupByID(ctx, fftypes.NewUUID().String()) - _, _, _ = dh.GetGroups(ctx, nil) + _, _, _ = dh.GetGroupsNS(ctx, "ns1", nil) _, _ = dh.ResolveInitGroup(ctx, nil) _, _ = dh.EnsureLocalGroup(ctx, nil) diff --git a/internal/identity/identitymanager.go b/internal/identity/identitymanager.go index 83d264300d..c47bc5e9aa 100644 --- a/internal/identity/identitymanager.go +++ b/internal/identity/identitymanager.go @@ -37,7 +37,7 @@ type Manager interface { ResolveSigningKey(ctx context.Context, inputKey string) (outputKey string, err error) ResolveSigningKeyIdentity(ctx context.Context, signingKey string) (author string, err error) ResolveLocalOrgDID(ctx context.Context) (localOrgDID string, err error) - GetOrgKey(ctx context.Context) string + GetLocalOrgKey(ctx context.Context) (string, error) OrgDID(org *fftypes.Organization) string GetLocalOrganization(ctx context.Context) (*fftypes.Organization, error) } @@ -47,6 +47,7 @@ type identityManager struct { plugin identity.Plugin blockchain blockchain.Plugin + localOrgSigningKey string localOrgDID string identityCacheTTL time.Duration identityCache *ccache.Cache @@ -124,7 +125,7 @@ func (im *identityManager) ResolveSigningKeyIdentity(ctx context.Context, signin } -func (im *identityManager) GetOrgKey(ctx context.Context) string { +func (im *identityManager) getConfigOrgKey() string { orgKey := config.GetString(config.OrgKey) if orgKey == "" { orgKey = config.GetString(config.OrgIdentityDeprecated) @@ -132,11 +133,23 @@ func (im *identityManager) GetOrgKey(ctx context.Context) string { return orgKey } +func (im *identityManager) GetLocalOrgKey(ctx context.Context) (string, error) { + if im.localOrgSigningKey != "" { + return im.localOrgSigningKey, nil + } + resolvedSigningKey, err := im.blockchain.ResolveSigningKey(ctx, im.getConfigOrgKey()) + if err != nil { + return "", err + } + im.localOrgSigningKey = resolvedSigningKey + return im.localOrgSigningKey, nil +} + func (im *identityManager) ResolveLocalOrgDID(ctx context.Context) (localOrgDID string, err error) { if im.localOrgDID != "" { return im.localOrgDID, nil } - orgKey := im.GetOrgKey(ctx) + orgKey := im.getConfigOrgKey() im.localOrgDID, err = im.ResolveSigningKeyIdentity(ctx, orgKey) if err != nil { diff --git a/internal/identity/identitymanager_test.go b/internal/identity/identitymanager_test.go index 201b2c9eaf..464851b1cb 100644 --- a/internal/identity/identitymanager_test.go +++ b/internal/identity/identitymanager_test.go @@ -366,6 +366,40 @@ func TestResolveSigningKeyIdentityOrgLookupUnresolved(t *testing.T) { mbi.AssertExpectations(t) } +func TestGetLocalOrgKey(t *testing.T) { + + ctx, im := newTestIdentityManager(t) + mbi := im.blockchain.(*blockchainmocks.Plugin) + mbi.On("ResolveSigningKey", ctx, "key1").Return("key1resolved", nil).Once() + + config.Set(config.OrgIdentityDeprecated, "key1") + + localOrgKey, err := im.GetLocalOrgKey(ctx) + assert.NoError(t, err) + assert.Equal(t, "key1resolved", localOrgKey) + + // Check cache + localOrgKey, err = im.GetLocalOrgKey(ctx) + assert.NoError(t, err) + assert.Equal(t, "key1resolved", localOrgKey) + + mbi.AssertExpectations(t) +} + +func TestGetLocalOrgKeyFail(t *testing.T) { + + ctx, im := newTestIdentityManager(t) + mbi := im.blockchain.(*blockchainmocks.Plugin) + mbi.On("ResolveSigningKey", ctx, "key1").Return("", fmt.Errorf("pop")).Once() + + config.Set(config.OrgIdentityDeprecated, "key1") + + _, err := im.GetLocalOrgKey(ctx) + assert.EqualError(t, err, "pop") + + mbi.AssertExpectations(t) +} + func TestResolveLocalOrgDIDSuccess(t *testing.T) { org := &fftypes.Organization{ diff --git a/internal/orchestrator/status.go b/internal/orchestrator/status.go index 0a2951e73c..a504caf4eb 100644 --- a/internal/orchestrator/status.go +++ b/internal/orchestrator/status.go @@ -43,7 +43,7 @@ func (or *orchestrator) GetNodeUUID(ctx context.Context) (node *fftypes.UUID) { func (or *orchestrator) GetStatus(ctx context.Context) (status *fftypes.NodeStatus, err error) { - orgKey := or.identity.GetOrgKey(ctx) + orgKey, _ := or.identity.GetLocalOrgKey(ctx) status = &fftypes.NodeStatus{ Node: fftypes.NodeStatusNode{ Name: config.GetString(config.NodeName), diff --git a/internal/orchestrator/status_test.go b/internal/orchestrator/status_test.go index 1331b732f7..97522ff2bc 100644 --- a/internal/orchestrator/status_test.go +++ b/internal/orchestrator/status_test.go @@ -51,7 +51,7 @@ func TestGetStatusRegistered(t *testing.T) { Owner: "0x1111111", }, nil) mim := or.identity.(*identitymanagermocks.Manager) - mim.On("GetOrgKey", mock.Anything).Return("0x1111111") + mim.On("GetLocalOrgKey", mock.Anything).Return("0x1111111", nil) status, err := or.GetStatus(or.ctx) assert.NoError(t, err) @@ -83,7 +83,7 @@ func TestGetStatusUnregistered(t *testing.T) { mdi := or.database.(*databasemocks.Plugin) mdi.On("GetOrganizationByName", or.ctx, "org1").Return(nil, nil) mim := or.identity.(*identitymanagermocks.Manager) - mim.On("GetOrgKey", mock.Anything).Return("0x1111111") + mim.On("GetLocalOrgKey", mock.Anything).Return("0x1111111", nil) status, err := or.GetStatus(or.ctx) assert.NoError(t, err) @@ -118,7 +118,7 @@ func TestGetStatusOrgOnlyRegistered(t *testing.T) { }, nil) mdi.On("GetNode", or.ctx, "0x1111111", "node1").Return(nil, nil) mim := or.identity.(*identitymanagermocks.Manager) - mim.On("GetOrgKey", mock.Anything).Return("0x1111111") + mim.On("GetLocalOrgKey", mock.Anything).Return("0x1111111", nil) status, err := or.GetStatus(or.ctx) assert.NoError(t, err) @@ -147,7 +147,7 @@ func TestGetStatuOrgError(t *testing.T) { mdi := or.database.(*databasemocks.Plugin) mdi.On("GetOrganizationByName", or.ctx, "org1").Return(nil, fmt.Errorf("pop")) mim := or.identity.(*identitymanagermocks.Manager) - mim.On("GetOrgKey", mock.Anything).Return("0x1111111") + mim.On("GetLocalOrgKey", mock.Anything).Return("0x1111111", nil) _, err := or.GetStatus(or.ctx) assert.EqualError(t, err, "pop") @@ -171,7 +171,7 @@ func TestGetStatusNodeError(t *testing.T) { }, nil) mdi.On("GetNode", or.ctx, "0x1111111", "node1").Return(nil, fmt.Errorf("pop")) mim := or.identity.(*identitymanagermocks.Manager) - mim.On("GetOrgKey", mock.Anything).Return("0x1111111") + mim.On("GetLocalOrgKey", mock.Anything).Return("0x1111111", nil) _, err := or.GetStatus(or.ctx) assert.EqualError(t, err, "pop") diff --git a/internal/privatemessaging/groupmanager.go b/internal/privatemessaging/groupmanager.go index 9e1edd500a..987074eac3 100644 --- a/internal/privatemessaging/groupmanager.go +++ b/internal/privatemessaging/groupmanager.go @@ -31,7 +31,7 @@ import ( type GroupManager interface { GetGroupByID(ctx context.Context, id string) (*fftypes.Group, error) - GetGroups(ctx context.Context, filter database.AndFilter) ([]*fftypes.Group, *database.FilterResult, error) + GetGroupsNS(ctx context.Context, ns string, filter database.AndFilter) ([]*fftypes.Group, *database.FilterResult, error) ResolveInitGroup(ctx context.Context, msg *fftypes.Message) (*fftypes.Group, error) EnsureLocalGroup(ctx context.Context, group *fftypes.Group) (ok bool, err error) } @@ -144,6 +144,10 @@ func (gm *groupManager) GetGroupByID(ctx context.Context, hash string) (*fftypes return gm.database.GetGroupByHash(ctx, h) } +func (gm *groupManager) GetGroupsNS(ctx context.Context, ns string, filter database.AndFilter) ([]*fftypes.Group, *database.FilterResult, error) { + return gm.GetGroups(ctx, filter.Condition(filter.Builder().Eq("namespace", ns))) +} + func (gm *groupManager) GetGroups(ctx context.Context, filter database.AndFilter) ([]*fftypes.Group, *database.FilterResult, error) { return gm.database.GetGroups(ctx, filter) } diff --git a/internal/privatemessaging/groupmanager_test.go b/internal/privatemessaging/groupmanager_test.go index df1615b23d..29e2aa5cfd 100644 --- a/internal/privatemessaging/groupmanager_test.go +++ b/internal/privatemessaging/groupmanager_test.go @@ -374,6 +374,25 @@ func TestGetGroupsOk(t *testing.T) { assert.Empty(t, groups) } +func TestGetGroupsNSOk(t *testing.T) { + pm, cancel := newTestPrivateMessaging(t) + defer cancel() + + mdi := pm.database.(*databasemocks.Plugin) + mdi.On("GetGroups", pm.ctx, mock.MatchedBy(func(filter database.AndFilter) bool { + f, err := filter.Finalize() + assert.NoError(t, err) + assert.Contains(t, f.String(), "namespace") + assert.Contains(t, f.String(), "ns1") + return true + })).Return([]*fftypes.Group{}, nil, nil) + + fb := database.GroupQueryFactory.NewFilter(pm.ctx) + groups, _, err := pm.GetGroupsNS(pm.ctx, "ns1", fb.And(fb.Eq("description", "mygroup"))) + assert.NoError(t, err) + assert.Empty(t, groups) +} + func TestGetGroupNodesCache(t *testing.T) { pm, cancel := newTestPrivateMessaging(t) defer cancel() diff --git a/internal/privatemessaging/message_test.go b/internal/privatemessaging/message_test.go index 4e3d9ec654..d60ef5e308 100644 --- a/internal/privatemessaging/message_test.go +++ b/internal/privatemessaging/message_test.go @@ -100,7 +100,7 @@ func TestSendUnpinnedMessageE2EOk(t *testing.T) { defer cancel() mim := pm.identity.(*identitymanagermocks.Manager) - mim.On("ResolveLocalOrgDID", pm.ctx).Return("localorg", nil) + mim.On("GetLocalOrgKey", pm.ctx).Return("localorgkey", nil) mim.On("ResolveInputIdentity", pm.ctx, mock.Anything).Run(func(args mock.Arguments) { identity := args[1].(*fftypes.Identity) identity.Author = "localorg" @@ -124,13 +124,13 @@ func TestSendUnpinnedMessageE2EOk(t *testing.T) { Hash: groupID, GroupIdentity: fftypes.GroupIdentity{ Members: fftypes.Members{ - {Node: nodeID1, Identity: "localorg"}, + {Node: nodeID1, Identity: "localorgkey"}, {Node: nodeID2, Identity: "remoteorg"}, }, }, }, nil).Once() mdi.On("GetNodeByID", pm.ctx, nodeID1).Return(&fftypes.Node{ - ID: nodeID1, Name: "node1", Owner: "localorg", DX: fftypes.DXInfo{Peer: "peer1-local"}, + ID: nodeID1, Name: "node1", Owner: "localorgkey", DX: fftypes.DXInfo{Peer: "peer1-local"}, }, nil).Once() mdi.On("GetNodeByID", pm.ctx, nodeID2).Return(&fftypes.Node{ ID: nodeID2, Name: "node2", Owner: "org1", DX: fftypes.DXInfo{Peer: "peer2-remote"}, @@ -651,7 +651,7 @@ func TestSendUnpinnedMessageEventFail(t *testing.T) { mim := pm.identity.(*identitymanagermocks.Manager) mim.On("ResolveInputIdentity", pm.ctx, mock.Anything).Return(nil) - mim.On("ResolveLocalOrgDID", pm.ctx).Return("localorg", nil) + mim.On("GetLocalOrgKey", pm.ctx).Return("localorgkey", nil) dataID := fftypes.NewUUID() groupID := fftypes.NewRandB32() @@ -670,13 +670,13 @@ func TestSendUnpinnedMessageEventFail(t *testing.T) { Hash: groupID, GroupIdentity: fftypes.GroupIdentity{ Members: fftypes.Members{ - {Node: nodeID1, Identity: "localorg"}, + {Node: nodeID1, Identity: "localorgkey"}, {Node: nodeID2, Identity: "remoteorg"}, }, }, }, nil).Once() mdi.On("GetNodeByID", pm.ctx, nodeID1).Return(&fftypes.Node{ - ID: nodeID1, Name: "node1", Owner: "localorg", DX: fftypes.DXInfo{Peer: "peer1-local"}, + ID: nodeID1, Name: "node1", Owner: "localorgkey", DX: fftypes.DXInfo{Peer: "peer1-local"}, }, nil).Once() mdi.On("GetNodeByID", pm.ctx, nodeID2).Return(&fftypes.Node{ ID: nodeID2, Name: "node2", Owner: "org1", DX: fftypes.DXInfo{Peer: "peer2-remote"}, diff --git a/internal/privatemessaging/privatemessaging.go b/internal/privatemessaging/privatemessaging.go index 9196347f7f..de68b41576 100644 --- a/internal/privatemessaging/privatemessaging.go +++ b/internal/privatemessaging/privatemessaging.go @@ -177,7 +177,8 @@ func (pm *privateMessaging) transferBlobs(ctx context.Context, data []*fftypes.D func (pm *privateMessaging) sendData(ctx context.Context, mType string, mID *fftypes.UUID, group *fftypes.Bytes32, ns string, nodes []*fftypes.Node, payload fftypes.Byteable, txid *fftypes.UUID, data []*fftypes.Data) (err error) { l := log.L(ctx) - localOrgDID, err := pm.identity.ResolveLocalOrgDID(ctx) + // TODO: move to using DIDs consistently as the way to reference the node/organization (i.e. node.Owner becomes a DID) + localOrgSigingKey, err := pm.identity.GetLocalOrgKey(ctx) if err != nil { return err } @@ -185,7 +186,7 @@ func (pm *privateMessaging) sendData(ctx context.Context, mType string, mID *fft // Write it to the dataexchange for each member for i, node := range nodes { - if node.Owner == localOrgDID { + if node.Owner == localOrgSigingKey { l.Debugf("Skipping send of %s for local node %s:%s for group=%s node=%s (%d/%d)", mType, ns, mID, group, node.ID, i+1, len(nodes)) continue } diff --git a/internal/privatemessaging/privatemessaging_test.go b/internal/privatemessaging/privatemessaging_test.go index 20f8fc460d..53e6b96297 100644 --- a/internal/privatemessaging/privatemessaging_test.go +++ b/internal/privatemessaging/privatemessaging_test.go @@ -100,7 +100,7 @@ func TestDispatchBatchWithBlobs(t *testing.T) { assert.Equal(t, "org1", identity.Author) identity.Key = "0x12345" }).Return(nil) - mim.On("ResolveLocalOrgDID", pm.ctx).Return("localorg", nil) + mim.On("GetLocalOrgKey", pm.ctx).Return("localorg", nil) mdi.On("GetGroupByHash", pm.ctx, groupID).Return(&fftypes.Group{ Hash: fftypes.NewRandB32(), GroupIdentity: fftypes.GroupIdentity{ @@ -210,7 +210,7 @@ func TestSendAndSubmitBatchBadID(t *testing.T) { mdi.On("GetGroupByHash", pm.ctx, mock.Anything).Return(nil, fmt.Errorf("pop")) mim := pm.identity.(*identitymanagermocks.Manager) - mim.On("ResolveLocalOrgDID", pm.ctx).Return("localorg", nil) + mim.On("GetLocalOrgKey", pm.ctx).Return("localorgkey", nil) mim.On("ResolveInputIdentity", pm.ctx, mock.MatchedBy(func(identity *fftypes.Identity) bool { assert.Equal(t, "badauthor", identity.Author) return true @@ -235,7 +235,7 @@ func TestSendAndSubmitBatchUnregisteredNode(t *testing.T) { mdi.On("GetGroupByHash", pm.ctx, mock.Anything).Return(nil, fmt.Errorf("pop")) mim := pm.identity.(*identitymanagermocks.Manager) - mim.On("ResolveLocalOrgDID", pm.ctx).Return("", fmt.Errorf("pop")) + mim.On("GetLocalOrgKey", pm.ctx).Return("", fmt.Errorf("pop")) err := pm.sendAndSubmitBatch(pm.ctx, &fftypes.Batch{ Identity: fftypes.Identity{ @@ -250,7 +250,7 @@ func TestSendImmediateFail(t *testing.T) { defer cancel() mim := pm.identity.(*identitymanagermocks.Manager) - mim.On("ResolveLocalOrgDID", pm.ctx).Return("localorg", nil) + mim.On("GetLocalOrgKey", pm.ctx).Return("localorg", nil) mdx := pm.exchange.(*dataexchangemocks.Plugin) mdx.On("SendMessage", pm.ctx, mock.Anything, mock.Anything).Return("", fmt.Errorf("pop")) @@ -275,7 +275,7 @@ func TestSendSubmitInsertOperationFail(t *testing.T) { defer cancel() mim := pm.identity.(*identitymanagermocks.Manager) - mim.On("ResolveLocalOrgDID", pm.ctx).Return("localorg", nil) + mim.On("GetLocalOrgKey", pm.ctx).Return("localorgkey", nil) mdx := pm.exchange.(*dataexchangemocks.Plugin) mdx.On("SendMessage", pm.ctx, mock.Anything, mock.Anything).Return("tracking1", nil) @@ -308,7 +308,7 @@ func TestSendSubmitBlobTransferFail(t *testing.T) { defer cancel() mim := pm.identity.(*identitymanagermocks.Manager) - mim.On("ResolveLocalOrgDID", pm.ctx).Return("localorg", nil) + mim.On("GetLocalOrgKey", pm.ctx).Return("localorgkey", nil) mdi := pm.database.(*databasemocks.Plugin) mdi.On("GetBlobMatchingHash", pm.ctx, mock.Anything).Return(nil, fmt.Errorf("pop")) diff --git a/manifest.json b/manifest.json index 93d349af72..c47ed04063 100644 --- a/manifest.json +++ b/manifest.json @@ -1,22 +1,22 @@ { "ethconnect": { "image": "ghcr.io/hyperledger/firefly-ethconnect", - "tag": "v3.1.0", - "sha": "aadd6a5cf66bbbe4a5cf8761b5928363a05379363e5afbeecda3a150ec0a057b" + "tag": "v3.0.5-20211201-12", + "sha": "d9065fb51b2ab83e544f509e86fa1d278a8359fd24224b00f4720c0b246222aa" }, "fabconnect": { "image": "ghcr.io/hyperledger/firefly-fabconnect", - "tag": "v0.9.2", - "sha": "c8b3e56f4d104683599beafe923234b6fcb7e0512d6d26d33e6034ee3862428e" + "tag": "v0.9.2-20211220-3", + "sha": "d40a52f0c878f60ec3cc5142f96974b108f86b7c11c10f5d9bc6f5a1ec073158" }, "dataexchange-https": { "image": "ghcr.io/hyperledger/firefly-dataexchange-https", - "tag": "v0.9.3", - "sha": "3a2256c1160f79b173bcc04d191e742946461b0e7e17df2e52098fbf97de9992" + "tag": "v0.9.2-20211209-2", + "sha": "03af6160b621ecfc1578bc8b9ff2c84c3404a965d252347d22d266c3d119bbe0" }, "tokens-erc1155": { "image": "ghcr.io/hyperledger/firefly-tokens-erc1155", - "tag": "v0.10.1", - "sha": "5ac18e1d2e30e0902ca786509aa074049993deaed5af8a92a83bebf9f91a49ea" + "tag": "v0.10.1-20211217-12", + "sha": "522e2240a33c3965d324ce2c6f4df847db7298903d6472ee59cc9ed3e9f3ecd7" } } diff --git a/mocks/definitionsmocks/definition_handlers.go b/mocks/definitionsmocks/definition_handlers.go index 2e81092fae..f292a1bf1c 100644 --- a/mocks/definitionsmocks/definition_handlers.go +++ b/mocks/definitionsmocks/definition_handlers.go @@ -62,13 +62,13 @@ func (_m *DefinitionHandlers) GetGroupByID(ctx context.Context, id string) (*fft return r0, r1 } -// GetGroups provides a mock function with given fields: ctx, filter -func (_m *DefinitionHandlers) GetGroups(ctx context.Context, filter database.AndFilter) ([]*fftypes.Group, *database.FilterResult, error) { - ret := _m.Called(ctx, filter) +// GetGroupsNS provides a mock function with given fields: ctx, ns, filter +func (_m *DefinitionHandlers) GetGroupsNS(ctx context.Context, ns string, filter database.AndFilter) ([]*fftypes.Group, *database.FilterResult, error) { + ret := _m.Called(ctx, ns, filter) var r0 []*fftypes.Group - if rf, ok := ret.Get(0).(func(context.Context, database.AndFilter) []*fftypes.Group); ok { - r0 = rf(ctx, filter) + if rf, ok := ret.Get(0).(func(context.Context, string, database.AndFilter) []*fftypes.Group); ok { + r0 = rf(ctx, ns, filter) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]*fftypes.Group) @@ -76,8 +76,8 @@ func (_m *DefinitionHandlers) GetGroups(ctx context.Context, filter database.And } var r1 *database.FilterResult - if rf, ok := ret.Get(1).(func(context.Context, database.AndFilter) *database.FilterResult); ok { - r1 = rf(ctx, filter) + if rf, ok := ret.Get(1).(func(context.Context, string, database.AndFilter) *database.FilterResult); ok { + r1 = rf(ctx, ns, filter) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*database.FilterResult) @@ -85,8 +85,8 @@ func (_m *DefinitionHandlers) GetGroups(ctx context.Context, filter database.And } var r2 error - if rf, ok := ret.Get(2).(func(context.Context, database.AndFilter) error); ok { - r2 = rf(ctx, filter) + if rf, ok := ret.Get(2).(func(context.Context, string, database.AndFilter) error); ok { + r2 = rf(ctx, ns, filter) } else { r2 = ret.Error(2) } diff --git a/mocks/identitymanagermocks/manager.go b/mocks/identitymanagermocks/manager.go index 9ce7cdd070..74ee5b27d3 100644 --- a/mocks/identitymanagermocks/manager.go +++ b/mocks/identitymanagermocks/manager.go @@ -15,17 +15,15 @@ type Manager struct { mock.Mock } -// GetLocalOrganization provides a mock function with given fields: ctx -func (_m *Manager) GetLocalOrganization(ctx context.Context) (*fftypes.Organization, error) { +// GetLocalOrgKey provides a mock function with given fields: ctx +func (_m *Manager) GetLocalOrgKey(ctx context.Context) (string, error) { ret := _m.Called(ctx) - var r0 *fftypes.Organization - if rf, ok := ret.Get(0).(func(context.Context) *fftypes.Organization); ok { + var r0 string + if rf, ok := ret.Get(0).(func(context.Context) string); ok { r0 = rf(ctx) } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*fftypes.Organization) - } + r0 = ret.Get(0).(string) } var r1 error @@ -38,18 +36,27 @@ func (_m *Manager) GetLocalOrganization(ctx context.Context) (*fftypes.Organizat return r0, r1 } -// GetOrgKey provides a mock function with given fields: ctx -func (_m *Manager) GetOrgKey(ctx context.Context) string { +// GetLocalOrganization provides a mock function with given fields: ctx +func (_m *Manager) GetLocalOrganization(ctx context.Context) (*fftypes.Organization, error) { ret := _m.Called(ctx) - var r0 string - if rf, ok := ret.Get(0).(func(context.Context) string); ok { + var r0 *fftypes.Organization + if rf, ok := ret.Get(0).(func(context.Context) *fftypes.Organization); ok { r0 = rf(ctx) } else { - r0 = ret.Get(0).(string) + if ret.Get(0) != nil { + r0 = ret.Get(0).(*fftypes.Organization) + } } - return r0 + var r1 error + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 } // OrgDID provides a mock function with given fields: org diff --git a/mocks/privatemessagingmocks/manager.go b/mocks/privatemessagingmocks/manager.go index 4634da107f..be51e56a61 100644 --- a/mocks/privatemessagingmocks/manager.go +++ b/mocks/privatemessagingmocks/manager.go @@ -62,13 +62,13 @@ func (_m *Manager) GetGroupByID(ctx context.Context, id string) (*fftypes.Group, return r0, r1 } -// GetGroups provides a mock function with given fields: ctx, filter -func (_m *Manager) GetGroups(ctx context.Context, filter database.AndFilter) ([]*fftypes.Group, *database.FilterResult, error) { - ret := _m.Called(ctx, filter) +// GetGroupsNS provides a mock function with given fields: ctx, ns, filter +func (_m *Manager) GetGroupsNS(ctx context.Context, ns string, filter database.AndFilter) ([]*fftypes.Group, *database.FilterResult, error) { + ret := _m.Called(ctx, ns, filter) var r0 []*fftypes.Group - if rf, ok := ret.Get(0).(func(context.Context, database.AndFilter) []*fftypes.Group); ok { - r0 = rf(ctx, filter) + if rf, ok := ret.Get(0).(func(context.Context, string, database.AndFilter) []*fftypes.Group); ok { + r0 = rf(ctx, ns, filter) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]*fftypes.Group) @@ -76,8 +76,8 @@ func (_m *Manager) GetGroups(ctx context.Context, filter database.AndFilter) ([] } var r1 *database.FilterResult - if rf, ok := ret.Get(1).(func(context.Context, database.AndFilter) *database.FilterResult); ok { - r1 = rf(ctx, filter) + if rf, ok := ret.Get(1).(func(context.Context, string, database.AndFilter) *database.FilterResult); ok { + r1 = rf(ctx, ns, filter) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*database.FilterResult) @@ -85,8 +85,8 @@ func (_m *Manager) GetGroups(ctx context.Context, filter database.AndFilter) ([] } var r2 error - if rf, ok := ret.Get(2).(func(context.Context, database.AndFilter) error); ok { - r2 = rf(ctx, filter) + if rf, ok := ret.Get(2).(func(context.Context, string, database.AndFilter) error); ok { + r2 = rf(ctx, ns, filter) } else { r2 = ret.Error(2) }