github.com/kaleido-io/firefly@v0.0.0-20210622132723-8b4b6aacb971/internal/privatemessaging/recipients.go (about) 1 // Copyright © 2021 Kaleido, Inc. 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 17 package privatemessaging 18 19 import ( 20 "context" 21 "fmt" 22 23 "github.com/kaleido-io/firefly/internal/i18n" 24 "github.com/kaleido-io/firefly/internal/log" 25 "github.com/kaleido-io/firefly/pkg/database" 26 "github.com/kaleido-io/firefly/pkg/fftypes" 27 ) 28 29 func (pm *privateMessaging) resolveReceipientList(ctx context.Context, sender *fftypes.Identity, in *fftypes.MessageInput) error { 30 if in.Header.Group != nil { 31 log.L(ctx).Debugf("Group '%s' specified for message", in.Header.Group) 32 return nil // validity of existing group checked later 33 } 34 if in.Group == nil || len(in.Group.Members) == 0 { 35 return i18n.NewError(ctx, i18n.MsgGroupMustHaveMembers) 36 } 37 group, isNew, err := pm.findOrGenerateGroup(ctx, in) 38 if err != nil { 39 return err 40 } 41 log.L(ctx).Debugf("Resolved group '%s' for message. New=%t", group.Hash, isNew) 42 in.Message.Header.Group = group.Hash 43 44 // If the group is new, we need to do a group initialization, before we send the message itself 45 if isNew { 46 return pm.groupManager.groupInit(ctx, sender, group) 47 } 48 return err 49 } 50 51 func (pm *privateMessaging) resolveOrg(ctx context.Context, orgInput string) (org *fftypes.Organization, err error) { 52 orgID, err := fftypes.ParseUUID(ctx, orgInput) 53 if err == nil { 54 org, err = pm.database.GetOrganizationByID(ctx, orgID) 55 } else { 56 org, err = pm.database.GetOrganizationByName(ctx, orgInput) 57 if err == nil && org == nil { 58 org, err = pm.database.GetOrganizationByIdentity(ctx, orgInput) 59 } 60 } 61 if err != nil { 62 return nil, err 63 } 64 if org == nil { 65 return nil, i18n.NewError(ctx, i18n.MsgOrgNotFound, orgInput) 66 } 67 return org, nil 68 } 69 70 func (pm *privateMessaging) resolveNode(ctx context.Context, org *fftypes.Organization, nodeInput string) (node *fftypes.Node, err error) { 71 if nodeInput != "" { 72 var nodeID *fftypes.UUID 73 nodeID, err = fftypes.ParseUUID(ctx, nodeInput) 74 if err == nil { 75 node, err = pm.database.GetNodeByID(ctx, nodeID) 76 } else { 77 node, err = pm.database.GetNode(ctx, org.Identity, nodeInput) 78 } 79 } else { 80 // Find any node owned by this organization 81 var nodes []*fftypes.Node 82 originalOrgName := fmt.Sprintf("%s/%s", org.Name, org.Identity) 83 for org != nil && node == nil { 84 filter := database.NodeQueryFactory.NewFilterLimit(ctx, 1).Eq("owner", org.Identity) 85 nodes, err = pm.database.GetNodes(ctx, filter) 86 switch { 87 case err == nil && len(nodes) > 0: 88 // This org owns a node 89 node = nodes[0] 90 case err == nil && org.Parent != "": 91 // This org has a parent, maybe that org owns a node 92 org, err = pm.database.GetOrganizationByIdentity(ctx, org.Parent) 93 default: 94 return nil, i18n.NewError(ctx, i18n.MsgNodeNotFoundInOrg, originalOrgName) 95 } 96 } 97 } 98 if err != nil { 99 return nil, err 100 } 101 if node == nil { 102 return nil, i18n.NewError(ctx, i18n.MsgNodeNotFound, nodeInput) 103 } 104 return node, nil 105 } 106 107 func (pm *privateMessaging) getReceipients(ctx context.Context, in *fftypes.MessageInput) (gi *fftypes.GroupIdentity, err error) { 108 foundLocal := false 109 gi = &fftypes.GroupIdentity{ 110 Name: in.Group.Name, 111 Ledger: in.Group.Ledger, 112 Members: make(fftypes.Members, len(in.Group.Members)), 113 } 114 for i, rInput := range in.Group.Members { 115 // Resolve the org 116 org, err := pm.resolveOrg(ctx, rInput.Identity) 117 if err != nil { 118 return nil, err 119 } 120 // Resolve the node 121 node, err := pm.resolveNode(ctx, org, rInput.Node) 122 if err != nil { 123 return nil, err 124 } 125 foundLocal = foundLocal || (node.Owner == pm.localOrgIdentity && node.Name == pm.localNodeName) 126 gi.Members[i] = &fftypes.Member{ 127 Identity: org.Identity, 128 Node: node.ID, 129 } 130 } 131 if !foundLocal { 132 return nil, i18n.NewError(ctx, i18n.MsgOneMemberLocal) 133 } 134 return gi, nil 135 } 136 137 func (pm *privateMessaging) findOrGenerateGroup(ctx context.Context, in *fftypes.MessageInput) (group *fftypes.Group, isNew bool, err error) { 138 gi, err := pm.getReceipients(ctx, in) 139 if err != nil { 140 return nil, false, err 141 } 142 hash := gi.Hash() 143 filter := database.GroupQueryFactory.NewFilterLimit(ctx, 1).Eq("hash", hash) 144 groups, err := pm.database.GetGroups(ctx, filter) 145 if err != nil { 146 return nil, false, err 147 } 148 if len(groups) > 0 { 149 return groups[0], false, nil 150 } 151 152 // Generate a new group on the fly here. 153 // It will need to be sent to the group ahead of the message the user is trying to send. 154 group = &fftypes.Group{ 155 GroupIdentity: *gi, 156 Hash: hash, 157 Created: fftypes.Now(), 158 } 159 group.Seal() 160 return group, true, nil 161 }