github.com/leonlxy/hyperledger@v1.0.0-alpha.0.20170427033203-34922035d248/common/cauthdsl/policyparser.go (about) 1 /* 2 Copyright IBM Corp. 2017 All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package cauthdsl 18 19 import ( 20 "fmt" 21 "reflect" 22 "regexp" 23 "strconv" 24 25 "github.com/Knetic/govaluate" 26 "github.com/hyperledger/fabric/protos/common" 27 "github.com/hyperledger/fabric/protos/msp" 28 "github.com/hyperledger/fabric/protos/utils" 29 ) 30 31 var regex *regexp.Regexp = regexp.MustCompile("^([[:alnum:]]+)([.])(member|admin)$") 32 33 func and(args ...interface{}) (interface{}, error) { 34 toret := "outof(" + strconv.Itoa(len(args)) 35 for _, arg := range args { 36 toret += ", " 37 switch t := arg.(type) { 38 case string: 39 if regex.MatchString(t) { 40 toret += "'" + t + "'" 41 } else { 42 toret += t 43 } 44 default: 45 return nil, fmt.Errorf("Unexpected type %s", reflect.TypeOf(arg)) 46 } 47 } 48 49 return toret + ")", nil 50 } 51 52 func or(args ...interface{}) (interface{}, error) { 53 toret := "outof(" + strconv.Itoa(1) 54 for _, arg := range args { 55 toret += ", " 56 switch t := arg.(type) { 57 case string: 58 if regex.MatchString(t) { 59 toret += "'" + t + "'" 60 } else { 61 toret += t 62 } 63 default: 64 return nil, fmt.Errorf("Unexpected type %s", reflect.TypeOf(arg)) 65 } 66 } 67 68 return toret + ")", nil 69 } 70 71 func firstPass(args ...interface{}) (interface{}, error) { 72 toret := "outof(ID" 73 for _, arg := range args { 74 toret += ", " 75 switch t := arg.(type) { 76 case string: 77 if regex.MatchString(t) { 78 toret += "'" + t + "'" 79 } else { 80 toret += t 81 } 82 case float32: 83 case float64: 84 toret += strconv.Itoa(int(t)) 85 default: 86 return nil, fmt.Errorf("Unexpected type %s", reflect.TypeOf(arg)) 87 } 88 } 89 90 return toret + ")", nil 91 } 92 93 func secondPass(args ...interface{}) (interface{}, error) { 94 /* general sanity check, we expect at least 3 args */ 95 if len(args) < 3 { 96 return nil, fmt.Errorf("At least 3 arguments expected, got %d", len(args)) 97 } 98 99 /* get the first argument, we expect it to be the context */ 100 var ctx *context 101 switch v := args[0].(type) { 102 case *context: 103 ctx = v 104 default: 105 return nil, fmt.Errorf("Unrecognized type, expected the context, got %s", reflect.TypeOf(args[0])) 106 } 107 108 /* get the second argument, we expect an integer telling us 109 how many of the remaining we expect to have*/ 110 var t int 111 switch arg := args[1].(type) { 112 case float64: 113 t = int(arg) 114 default: 115 return nil, fmt.Errorf("Unrecognized type, expected a number, got %s", reflect.TypeOf(args[1])) 116 } 117 118 /* get the n in the t out of n */ 119 var n int = len(args) - 1 120 121 /* sanity check - t better be <= n */ 122 if t > n { 123 return nil, fmt.Errorf("Invalid t-out-of-n predicate, t %d, n %d", t, n) 124 } 125 126 policies := make([]*common.SignaturePolicy, 0) 127 128 /* handle the rest of the arguments */ 129 for _, principal := range args[2:] { 130 switch t := principal.(type) { 131 /* if it's a string, we expect it to be formed as 132 <MSP_ID> . <ROLE>, where MSP_ID is the MSP identifier 133 and ROLE is either a member of an admin*/ 134 case string: 135 /* split the string */ 136 subm := regex.FindAllStringSubmatch(t, -1) 137 if subm == nil || len(subm) != 1 || len(subm[0]) != 4 { 138 return nil, fmt.Errorf("Error parsing principal %s", t) 139 } 140 141 /* get the right role */ 142 var r msp.MSPRole_MSPRoleType 143 if subm[0][3] == "member" { 144 r = msp.MSPRole_MEMBER 145 } else { 146 r = msp.MSPRole_ADMIN 147 } 148 149 /* build the principal we've been told */ 150 p := &msp.MSPPrincipal{ 151 PrincipalClassification: msp.MSPPrincipal_ROLE, 152 Principal: utils.MarshalOrPanic(&msp.MSPRole{MspIdentifier: subm[0][1], Role: r})} 153 ctx.principals = append(ctx.principals, p) 154 155 /* create a SignaturePolicy that requires a signature from 156 the principal we've just built*/ 157 dapolicy := SignedBy(int32(ctx.IDNum)) 158 policies = append(policies, dapolicy) 159 160 /* increment the identity counter. Note that this is 161 suboptimal as we are not reusing identities. We 162 can deduplicate them easily and make this puppy 163 smaller. For now it's fine though */ 164 // TODO: deduplicate principals 165 ctx.IDNum++ 166 167 /* if we've already got a policy we're good, just append it */ 168 case *common.SignaturePolicy: 169 policies = append(policies, t) 170 171 default: 172 return nil, fmt.Errorf("Unrecognized type, expected a principal or a policy, got %s", reflect.TypeOf(principal)) 173 } 174 } 175 176 return NOutOf(int32(t), policies), nil 177 } 178 179 type context struct { 180 IDNum int 181 principals []*msp.MSPPrincipal 182 } 183 184 func newContext() *context { 185 return &context{IDNum: 0, principals: make([]*msp.MSPPrincipal, 0)} 186 } 187 188 // FromString takes a string representation of the policy, 189 // parses it and returns a SignaturePolicyEnvelope that 190 // implements that policy. The supported language is as follows 191 // 192 // GATE(P[, P]) 193 // 194 // where 195 // - GATE is either "and" or "or" 196 // - P is either a principal or another nested call to GATE 197 // 198 // a principal is defined as 199 // 200 // ORG.ROLE 201 // 202 // where 203 // - ORG is a string (representing the MSP identifier) 204 // - ROLE is either the string "member" or the string "admin" representing the required role 205 func FromString(policy string) (*common.SignaturePolicyEnvelope, error) { 206 // first we translate the and/or business into outof gates 207 intermediate, err := govaluate.NewEvaluableExpressionWithFunctions(policy, map[string]govaluate.ExpressionFunction{"AND": and, "and": and, "OR": or, "or": or}) 208 if err != nil { 209 return nil, err 210 } 211 212 intermediateRes, err := intermediate.Evaluate(nil) 213 if err != nil { 214 return nil, err 215 } 216 217 // we still need two passes. The first pass just adds an extra 218 // argument ID to each of the outof calls. This is 219 // required because govaluate has no means of giving context 220 // to user-implemented functions other than via arguments. 221 // We need this argument because we need a global place where 222 // we put the identities that the policy requires 223 exp, err := govaluate.NewEvaluableExpressionWithFunctions(intermediateRes.(string), map[string]govaluate.ExpressionFunction{"outof": firstPass}) 224 if err != nil { 225 return nil, err 226 } 227 228 res, err := exp.Evaluate(nil) 229 if err != nil { 230 return nil, err 231 } 232 233 ctx := newContext() 234 parameters := make(map[string]interface{}, 1) 235 parameters["ID"] = ctx 236 237 exp, err = govaluate.NewEvaluableExpressionWithFunctions(res.(string), map[string]govaluate.ExpressionFunction{"outof": secondPass}) 238 if err != nil { 239 return nil, err 240 } 241 242 res, err = exp.Evaluate(parameters) 243 if err != nil { 244 return nil, err 245 } 246 247 p := &common.SignaturePolicyEnvelope{ 248 Identities: ctx.principals, 249 Version: 0, 250 Policy: res.(*common.SignaturePolicy), 251 } 252 253 return p, nil 254 }