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  }