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  }