1 Star 0 Fork 0

peter / fabric

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
policyparser.go 8.98 KB
一键复制 编辑 原始数据 按行查看 历史
/*
Copyright IBM Corp. 2017 All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package policydsl
import (
"fmt"
"reflect"
"regexp"
"strconv"
"strings"
"github.com/Knetic/govaluate"
"github.com/golang/protobuf/proto"
cb "github.com/hyperledger/fabric-protos-go/common"
mb "github.com/hyperledger/fabric-protos-go/msp"
)
// Gate values
const (
GateAnd = "And"
GateOr = "Or"
GateOutOf = "OutOf"
)
// Role values for principals
const (
RoleAdmin = "admin"
RoleMember = "member"
RoleClient = "client"
RolePeer = "peer"
RoleOrderer = "orderer"
)
var (
regex = regexp.MustCompile(
fmt.Sprintf("^([[:alnum:].-]+)([.])(%s|%s|%s|%s|%s)$",
RoleAdmin, RoleMember, RoleClient, RolePeer, RoleOrderer),
)
regexErr = regexp.MustCompile("^No parameter '([^']+)' found[.]$")
)
// a stub function - it returns the same string as it's passed.
// This will be evaluated by second/third passes to convert to a proto policy
func outof(args ...interface{}) (interface{}, error) {
toret := "outof("
if len(args) < 2 {
return nil, fmt.Errorf("expected at least two arguments to NOutOf. Given %d", len(args))
}
arg0 := args[0]
// govaluate treats all numbers as float64 only. But and/or may pass int/string. Allowing int/string for flexibility of caller
if n, ok := arg0.(float64); ok {
toret += strconv.Itoa(int(n))
} else if n, ok := arg0.(int); ok {
toret += strconv.Itoa(n)
} else if n, ok := arg0.(string); ok {
toret += n
} else {
return nil, fmt.Errorf("unexpected type %s", reflect.TypeOf(arg0))
}
for _, arg := range args[1:] {
toret += ", "
switch t := arg.(type) {
case string:
if regex.MatchString(t) {
toret += "'" + t + "'"
} else {
toret += t
}
default:
return nil, fmt.Errorf("unexpected type %s", reflect.TypeOf(arg))
}
}
return toret + ")", nil
}
func and(args ...interface{}) (interface{}, error) {
args = append([]interface{}{len(args)}, args...)
return outof(args...)
}
func or(args ...interface{}) (interface{}, error) {
args = append([]interface{}{1}, args...)
return outof(args...)
}
func firstPass(args ...interface{}) (interface{}, error) {
toret := "outof(ID"
for _, arg := range args {
toret += ", "
switch t := arg.(type) {
case string:
if regex.MatchString(t) {
toret += "'" + t + "'"
} else {
toret += t
}
case float32:
case float64:
toret += strconv.Itoa(int(t))
default:
return nil, fmt.Errorf("unexpected type %s", reflect.TypeOf(arg))
}
}
return toret + ")", nil
}
func secondPass(args ...interface{}) (interface{}, error) {
/* general sanity check, we expect at least 3 args */
if len(args) < 3 {
return nil, fmt.Errorf("at least 3 arguments expected, got %d", len(args))
}
/* get the first argument, we expect it to be the context */
var ctx *context
switch v := args[0].(type) {
case *context:
ctx = v
default:
return nil, fmt.Errorf("unrecognized type, expected the context, got %s", reflect.TypeOf(args[0]))
}
/* get the second argument, we expect an integer telling us
how many of the remaining we expect to have*/
var t int
switch arg := args[1].(type) {
case float64:
t = int(arg)
default:
return nil, fmt.Errorf("unrecognized type, expected a number, got %s", reflect.TypeOf(args[1]))
}
/* get the n in the t out of n */
var n int = len(args) - 2
/* sanity check - t should be positive, permit equal to n+1, but disallow over n+1 */
if t < 0 || t > n+1 {
return nil, fmt.Errorf("invalid t-out-of-n predicate, t %d, n %d", t, n)
}
policies := make([]*cb.SignaturePolicy, 0)
/* handle the rest of the arguments */
for _, principal := range args[2:] {
switch t := principal.(type) {
/* if it's a string, we expect it to be formed as
<MSP_ID> . <ROLE>, where MSP_ID is the MSP identifier
and ROLE is either a member, an admin, a client, a peer or an orderer*/
case string:
/* split the string */
subm := regex.FindAllStringSubmatch(t, -1)
if subm == nil || len(subm) != 1 || len(subm[0]) != 4 {
return nil, fmt.Errorf("error parsing principal %s", t)
}
/* get the right role */
var r mb.MSPRole_MSPRoleType
switch subm[0][3] {
case RoleMember:
r = mb.MSPRole_MEMBER
case RoleAdmin:
r = mb.MSPRole_ADMIN
case RoleClient:
r = mb.MSPRole_CLIENT
case RolePeer:
r = mb.MSPRole_PEER
case RoleOrderer:
r = mb.MSPRole_ORDERER
default:
return nil, fmt.Errorf("error parsing role %s", t)
}
/* build the principal we've been told */
mspRole, err := proto.Marshal(&mb.MSPRole{MspIdentifier: subm[0][1], Role: r})
if err != nil {
return nil, fmt.Errorf("error marshalling msp role: %s", err)
}
p := &mb.MSPPrincipal{
PrincipalClassification: mb.MSPPrincipal_ROLE,
Principal: mspRole,
}
ctx.principals = append(ctx.principals, p)
/* create a SignaturePolicy that requires a signature from
the principal we've just built*/
dapolicy := SignedBy(int32(ctx.IDNum))
policies = append(policies, dapolicy)
/* increment the identity counter. Note that this is
suboptimal as we are not reusing identities. We
can deduplicate them easily and make this puppy
smaller. For now it's fine though */
// TODO: deduplicate principals
ctx.IDNum++
/* if we've already got a policy we're good, just append it */
case *cb.SignaturePolicy:
policies = append(policies, t)
default:
return nil, fmt.Errorf("unrecognized type, expected a principal or a policy, got %s", reflect.TypeOf(principal))
}
}
return NOutOf(int32(t), policies), nil
}
type context struct {
IDNum int
principals []*mb.MSPPrincipal
}
func newContext() *context {
return &context{IDNum: 0, principals: make([]*mb.MSPPrincipal, 0)}
}
// FromString takes a string representation of the policy,
// parses it and returns a SignaturePolicyEnvelope that
// implements that policy. The supported language is as follows:
//
// GATE(P[, P])
//
// where:
// - GATE is either "and" or "or"
// - P is either a principal or another nested call to GATE
//
// A principal is defined as:
//
// ORG.ROLE
//
// where:
// - ORG is a string (representing the MSP identifier)
// - ROLE takes the value of any of the RoleXXX constants representing
// the required role
func FromString(policy string) (*cb.SignaturePolicyEnvelope, error) {
// first we translate the and/or business into outof gates
intermediate, err := govaluate.NewEvaluableExpressionWithFunctions(
policy, map[string]govaluate.ExpressionFunction{
GateAnd: and,
strings.ToLower(GateAnd): and,
strings.ToUpper(GateAnd): and,
GateOr: or,
strings.ToLower(GateOr): or,
strings.ToUpper(GateOr): or,
GateOutOf: outof,
strings.ToLower(GateOutOf): outof,
strings.ToUpper(GateOutOf): outof,
},
)
if err != nil {
return nil, err
}
intermediateRes, err := intermediate.Evaluate(map[string]interface{}{})
if err != nil {
// attempt to produce a meaningful error
if regexErr.MatchString(err.Error()) {
sm := regexErr.FindStringSubmatch(err.Error())
if len(sm) == 2 {
return nil, fmt.Errorf("unrecognized token '%s' in policy string", sm[1])
}
}
return nil, err
}
resStr, ok := intermediateRes.(string)
if !ok {
return nil, fmt.Errorf("invalid policy string '%s'", policy)
}
// we still need two passes. The first pass just adds an extra
// argument ID to each of the outof calls. This is
// required because govaluate has no means of giving context
// to user-implemented functions other than via arguments.
// We need this argument because we need a global place where
// we put the identities that the policy requires
exp, err := govaluate.NewEvaluableExpressionWithFunctions(
resStr,
map[string]govaluate.ExpressionFunction{"outof": firstPass},
)
if err != nil {
return nil, err
}
res, err := exp.Evaluate(map[string]interface{}{})
if err != nil {
// attempt to produce a meaningful error
if regexErr.MatchString(err.Error()) {
sm := regexErr.FindStringSubmatch(err.Error())
if len(sm) == 2 {
return nil, fmt.Errorf("unrecognized token '%s' in policy string", sm[1])
}
}
return nil, err
}
resStr, ok = res.(string)
if !ok {
return nil, fmt.Errorf("invalid policy string '%s'", policy)
}
ctx := newContext()
parameters := make(map[string]interface{}, 1)
parameters["ID"] = ctx
exp, err = govaluate.NewEvaluableExpressionWithFunctions(
resStr,
map[string]govaluate.ExpressionFunction{"outof": secondPass},
)
if err != nil {
return nil, err
}
res, err = exp.Evaluate(parameters)
if err != nil {
// attempt to produce a meaningful error
if regexErr.MatchString(err.Error()) {
sm := regexErr.FindStringSubmatch(err.Error())
if len(sm) == 2 {
return nil, fmt.Errorf("unrecognized token '%s' in policy string", sm[1])
}
}
return nil, err
}
rule, ok := res.(*cb.SignaturePolicy)
if !ok {
return nil, fmt.Errorf("invalid policy string '%s'", policy)
}
p := &cb.SignaturePolicyEnvelope{
Identities: ctx.principals,
Version: 0,
Rule: rule,
}
return p, nil
}
1
https://gitee.com/peter_code_git/fabric.git
git@gitee.com:peter_code_git/fabric.git
peter_code_git
fabric
fabric
v2.1.1

搜索帮助

53164aa7 5694891 3bd8fe86 5694891