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