github.com/blockchain-gm/fabric-ca@v0.0.0-20200423072702-b2c40c7ac69c/lib/serveridentities.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package lib 8 9 import ( 10 "encoding/json" 11 "fmt" 12 "net/http" 13 "os" 14 "strconv" 15 16 "github.com/cloudflare/cfssl/log" 17 "github.com/hyperledger/fabric-ca/api" 18 "github.com/hyperledger/fabric-ca/lib/attr" 19 "github.com/hyperledger/fabric-ca/lib/caerrors" 20 "github.com/hyperledger/fabric-ca/lib/server/user" 21 "github.com/hyperledger/fabric-ca/util" 22 "github.com/pkg/errors" 23 ) 24 25 func newIdentitiesEndpoint(s *Server) *serverEndpoint { 26 return &serverEndpoint{ 27 Path: "identities/{id}", 28 Methods: []string{"GET", "DELETE", "PUT"}, 29 Handler: identitiesHandler, 30 Server: s, 31 successRC: 200, 32 } 33 } 34 35 func newIdentitiesStreamingEndpoint(s *Server) *serverEndpoint { 36 return &serverEndpoint{ 37 Path: "identities", 38 Methods: []string{"GET", "POST"}, 39 Handler: identitiesStreamingHandler, 40 Server: s, 41 successRC: 200, 42 } 43 } 44 45 func identitiesStreamingHandler(ctx *serverRequestContextImpl) (interface{}, error) { 46 // Authenticate 47 callerID, err := ctx.TokenAuthentication() 48 log.Debugf("Received identity update request from %s", callerID) 49 if err != nil { 50 return nil, err 51 } 52 caname, err := ctx.getCAName() 53 if err != nil { 54 return nil, err 55 } 56 caller, err := ctx.GetCaller() 57 if err != nil { 58 return nil, err 59 } 60 // Process Request 61 resp, err := processStreamingRequest(ctx, caname, caller) 62 if err != nil { 63 return nil, err 64 } 65 return resp, nil 66 } 67 68 func identitiesHandler(ctx *serverRequestContextImpl) (interface{}, error) { 69 var err error 70 // Authenticate 71 callerID, err := ctx.TokenAuthentication() 72 log.Debugf("Received identity update request from %s", callerID) 73 if err != nil { 74 return nil, err 75 } 76 caname, err := ctx.getCAName() 77 if err != nil { 78 return nil, err 79 } 80 caller, err := ctx.GetCaller() 81 if err != nil { 82 return nil, err 83 } 84 // Process Request 85 resp, err := processRequest(ctx, caname, caller) 86 if err != nil { 87 return nil, err 88 } 89 90 return resp, nil 91 } 92 93 // processStreamingRequest will process the configuration request 94 func processStreamingRequest(ctx *serverRequestContextImpl, caname string, caller user.User) (interface{}, error) { 95 log.Debug("Processing identity configuration update request") 96 97 method := ctx.req.Method 98 switch method { 99 case "GET": 100 return nil, processGetAllIDsRequest(ctx, caller, caname) 101 case "POST": 102 return processPostRequest(ctx, caname) 103 default: 104 return nil, errors.Errorf("Invalid request: %s", method) 105 } 106 } 107 108 // processRequest will process the configuration request 109 func processRequest(ctx *serverRequestContextImpl, caname string, caller user.User) (interface{}, error) { 110 log.Debug("Processing identity configuration update request") 111 112 method := ctx.req.Method 113 switch method { 114 case "GET": 115 return processGetIDRequest(ctx, caller, caname) 116 case "DELETE": 117 return processDeleteRequest(ctx, caname) 118 case "PUT": 119 return processPutRequest(ctx, caname) 120 default: 121 return nil, errors.Errorf("Invalid request: %s", method) 122 } 123 } 124 125 func processGetAllIDsRequest(ctx *serverRequestContextImpl, caller user.User, caname string) error { 126 log.Debug("Processing GET all IDs request") 127 128 err := getIDs(ctx, caller, caname) 129 if err != nil { 130 return err 131 } 132 return nil 133 } 134 135 func processGetIDRequest(ctx *serverRequestContextImpl, caller user.User, caname string) (interface{}, error) { 136 log.Debug("Processing GET ID request") 137 138 id, err := ctx.GetVar("id") 139 if err != nil { 140 return nil, err 141 } 142 143 resp, err := getID(ctx, caller, id, caname) 144 if err != nil { 145 return nil, err 146 } 147 148 return resp, nil 149 } 150 151 func getIDs(ctx *serverRequestContextImpl, caller user.User, caname string) error { 152 log.Debug("Requesting all identities that the caller is authorized view") 153 var err error 154 155 w := ctx.resp 156 flusher, _ := w.(http.Flusher) 157 158 callerTypes, isRegistrar, err := ctx.isRegistrar() 159 if err != nil { 160 return err 161 } 162 if !isRegistrar { 163 return caerrors.NewAuthorizationErr(caerrors.ErrGettingUser, "Caller is not a registrar") 164 } 165 166 // Getting all identities of appropriate affiliation and type 167 callerAff := user.GetAffiliation(caller) 168 registry := ctx.ca.registry 169 rows, err := registry.GetFilteredUsers(callerAff, callerTypes) 170 if err != nil { 171 return caerrors.NewHTTPErr(500, caerrors.ErrGettingUser, "Failed to get users by affiliation and type: %s", err) 172 } 173 174 // Get the number of identities to return back to client in a chunk based on the environment variable 175 // If environment variable not set, default to 100 identities 176 numberOfIdentities := os.Getenv("FABRIC_CA_SERVER_MAX_IDS_PER_CHUNK") 177 var numIdentities int 178 if numberOfIdentities == "" { 179 numIdentities = 100 180 } else { 181 numIdentities, err = strconv.Atoi(numberOfIdentities) 182 if err != nil { 183 return caerrors.NewHTTPErr(500, caerrors.ErrGettingUser, "Incorrect format specified for environment variable 'FABRIC_CA_SERVER_MAX_IDS_PER_CHUNK', an integer value is required: %s", err) 184 } 185 } 186 187 log.Debugf("Number of identities to be delivered in each chunk: %d", numIdentities) 188 189 w.Write([]byte(`{"identities":[`)) 190 191 rowNumber := 0 192 for rows.Next() { 193 rowNumber++ 194 var id user.Record 195 err := rows.StructScan(&id) 196 if err != nil { 197 return caerrors.NewHTTPErr(500, caerrors.ErrGettingUser, "Failed to get read row: %s", err) 198 } 199 200 if rowNumber > 1 { 201 w.Write([]byte(",")) 202 } 203 204 var attrs []api.Attribute 205 json.Unmarshal([]byte(id.Attributes), &attrs) 206 207 idInfo := api.IdentityInfo{ 208 ID: id.Name, 209 Type: id.Type, 210 Affiliation: id.Affiliation, 211 MaxEnrollments: id.MaxEnrollments, 212 Attributes: attrs, 213 } 214 215 resp, err := util.Marshal(idInfo, "identities info") 216 if err != nil { 217 return caerrors.NewHTTPErr(500, caerrors.ErrGettingUser, "Failed to marshal identity info: %s", err) 218 } 219 w.Write(resp) 220 221 // If hit the number of identities requested then flush 222 if rowNumber%numIdentities == 0 { 223 flusher.Flush() // Trigger "chunked" encoding and send a chunk... 224 } 225 } 226 227 // Close the JSON object 228 w.Write([]byte(fmt.Sprintf("], \"caname\":\"%s\"}", caname))) 229 flusher.Flush() 230 231 return nil 232 } 233 234 func getID(ctx *serverRequestContextImpl, caller user.User, id, caname string) (*api.GetIDResponse, error) { 235 log.Debugf("Requesting identity '%s'", id) 236 237 registry := ctx.ca.registry 238 caUser, err := registry.GetUser(id, nil) 239 if err != nil { 240 return nil, err 241 } 242 243 err = ctx.CanManageUser(caUser) 244 if err != nil { 245 return nil, err 246 } 247 248 allAttributes, err := caUser.GetAttributes(nil) 249 if err != nil { 250 return nil, err 251 } 252 253 resp := &api.GetIDResponse{ 254 ID: caUser.GetName(), 255 Type: caUser.GetType(), 256 Affiliation: user.GetAffiliation(caUser), 257 Attributes: allAttributes, 258 MaxEnrollments: caUser.GetMaxEnrollments(), 259 CAName: caname, 260 } 261 262 return resp, nil 263 } 264 265 func processDeleteRequest(ctx *serverRequestContextImpl, caname string) (*api.IdentityResponse, error) { 266 log.Debug("Processing DELETE request") 267 268 if !ctx.ca.Config.Cfg.Identities.AllowRemove { 269 return nil, caerrors.NewHTTPErr(403, caerrors.ErrRemoveIdentity, "Identity removal is disabled") 270 } 271 272 removeID, err := ctx.GetVar("id") 273 if err != nil { 274 return nil, err 275 } 276 277 if removeID == "" { 278 return nil, caerrors.NewHTTPErr(400, caerrors.ErrRemoveIdentity, "No ID name specified in remove request") 279 } 280 281 log.Debugf("Removing identity '%s'", removeID) 282 283 force, err := ctx.GetBoolQueryParm("force") 284 if err != nil { 285 return nil, err 286 } 287 288 if removeID == ctx.caller.GetName() && !force { 289 return nil, caerrors.NewHTTPErr(403, caerrors.ErrRemoveIdentity, "Need to use 'force' option to delete your own identity") 290 } 291 292 registry := ctx.ca.registry 293 userToRemove, err := ctx.GetUser(removeID) 294 if err != nil { 295 return nil, err 296 } 297 298 _, err = registry.DeleteUser(removeID) 299 if err != nil { 300 return nil, caerrors.NewHTTPErr(500, caerrors.ErrRemoveIdentity, "Failed to remove identity: %s", err) 301 } 302 303 resp, err := getIDResp(userToRemove, "", caname) 304 if err != nil { 305 return nil, err 306 } 307 308 log.Debugf("Identity '%s' successfully removed", removeID) 309 return resp, nil 310 } 311 312 func processPostRequest(ctx *serverRequestContextImpl, caname string) (*api.IdentityResponse, error) { 313 log.Debug("Processing POST request") 314 315 ctx.endpoint.successRC = 201 316 var req api.AddIdentityRequest 317 err := ctx.ReadBody(&req) 318 if err != nil { 319 return nil, err 320 } 321 322 if req.ID == "" { 323 return nil, caerrors.NewHTTPErr(400, caerrors.ErrAddIdentity, "Missing 'ID' in request to add a new identity") 324 } 325 addReq := &api.RegistrationRequest{ 326 Name: req.ID, 327 Secret: req.Secret, 328 Type: req.Type, 329 Affiliation: req.Affiliation, 330 Attributes: req.Attributes, 331 MaxEnrollments: req.MaxEnrollments, 332 } 333 log.Debugf("Adding identity: %+v", util.StructToString(addReq)) 334 335 caller, err := ctx.GetCaller() 336 if err != nil { 337 return nil, errors.WithMessage(err, "Failed to get caller identity") 338 } 339 callerID := caller.GetName() 340 341 pass, err := registerUser(addReq, callerID, ctx.ca, ctx) 342 if err != nil { 343 return nil, caerrors.NewHTTPErr(400, caerrors.ErrAddIdentity, "Failed to add identity: %s", err) 344 345 } 346 347 user, err := ctx.ca.registry.GetUser(req.ID, nil) 348 if err != nil { 349 return nil, err 350 } 351 352 resp, err := getIDResp(user, pass, caname) 353 if err != nil { 354 return nil, err 355 } 356 357 log.Debugf("Identity successfully added") 358 return resp, nil 359 } 360 361 func processPutRequest(ctx *serverRequestContextImpl, caname string) (*api.IdentityResponse, error) { 362 log.Debug("Processing PUT request") 363 364 modifyID, err := ctx.GetVar("id") 365 if err != nil { 366 return nil, err 367 } 368 369 if modifyID == "" { 370 return nil, caerrors.NewHTTPErr(400, caerrors.ErrModifyingIdentity, "No ID name specified in modify request") 371 } 372 373 log.Debugf("Modifying identity '%s'", modifyID) 374 userToModify, err := ctx.GetUser(modifyID) 375 if err != nil { 376 return nil, err 377 } 378 379 registry := ctx.ca.registry 380 381 var req api.ModifyIdentityRequest 382 err = ctx.ReadBody(&req) 383 if err != nil { 384 return nil, err 385 } 386 387 var checkAff, checkType, checkAttrs bool 388 modReq, setPass := getModifyReq(userToModify, &req) 389 log.Debugf("Modify Request: %+v", util.StructToString(modReq)) 390 391 if req.Affiliation != "" { 392 newAff := req.Affiliation 393 if newAff != "." { // Only need to check if not requesting root affiliation 394 aff, _ := registry.GetAffiliation(newAff) 395 if aff == nil { 396 return nil, caerrors.NewHTTPErr(400, caerrors.ErrModifyingIdentity, "Affiliation '%s' is not supported", newAff) 397 } 398 } 399 checkAff = true 400 } 401 402 if req.Type != "" { 403 checkType = true 404 } 405 406 if len(req.Attributes) != 0 { 407 checkAttrs = true 408 } 409 410 err = ctx.CanModifyUser(&req, checkAff, checkType, checkAttrs, userToModify) 411 if err != nil { 412 return nil, err 413 } 414 415 err = registry.UpdateUser(modReq, setPass) 416 if err != nil { 417 return nil, err 418 } 419 420 userToModify, err = registry.GetUser(modifyID, nil) 421 if err != nil { 422 return nil, err 423 } 424 425 resp, err := getIDResp(userToModify, req.Secret, caname) 426 if err != nil { 427 return nil, err 428 } 429 430 log.Debugf("Identity successfully modified") 431 return resp, nil 432 } 433 434 // Function takes the modification request and fills in missing information with the current user information 435 // and parses the modification request to generate the correct input to be stored in the database 436 func getModifyReq(caUser user.User, req *api.ModifyIdentityRequest) (*user.Info, bool) { 437 modifyUserInfo := caUser.(*user.Impl).Info 438 userPass := caUser.(*user.Impl).GetPass() 439 setPass := false 440 441 if req.Secret != "" { 442 setPass = true 443 modifyUserInfo.Pass = req.Secret 444 modifyUserInfo.IncorrectPasswordAttempts = 0 445 } else { 446 modifyUserInfo.Pass = string(userPass) 447 448 } 449 450 if req.MaxEnrollments == -2 { 451 modifyUserInfo.MaxEnrollments = 0 452 } else if req.MaxEnrollments != 0 { 453 modifyUserInfo.MaxEnrollments = req.MaxEnrollments 454 } 455 456 reqAttrs := req.Attributes 457 if req.Affiliation == "." { 458 modifyUserInfo.Affiliation = "" 459 addAttributeToRequest(attr.Affiliation, "", &reqAttrs) 460 } else if req.Affiliation != "" { 461 modifyUserInfo.Affiliation = req.Affiliation 462 addAttributeToRequest(attr.Affiliation, req.Affiliation, &reqAttrs) 463 } 464 465 if req.Type != "" { 466 modifyUserInfo.Type = req.Type 467 addAttributeToRequest(attr.Type, req.Type, &reqAttrs) 468 } 469 470 // Update existing attribute, or add attribute if it does not already exist 471 if len(reqAttrs) != 0 { 472 modifyUserInfo.Attributes = user.GetNewAttributes(modifyUserInfo.Attributes, reqAttrs) 473 } 474 return &modifyUserInfo, setPass 475 } 476 477 // Get the identity response 478 // Note that the secret will be the empty string unless the 479 // caller is permitted to see the secret. For example, 480 // when adding a new identity and a secret is automatically 481 // generated, it must be returned to the registrar. 482 func getIDResp(caUser user.User, secret, caname string) (*api.IdentityResponse, error) { 483 allAttributes, err := caUser.GetAttributes(nil) 484 if err != nil { 485 return nil, err 486 } 487 return &api.IdentityResponse{ 488 ID: caUser.GetName(), 489 Type: caUser.GetType(), 490 Affiliation: user.GetAffiliation(caUser), 491 Attributes: allAttributes, 492 MaxEnrollments: caUser.GetMaxEnrollments(), 493 Secret: secret, 494 CAName: caname, 495 }, nil 496 }