gitee.com/hyperledger/fabric-ca@v2.0.0-alpha+incompatible/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, errors.WithMessagef(err, "Failed to add identity '%s'", req.ID)
   344  	}
   345  
   346  	user, err := ctx.ca.registry.GetUser(req.ID, nil)
   347  	if err != nil {
   348  		return nil, err
   349  	}
   350  
   351  	resp, err := getIDResp(user, pass, caname)
   352  	if err != nil {
   353  		return nil, err
   354  	}
   355  
   356  	log.Debugf("Identity successfully added")
   357  	return resp, nil
   358  }
   359  
   360  func processPutRequest(ctx *serverRequestContextImpl, caname string) (*api.IdentityResponse, error) {
   361  	log.Debug("Processing PUT request")
   362  
   363  	modifyID, err := ctx.GetVar("id")
   364  	if err != nil {
   365  		return nil, err
   366  	}
   367  
   368  	if modifyID == "" {
   369  		return nil, caerrors.NewHTTPErr(400, caerrors.ErrModifyingIdentity, "No ID name specified in modify request")
   370  	}
   371  
   372  	log.Debugf("Modifying identity '%s'", modifyID)
   373  	userToModify, err := ctx.GetUser(modifyID)
   374  	if err != nil {
   375  		return nil, err
   376  	}
   377  
   378  	registry := ctx.ca.registry
   379  
   380  	var req api.ModifyIdentityRequest
   381  	err = ctx.ReadBody(&req)
   382  	if err != nil {
   383  		return nil, err
   384  	}
   385  
   386  	var checkAff, checkType, checkAttrs bool
   387  	modReq, setPass := getModifyReq(userToModify, &req)
   388  	log.Debugf("Modify Request: %+v", util.StructToString(modReq))
   389  
   390  	if req.Affiliation != "" {
   391  		newAff := req.Affiliation
   392  		if newAff != "." { // Only need to check if not requesting root affiliation
   393  			aff, _ := registry.GetAffiliation(newAff)
   394  			if aff == nil {
   395  				return nil, caerrors.NewHTTPErr(400, caerrors.ErrModifyingIdentity, "Affiliation '%s' is not supported", newAff)
   396  			}
   397  		}
   398  		checkAff = true
   399  	}
   400  
   401  	if req.Type != "" {
   402  		checkType = true
   403  	}
   404  
   405  	if len(req.Attributes) != 0 {
   406  		checkAttrs = true
   407  	}
   408  
   409  	err = ctx.CanModifyUser(&req, checkAff, checkType, checkAttrs, userToModify)
   410  	if err != nil {
   411  		return nil, err
   412  	}
   413  
   414  	err = registry.UpdateUser(modReq, setPass)
   415  	if err != nil {
   416  		return nil, err
   417  	}
   418  
   419  	userToModify, err = registry.GetUser(modifyID, nil)
   420  	if err != nil {
   421  		return nil, err
   422  	}
   423  
   424  	resp, err := getIDResp(userToModify, req.Secret, caname)
   425  	if err != nil {
   426  		return nil, err
   427  	}
   428  
   429  	log.Debugf("Identity successfully modified")
   430  	return resp, nil
   431  }
   432  
   433  // Function takes the modification request and fills in missing information with the current user information
   434  // and parses the modification request to generate the correct input to be stored in the database
   435  func getModifyReq(caUser user.User, req *api.ModifyIdentityRequest) (*user.Info, bool) {
   436  	modifyUserInfo := caUser.(*user.Impl).Info
   437  	userPass := caUser.(*user.Impl).GetPass()
   438  	setPass := false
   439  
   440  	if req.Secret != "" {
   441  		setPass = true
   442  		modifyUserInfo.Pass = req.Secret
   443  		modifyUserInfo.IncorrectPasswordAttempts = 0
   444  	} else {
   445  		modifyUserInfo.Pass = string(userPass)
   446  
   447  	}
   448  
   449  	if req.MaxEnrollments == -2 {
   450  		modifyUserInfo.MaxEnrollments = 0
   451  	} else if req.MaxEnrollments != 0 {
   452  		modifyUserInfo.MaxEnrollments = req.MaxEnrollments
   453  	}
   454  
   455  	reqAttrs := req.Attributes
   456  	if req.Affiliation == "." {
   457  		modifyUserInfo.Affiliation = ""
   458  		addAttributeToRequest(attr.Affiliation, "", &reqAttrs)
   459  	} else if req.Affiliation != "" {
   460  		modifyUserInfo.Affiliation = req.Affiliation
   461  		addAttributeToRequest(attr.Affiliation, req.Affiliation, &reqAttrs)
   462  	}
   463  
   464  	if req.Type != "" {
   465  		modifyUserInfo.Type = req.Type
   466  		addAttributeToRequest(attr.Type, req.Type, &reqAttrs)
   467  	}
   468  
   469  	// Update existing attribute, or add attribute if it does not already exist
   470  	if len(reqAttrs) != 0 {
   471  		modifyUserInfo.Attributes = user.GetNewAttributes(modifyUserInfo.Attributes, reqAttrs)
   472  	}
   473  	return &modifyUserInfo, setPass
   474  }
   475  
   476  // Get the identity response
   477  // Note that the secret will be the empty string unless the
   478  // caller is permitted to see the secret.  For example,
   479  // when adding a new identity and a secret is automatically
   480  // generated, it must be returned to the registrar.
   481  func getIDResp(caUser user.User, secret, caname string) (*api.IdentityResponse, error) {
   482  	allAttributes, err := caUser.GetAttributes(nil)
   483  	if err != nil {
   484  		return nil, err
   485  	}
   486  	return &api.IdentityResponse{
   487  		ID:             caUser.GetName(),
   488  		Type:           caUser.GetType(),
   489  		Affiliation:    user.GetAffiliation(caUser),
   490  		Attributes:     allAttributes,
   491  		MaxEnrollments: caUser.GetMaxEnrollments(),
   492  		Secret:         secret,
   493  		CAName:         caname,
   494  	}, nil
   495  }