github.com/silveraid/fabric-ca@v1.1.0-preview.0.20180127000700-71974f53ab08/lib/serveraffiliations.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 lib 18 19 import ( 20 "fmt" 21 "strconv" 22 "strings" 23 24 "github.com/cloudflare/cfssl/log" 25 "github.com/hyperledger/fabric-ca/api" 26 "github.com/hyperledger/fabric-ca/lib/attr" 27 "github.com/hyperledger/fabric-ca/lib/spi" 28 "github.com/pkg/errors" 29 ) 30 31 func newAffiliationsEndpoint(s *Server) *serverEndpoint { 32 return &serverEndpoint{ 33 Methods: []string{"GET", "DELETE", "PUT"}, 34 Handler: affiliationsHandler, 35 Server: s, 36 successRC: 200, 37 } 38 } 39 40 func newAffiliationsStreamingEndpoint(s *Server) *serverEndpoint { 41 return &serverEndpoint{ 42 Methods: []string{"GET", "POST"}, 43 Handler: affiliationsStreamingHandler, 44 Server: s, 45 successRC: 200, 46 } 47 } 48 49 func affiliationsHandler(ctx *serverRequestContext) (interface{}, error) { 50 var err error 51 // Authenticate 52 callerID, err := ctx.TokenAuthentication() 53 log.Debugf("Received affiliation update request from %s", callerID) 54 if err != nil { 55 return nil, err 56 } 57 caname, err := ctx.getCAName() 58 if err != nil { 59 return nil, err 60 } 61 caller, err := ctx.GetCaller() 62 if err != nil { 63 return nil, err 64 } 65 err = ctx.HasRole(attr.AffiliationMgr) 66 if err != nil { 67 return nil, err 68 } 69 // Process Request 70 resp, err := processAffiliationRequest(ctx, caname, caller) 71 if err != nil { 72 return nil, err 73 } 74 75 return resp, nil 76 } 77 78 func affiliationsStreamingHandler(ctx *serverRequestContext) (interface{}, error) { 79 var err error 80 81 // Authenticate 82 callerID, err := ctx.TokenAuthentication() 83 log.Debugf("Received affiliation update request from %s", callerID) 84 if err != nil { 85 return nil, err 86 } 87 caname, err := ctx.getCAName() 88 if err != nil { 89 return nil, err 90 } 91 caller, err := ctx.GetCaller() 92 if err != nil { 93 return nil, err 94 } 95 err = ctx.HasRole(attr.AffiliationMgr) 96 if err != nil { 97 return nil, err 98 } 99 // Process Request 100 resp, err := processStreamingAffiliationRequest(ctx, caname, caller) 101 if err != nil { 102 return nil, err 103 } 104 105 return resp, nil 106 } 107 108 // processStreamingAffiliationRequest will process the configuration request 109 func processStreamingAffiliationRequest(ctx *serverRequestContext, caname string, caller spi.User) (interface{}, error) { 110 log.Debug("Processing affiliation configuration update request") 111 112 method := ctx.req.Method 113 switch method { 114 case "GET": 115 return processGetAllAffiliationsRequest(ctx, caller, caname) 116 case "POST": 117 return processAffiliationPostRequest(ctx, caname) 118 default: 119 return nil, errors.Errorf("Invalid request: %s", method) 120 } 121 } 122 123 // processRequest will process the configuration request 124 func processAffiliationRequest(ctx *serverRequestContext, caname string, caller spi.User) (interface{}, error) { 125 log.Debug("Processing affiliation configuration update request") 126 127 method := ctx.req.Method 128 switch method { 129 case "GET": 130 return processGetAffiliationRequest(ctx, caller, caname) 131 case "DELETE": 132 return processAffiliationDeleteRequest(ctx, caname) 133 case "PUT": 134 return processAffiliationPutRequest(ctx, caname) 135 default: 136 return nil, errors.Errorf("Invalid request: %s", method) 137 } 138 } 139 140 func processGetAllAffiliationsRequest(ctx *serverRequestContext, caller spi.User, caname string) (*api.AffiliationResponse, error) { 141 log.Debug("Processing GET all affiliations request") 142 143 resp, err := getAffiliations(ctx, caller, caname) 144 if err != nil { 145 return nil, err 146 } 147 148 return resp, nil 149 } 150 151 func processGetAffiliationRequest(ctx *serverRequestContext, caller spi.User, caname string) (*api.AffiliationResponse, error) { 152 log.Debug("Processing GET affiliation request") 153 154 affiliation, err := ctx.GetVar("affiliation") 155 if err != nil { 156 return nil, err 157 } 158 159 resp, err := getAffiliation(ctx, caller, affiliation, caname) 160 if err != nil { 161 return nil, err 162 } 163 164 return resp, nil 165 } 166 167 func getAffiliations(ctx *serverRequestContext, caller spi.User, caname string) (*api.AffiliationResponse, error) { 168 log.Debug("Requesting all affiliations that the caller is authorized view") 169 var err error 170 171 registry := ctx.ca.registry 172 callerAff := GetUserAffiliation(caller) 173 rows, err := registry.GetAllAffiliations(callerAff) 174 if err != nil { 175 return nil, newHTTPErr(500, ErrGettingUser, "Failed to get affiliation: %s", err) 176 } 177 178 an := &affiliationNode{} 179 for rows.Next() { 180 var aff AffiliationRecord 181 err := rows.StructScan(&aff) 182 if err != nil { 183 return nil, newHTTPErr(500, ErrGettingAffiliation, "Failed to get read row: %s", err) 184 } 185 186 an.insertByName(aff.Name) 187 } 188 root := an.GetRoot() 189 190 resp := &api.AffiliationResponse{ 191 CAName: caname, 192 } 193 resp.Name = root.Name 194 resp.Affiliations = root.Affiliations 195 resp.Identities = root.Identities 196 197 return resp, nil 198 } 199 200 func getAffiliation(ctx *serverRequestContext, caller spi.User, requestedAffiliation, caname string) (*api.AffiliationResponse, error) { 201 log.Debugf("Requesting affiliation '%s'", requestedAffiliation) 202 203 registry := ctx.ca.registry 204 err := ctx.ContainsAffiliation(requestedAffiliation) 205 if err != nil { 206 return nil, err 207 } 208 209 result, err := registry.GetAffiliationTree(requestedAffiliation) 210 if err != nil { 211 return nil, newHTTPErr(500, ErrGettingAffiliation, "Failed to get affiliation: %s", err) 212 } 213 214 resp, err := getResponse(result, caname) 215 if err != nil { 216 return nil, err 217 } 218 219 return resp, nil 220 } 221 222 func processAffiliationDeleteRequest(ctx *serverRequestContext, caname string) (*api.AffiliationResponse, error) { 223 log.Debug("Processing DELETE request") 224 225 if !ctx.ca.Config.Cfg.Affiliations.AllowRemove { 226 return nil, newAuthErr(ErrUpdateConfigRemoveAff, "Affiliation removal is disabled") 227 } 228 229 removeAffiliation, err := ctx.GetVar("affiliation") 230 if err != nil { 231 return nil, err 232 } 233 log.Debugf("Request to remove affiliation '%s'", removeAffiliation) 234 235 callerAff := GetUserAffiliation(ctx.caller) 236 if callerAff == removeAffiliation { 237 return nil, newAuthErr(ErrUpdateConfigRemoveAff, "Can't remove affiliation '%s' because the caller is associated with this affiliation", removeAffiliation) 238 } 239 240 err = ctx.ContainsAffiliation(removeAffiliation) 241 if err != nil { 242 return nil, err 243 } 244 245 force, err := ctx.GetBoolQueryParm("force") 246 if err != nil { 247 return nil, err 248 } 249 250 _, isRegistrar, err := ctx.isRegistrar() 251 if err != nil { 252 httpErr := getHTTPErr(err) 253 if httpErr.lcode != 20 { 254 return nil, err 255 } 256 } 257 258 identityRemoval := ctx.ca.Config.Cfg.Identities.AllowRemove 259 result, err := ctx.ca.registry.DeleteAffiliation(removeAffiliation, force, identityRemoval, isRegistrar) 260 if err != nil { 261 return nil, err 262 } 263 264 resp, err := getResponse(result, caname) 265 if err != nil { 266 return nil, err 267 } 268 269 return resp, nil 270 } 271 272 func processAffiliationPostRequest(ctx *serverRequestContext, caname string) (*api.AffiliationResponse, error) { 273 log.Debug("Processing POST request") 274 275 ctx.endpoint.successRC = 201 276 277 var req api.AddAffiliationRequestNet 278 err := ctx.ReadBody(&req) 279 if err != nil { 280 return nil, err 281 } 282 283 addAffiliation := req.Name 284 log.Debugf("Request to add affiliation '%s'", addAffiliation) 285 286 registry := ctx.ca.registry 287 _, err = registry.GetAffiliation(addAffiliation) 288 if err == nil { 289 return nil, newHTTPErr(400, ErrUpdateConfigAddAff, "Affiliation already exists") 290 } 291 292 err = ctx.ContainsAffiliation(addAffiliation) 293 if err != nil { 294 return nil, err 295 } 296 297 force, err := ctx.GetBoolQueryParm("force") 298 if err != nil { 299 return nil, err 300 } 301 302 addAffiliationSlice := strings.Split(addAffiliation, ".") 303 var parentAffiliationPath string 304 305 affLevel := ctx.ca.server.levels.Affiliation 306 if force { 307 // With force option, add any parent affiliations that don't exist 308 var affiliationPath string 309 for _, addAff := range addAffiliationSlice { 310 affiliationPath = affiliationPath + addAff 311 err := registry.InsertAffiliation(affiliationPath, parentAffiliationPath, affLevel) 312 if err != nil { 313 return nil, newHTTPErr(500, ErrUpdateConfigAddAff, "Failed to add affiliations '%s': %s", addAffiliation, err) 314 } 315 parentAffiliationPath = affiliationPath 316 affiliationPath = affiliationPath + "." 317 } 318 } else { 319 // If the affiliation being added has a parent affiliation, check to make sure that parent affiliation exists 320 if len(addAffiliationSlice) > 1 { 321 parentAffiliationPath = strings.Join(addAffiliationSlice[:len(addAffiliationSlice)-1], ".") // Get the path up until the last affiliation 322 _, err = registry.GetAffiliation(parentAffiliationPath) 323 if err != nil { 324 httpErr := getHTTPErr(err) 325 if httpErr.rcode == 400 { 326 return nil, newHTTPErr(400, ErrUpdateConfigAddAff, "Parent affiliation does not exist, 'force' option required on request to add affiliation") 327 } 328 return nil, err 329 } 330 err := registry.InsertAffiliation(addAffiliation, parentAffiliationPath, affLevel) 331 if err != nil { 332 return nil, newHTTPErr(500, ErrUpdateConfigAddAff, "Failed to add affiliation '%s': %s", addAffiliation, err) 333 } 334 } else { 335 err := registry.InsertAffiliation(addAffiliation, "", affLevel) 336 if err != nil { 337 return nil, newHTTPErr(500, ErrUpdateConfigAddAff, "Failed to add affiliation '%s': %s", addAffiliation, err) 338 } 339 } 340 341 } 342 343 resp := &api.AffiliationResponse{CAName: caname} 344 resp.Name = addAffiliation 345 346 return resp, nil 347 } 348 349 func processAffiliationPutRequest(ctx *serverRequestContext, caname string) (*api.AffiliationResponse, error) { 350 log.Debug("Processing PUT request") 351 352 modifyAffiliation, err := ctx.GetVar("affiliation") 353 if err != nil { 354 return nil, err 355 } 356 357 var req api.ModifyAffiliationRequestNet 358 err = ctx.ReadBody(&req) 359 if err != nil { 360 return nil, err 361 } 362 newAffiliation := req.NewName 363 log.Debugf("Request to modify affiliation '%s' to '%s'", modifyAffiliation, newAffiliation) 364 365 err = ctx.ContainsAffiliation(modifyAffiliation) 366 if err != nil { 367 return nil, err 368 } 369 370 err = ctx.ContainsAffiliation(newAffiliation) 371 if err != nil { 372 return nil, err 373 } 374 375 force := false 376 forceStr := ctx.req.URL.Query().Get("force") 377 if forceStr != "" { 378 force, err = strconv.ParseBool(forceStr) 379 if err != nil { 380 return nil, newHTTPErr(500, ErrUpdateConfigAddAff, "The 'force' query parameter value must be a boolean: %s", err) 381 } 382 383 } 384 385 _, isRegistrar, err := ctx.isRegistrar() 386 if err != nil { 387 httpErr := getHTTPErr(err) 388 if httpErr.lcode != 20 { 389 return nil, err 390 } 391 } 392 393 registry := ctx.ca.registry 394 result, err := registry.ModifyAffiliation(modifyAffiliation, newAffiliation, force, isRegistrar) 395 if err != nil { 396 return nil, errors.WithMessage(err, fmt.Sprintf("Failed to modify affiliation from '%s' to '%s'", modifyAffiliation, newAffiliation)) 397 } 398 399 resp, err := getResponse(result, caname) 400 if err != nil { 401 return nil, err 402 } 403 404 return resp, nil 405 } 406 407 func getResponse(result *spi.DbTxResult, caname string) (*api.AffiliationResponse, error) { 408 resp := &api.AffiliationResponse{CAName: caname} 409 // Get all root affiliation names from the result 410 rootNames := getRootAffiliationNames(result.Affiliations) 411 if len(rootNames) == 0 { 412 return resp, nil 413 } 414 if len(rootNames) != 1 { 415 return nil, errors.Errorf("multiple root affiliations found: %+v", rootNames) 416 } 417 affInfo := &api.AffiliationInfo{} 418 err := fillAffiliationInfo(affInfo, rootNames[0], result, result.Affiliations) 419 if err != nil { 420 return nil, err 421 } 422 resp.AffiliationInfo = *affInfo 423 return resp, nil 424 } 425 426 // Get all of the root affiliation names from this list of affiliations 427 func getRootAffiliationNames(affiliations []spi.Affiliation) []string { 428 roots := []string{} 429 for _, aff1 := range affiliations { 430 isRoot := true 431 for _, aff2 := range affiliations { 432 if isChildAffiliation(aff2.GetName(), aff1.GetName()) { 433 isRoot = false 434 break 435 } 436 } 437 if isRoot { 438 roots = append(roots, aff1.GetName()) 439 } 440 } 441 return roots 442 } 443 444 // Fill 'info' with affiliation info associated with affiliation 'name' hierarchically. 445 // Use 'affiliations' to find child affiliations for this affiliation, and 446 // 'identities' to find identities associated with this affiliation. 447 func fillAffiliationInfo(info *api.AffiliationInfo, name string, result *spi.DbTxResult, affiliations []spi.Affiliation) error { 448 info.Name = name 449 // Add identities which have this affiliation 450 identities := []api.IdentityInfo{} 451 for _, identity := range result.Identities { 452 idAff := strings.Join(identity.GetAffiliationPath(), ".") 453 if idAff == name { 454 id, err := getIDInfo(identity) 455 if err != nil { 456 return err 457 } 458 identities = append(identities, *id) 459 } 460 } 461 if len(identities) > 0 { 462 info.Identities = identities 463 } 464 // Create child affiliations (if any) 465 children := []api.AffiliationInfo{} 466 var child spi.Affiliation 467 for { 468 child = nil 469 // Search for a child affiliations 470 for idx, aff := range affiliations { 471 affName := aff.GetName() 472 if isChildAffiliation(name, affName) { 473 child = aff 474 // Remove this child affiliation 475 affiliations = append(affiliations[:idx], affiliations[idx+1:]...) 476 break 477 } 478 } 479 if child == nil { 480 // No more children of this affiliation 'name' found 481 break 482 } 483 // Found a child of affiliation 'name' 484 childAff := api.AffiliationInfo{Name: child.GetName()} 485 err := fillAffiliationInfo(&childAff, child.GetName(), result, affiliations) 486 if err != nil { 487 return err 488 } 489 children = append(children, childAff) 490 } 491 if len(children) > 0 { 492 info.Affiliations = children 493 } 494 return nil 495 } 496 497 // Determine if the affiliation with name 'child' is a child of affiliation with name 'name' 498 func isChildAffiliation(name, child string) bool { 499 if !strings.HasPrefix(child, name+".") { 500 return false 501 } 502 nameParts := strings.Split(name, ".") 503 childParts := strings.Split(child, ".") 504 if len(childParts) != len(nameParts)+1 { 505 return false 506 } 507 return true 508 } 509 510 func getIDInfo(user spi.User) (*api.IdentityInfo, error) { 511 allAttributes, err := user.GetAttributes(nil) 512 if err != nil { 513 return nil, err 514 } 515 return &api.IdentityInfo{ 516 ID: user.GetName(), 517 Type: user.GetType(), 518 Affiliation: GetUserAffiliation(user), 519 Attributes: allAttributes, 520 MaxEnrollments: user.GetMaxEnrollments(), 521 }, nil 522 } 523 524 type affiliationNode struct { 525 children map[string]*affiliationNode 526 } 527 528 func (an *affiliationNode) insertByName(name string) { 529 an.insertByPath(strings.Split(name, ".")) 530 } 531 532 func (an *affiliationNode) insertByPath(path []string) { 533 if len(path) == 0 { 534 return 535 } 536 if an.children == nil { 537 an.children = map[string]*affiliationNode{} 538 } 539 node := an.children[path[0]] 540 if node == nil { 541 node = &affiliationNode{} 542 an.children[path[0]] = node 543 } 544 node.insertByPath(path[1:]) 545 } 546 547 func (an *affiliationNode) GetRoot() *api.AffiliationInfo { 548 result := &api.AffiliationInfo{} 549 an.fill([]string{}, result) 550 switch len(result.Affiliations) { 551 case 0: 552 return nil 553 case 1: 554 return &result.Affiliations[0] 555 default: 556 return result 557 } 558 } 559 560 func (an *affiliationNode) fill(path []string, ai *api.AffiliationInfo) { 561 ai.Name = strings.Join(path, ".") 562 if len(an.children) > 0 { 563 ai.Affiliations = make([]api.AffiliationInfo, len(an.children)) 564 idx := 0 565 for key, child := range an.children { 566 child.fill(append(path, key), &ai.Affiliations[idx]) 567 idx++ 568 } 569 } 570 }