github.com/hellobchain/third_party@v0.0.0-20230331131523-deb0478a2e52/hyperledger/fabric/common/policydsl/policyparsergo.go (about)

     1  /*
     2  Copyright IBM Corp. 2017 All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  /*
     7  Notice: This file has been modified for Hyperledger Fabric SDK Go usage.
     8  Please review third_party pinning scripts and patches for more details.
     9  */
    10  
    11  package policydsl
    12  
    13  import (
    14  	"fmt"
    15  	"reflect"
    16  	"regexp"
    17  	"strconv"
    18  	"strings"
    19  
    20  	"github.com/Knetic/govaluate"
    21  	"github.com/golang/protobuf/proto"
    22  	cb "github.com/hyperledger/fabric-protos-go/common"
    23  	mb "github.com/hyperledger/fabric-protos-go/msp"
    24  )
    25  
    26  // Gate values
    27  const (
    28  	GateAnd   = "And"
    29  	GateOr    = "Or"
    30  	GateOutOf = "OutOf"
    31  )
    32  
    33  // Role values for principals
    34  const (
    35  	RoleAdmin   = "admin"
    36  	RoleMember  = "member"
    37  	RoleClient  = "client"
    38  	RolePeer    = "peer"
    39  	RoleOrderer = "orderer"
    40  )
    41  
    42  var (
    43  	regex = regexp.MustCompile(
    44  		fmt.Sprintf("^([[:alnum:].-]+)([.])(%s|%s|%s|%s|%s)$",
    45  			RoleAdmin, RoleMember, RoleClient, RolePeer, RoleOrderer),
    46  	)
    47  	regexErr = regexp.MustCompile("^No parameter '([^']+)' found[.]$")
    48  )
    49  
    50  // a stub function - it returns the same string as it's passed.
    51  // This will be evaluated by second/third passes to convert to a proto policy
    52  func outof(args ...interface{}) (interface{}, error) {
    53  	toret := "outof("
    54  
    55  	if len(args) < 2 {
    56  		return nil, fmt.Errorf("expected at least two arguments to NOutOf. Given %d", len(args))
    57  	}
    58  
    59  	arg0 := args[0]
    60  	// govaluate treats all numbers as float64 only. But and/or may pass int/string. Allowing int/string for flexibility of caller
    61  	if n, ok := arg0.(float64); ok {
    62  		toret += strconv.Itoa(int(n))
    63  	} else if n, ok := arg0.(int); ok {
    64  		toret += strconv.Itoa(n)
    65  	} else if n, ok := arg0.(string); ok {
    66  		toret += n
    67  	} else {
    68  		return nil, fmt.Errorf("unexpected type %s", reflect.TypeOf(arg0))
    69  	}
    70  
    71  	for _, arg := range args[1:] {
    72  		toret += ", "
    73  
    74  		switch t := arg.(type) {
    75  		case string:
    76  			if regex.MatchString(t) {
    77  				toret += "'" + t + "'"
    78  			} else {
    79  				toret += t
    80  			}
    81  		default:
    82  			return nil, fmt.Errorf("unexpected type %s", reflect.TypeOf(arg))
    83  		}
    84  	}
    85  
    86  	return toret + ")", nil
    87  }
    88  
    89  func and(args ...interface{}) (interface{}, error) {
    90  	args = append([]interface{}{len(args)}, args...)
    91  	return outof(args...)
    92  }
    93  
    94  func or(args ...interface{}) (interface{}, error) {
    95  	args = append([]interface{}{1}, args...)
    96  	return outof(args...)
    97  }
    98  
    99  func firstPass(args ...interface{}) (interface{}, error) {
   100  	toret := "outof(ID"
   101  	for _, arg := range args {
   102  		toret += ", "
   103  
   104  		switch t := arg.(type) {
   105  		case string:
   106  			if regex.MatchString(t) {
   107  				toret += "'" + t + "'"
   108  			} else {
   109  				toret += t
   110  			}
   111  		case float32:
   112  		case float64:
   113  			toret += strconv.Itoa(int(t))
   114  		default:
   115  			return nil, fmt.Errorf("unexpected type %s", reflect.TypeOf(arg))
   116  		}
   117  	}
   118  
   119  	return toret + ")", nil
   120  }
   121  
   122  func secondPass(args ...interface{}) (interface{}, error) {
   123  	/* general sanity check, we expect at least 3 args */
   124  	if len(args) < 3 {
   125  		return nil, fmt.Errorf("at least 3 arguments expected, got %d", len(args))
   126  	}
   127  
   128  	/* get the first argument, we expect it to be the context */
   129  	var ctx *context
   130  	switch v := args[0].(type) {
   131  	case *context:
   132  		ctx = v
   133  	default:
   134  		return nil, fmt.Errorf("unrecognized type, expected the context, got %s", reflect.TypeOf(args[0]))
   135  	}
   136  
   137  	/* get the second argument, we expect an integer telling us
   138  	   how many of the remaining we expect to have*/
   139  	var t int
   140  	switch arg := args[1].(type) {
   141  	case float64:
   142  		t = int(arg)
   143  	default:
   144  		return nil, fmt.Errorf("unrecognized type, expected a number, got %s", reflect.TypeOf(args[1]))
   145  	}
   146  
   147  	/* get the n in the t out of n */
   148  	var n int = len(args) - 2
   149  
   150  	/* sanity check - t should be positive, permit equal to n+1, but disallow over n+1 */
   151  	if t < 0 || t > n+1 {
   152  		return nil, fmt.Errorf("invalid t-out-of-n predicate, t %d, n %d", t, n)
   153  	}
   154  
   155  	policies := make([]*cb.SignaturePolicy, 0)
   156  
   157  	/* handle the rest of the arguments */
   158  	for _, principal := range args[2:] {
   159  		switch t := principal.(type) {
   160  		/* if it's a string, we expect it to be formed as
   161  		   <MSP_ID> . <ROLE>, where MSP_ID is the MSP identifier
   162  		   and ROLE is either a member, an admin, a client, a peer or an orderer*/
   163  		case string:
   164  			/* split the string */
   165  			subm := regex.FindAllStringSubmatch(t, -1)
   166  			if subm == nil || len(subm) != 1 || len(subm[0]) != 4 {
   167  				return nil, fmt.Errorf("error parsing principal %s", t)
   168  			}
   169  
   170  			/* get the right role */
   171  			var r mb.MSPRole_MSPRoleType
   172  
   173  			switch subm[0][3] {
   174  			case RoleMember:
   175  				r = mb.MSPRole_MEMBER
   176  			case RoleAdmin:
   177  				r = mb.MSPRole_ADMIN
   178  			case RoleClient:
   179  				r = mb.MSPRole_CLIENT
   180  			case RolePeer:
   181  				r = mb.MSPRole_PEER
   182  			case RoleOrderer:
   183  				r = mb.MSPRole_ORDERER
   184  			default:
   185  				return nil, fmt.Errorf("error parsing role %s", t)
   186  			}
   187  
   188  			/* build the principal we've been told */
   189  			mspRole, err := proto.Marshal(&mb.MSPRole{MspIdentifier: subm[0][1], Role: r})
   190  			if err != nil {
   191  				return nil, fmt.Errorf("error marshalling msp role: %s", err)
   192  			}
   193  
   194  			p := &mb.MSPPrincipal{
   195  				PrincipalClassification: mb.MSPPrincipal_ROLE,
   196  				Principal:               mspRole,
   197  			}
   198  			ctx.principals = append(ctx.principals, p)
   199  
   200  			/* create a SignaturePolicy that requires a signature from
   201  			   the principal we've just built*/
   202  			dapolicy := SignedBy(int32(ctx.IDNum))
   203  			policies = append(policies, dapolicy)
   204  
   205  			/* increment the identity counter. Note that this is
   206  			   suboptimal as we are not reusing identities. We
   207  			   can deduplicate them easily and make this puppy
   208  			   smaller. For now it's fine though */
   209  			// TODO: deduplicate principals
   210  			ctx.IDNum++
   211  
   212  		/* if we've already got a policy we're good, just append it */
   213  		case *cb.SignaturePolicy:
   214  			policies = append(policies, t)
   215  
   216  		default:
   217  			return nil, fmt.Errorf("unrecognized type, expected a principal or a policy, got %s", reflect.TypeOf(principal))
   218  		}
   219  	}
   220  
   221  	return NOutOf(int32(t), policies), nil
   222  }
   223  
   224  type context struct {
   225  	IDNum      int
   226  	principals []*mb.MSPPrincipal
   227  }
   228  
   229  func newContext() *context {
   230  	return &context{IDNum: 0, principals: make([]*mb.MSPPrincipal, 0)}
   231  }
   232  
   233  // FromString takes a string representation of the policy,
   234  // parses it and returns a SignaturePolicyEnvelope that
   235  // implements that policy. The supported language is as follows:
   236  //
   237  // GATE(P[, P])
   238  //
   239  // where:
   240  //	- GATE is either "and" or "or"
   241  //	- P is either a principal or another nested call to GATE
   242  //
   243  // A principal is defined as:
   244  //
   245  // ORG.ROLE
   246  //
   247  // where:
   248  //	- ORG is a string (representing the MSP identifier)
   249  //	- ROLE takes the value of any of the RoleXXX constants representing
   250  //    the required role
   251  func FromString(policy string) (*cb.SignaturePolicyEnvelope, error) {
   252  	// first we translate the and/or business into outof gates
   253  	intermediate, err := govaluate.NewEvaluableExpressionWithFunctions(
   254  		policy, map[string]govaluate.ExpressionFunction{
   255  			GateAnd:                    and,
   256  			strings.ToLower(GateAnd):   and,
   257  			strings.ToUpper(GateAnd):   and,
   258  			GateOr:                     or,
   259  			strings.ToLower(GateOr):    or,
   260  			strings.ToUpper(GateOr):    or,
   261  			GateOutOf:                  outof,
   262  			strings.ToLower(GateOutOf): outof,
   263  			strings.ToUpper(GateOutOf): outof,
   264  		},
   265  	)
   266  	if err != nil {
   267  		return nil, err
   268  	}
   269  
   270  	intermediateRes, err := intermediate.Evaluate(map[string]interface{}{})
   271  	if err != nil {
   272  		// attempt to produce a meaningful error
   273  		if regexErr.MatchString(err.Error()) {
   274  			sm := regexErr.FindStringSubmatch(err.Error())
   275  			if len(sm) == 2 {
   276  				return nil, fmt.Errorf("unrecognized token '%s' in policy string", sm[1])
   277  			}
   278  		}
   279  
   280  		return nil, err
   281  	}
   282  
   283  	resStr, ok := intermediateRes.(string)
   284  	if !ok {
   285  		return nil, fmt.Errorf("invalid policy string '%s'", policy)
   286  	}
   287  
   288  	// we still need two passes. The first pass just adds an extra
   289  	// argument ID to each of the outof calls. This is
   290  	// required because govaluate has no means of giving context
   291  	// to user-implemented functions other than via arguments.
   292  	// We need this argument because we need a global place where
   293  	// we put the identities that the policy requires
   294  	exp, err := govaluate.NewEvaluableExpressionWithFunctions(
   295  		resStr,
   296  		map[string]govaluate.ExpressionFunction{"outof": firstPass},
   297  	)
   298  	if err != nil {
   299  		return nil, err
   300  	}
   301  
   302  	res, err := exp.Evaluate(map[string]interface{}{})
   303  	if err != nil {
   304  		// attempt to produce a meaningful error
   305  		if regexErr.MatchString(err.Error()) {
   306  			sm := regexErr.FindStringSubmatch(err.Error())
   307  			if len(sm) == 2 {
   308  				return nil, fmt.Errorf("unrecognized token '%s' in policy string", sm[1])
   309  			}
   310  		}
   311  
   312  		return nil, err
   313  	}
   314  
   315  	resStr, ok = res.(string)
   316  	if !ok {
   317  		return nil, fmt.Errorf("invalid policy string '%s'", policy)
   318  	}
   319  
   320  	ctx := newContext()
   321  	parameters := make(map[string]interface{}, 1)
   322  	parameters["ID"] = ctx
   323  
   324  	exp, err = govaluate.NewEvaluableExpressionWithFunctions(
   325  		resStr,
   326  		map[string]govaluate.ExpressionFunction{"outof": secondPass},
   327  	)
   328  	if err != nil {
   329  		return nil, err
   330  	}
   331  
   332  	res, err = exp.Evaluate(parameters)
   333  	if err != nil {
   334  		// attempt to produce a meaningful error
   335  		if regexErr.MatchString(err.Error()) {
   336  			sm := regexErr.FindStringSubmatch(err.Error())
   337  			if len(sm) == 2 {
   338  				return nil, fmt.Errorf("unrecognized token '%s' in policy string", sm[1])
   339  			}
   340  		}
   341  
   342  		return nil, err
   343  	}
   344  
   345  	rule, ok := res.(*cb.SignaturePolicy)
   346  	if !ok {
   347  		return nil, fmt.Errorf("invalid policy string '%s'", policy)
   348  	}
   349  
   350  	p := &cb.SignaturePolicyEnvelope{
   351  		Identities: ctx.principals,
   352  		Version:    0,
   353  		Rule:       rule,
   354  	}
   355  
   356  	return p, nil
   357  }