github.com/cactusblossom/fabric-ca@v0.0.0-20200611062428-0082fc643826/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  }