github.com/hyperledger/fabric-ca@v2.0.0-alpha.0.20201120210307-7b4f34729db1+incompatible/lib/servercertificates.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  	"net/http"
    12  	"os"
    13  
    14  	"github.com/cloudflare/cfssl/log"
    15  	"github.com/hyperledger/fabric-ca/internal/pkg/util"
    16  	"github.com/hyperledger/fabric-ca/lib/caerrors"
    17  	"github.com/hyperledger/fabric-ca/lib/server/certificaterequest"
    18  	cadbuser "github.com/hyperledger/fabric-ca/lib/server/user"
    19  	"github.com/pkg/errors"
    20  )
    21  
    22  type certPEM struct {
    23  	PEM string `db:"pem"`
    24  }
    25  
    26  func newCertificateEndpoint(s *Server) *serverEndpoint {
    27  	return &serverEndpoint{
    28  		Path:      "certificates",
    29  		Methods:   []string{"GET", "DELETE"},
    30  		Handler:   certificatesHandler,
    31  		Server:    s,
    32  		successRC: 200,
    33  	}
    34  }
    35  
    36  func certificatesHandler(ctx *serverRequestContextImpl) (interface{}, error) {
    37  	var err error
    38  	// Process Request
    39  	err = processCertificateRequest(ctx)
    40  	if err != nil {
    41  		return nil, err
    42  	}
    43  	return nil, nil
    44  }
    45  
    46  // processCertificateRequest will process the certificate request
    47  func processCertificateRequest(ctx ServerRequestContext) error {
    48  	log.Debug("Processing certificate request")
    49  	var err error
    50  
    51  	// Authenticate
    52  	_, err = ctx.TokenAuthentication()
    53  	if err != nil {
    54  		return err
    55  	}
    56  
    57  	// Perform authority checks to make sure that caller has the correct
    58  	// set of attributes to manage certificates
    59  	err = authChecks(ctx)
    60  	if err != nil {
    61  		return err
    62  	}
    63  
    64  	method := ctx.GetReq().Method
    65  	switch method {
    66  	case "GET":
    67  		return processGetCertificateRequest(ctx)
    68  	case "DELETE":
    69  		return errors.New("DELETE Not Implemented")
    70  	default:
    71  		return errors.Errorf("Invalid request: %s", method)
    72  	}
    73  }
    74  
    75  // authChecks verifies that the caller has either attribute "hf.Registrar.Roles"
    76  // or "hf.Revoker" with a value of true
    77  func authChecks(ctx ServerRequestContext) error {
    78  	log.Debug("Performing attribute authorization checks for certificates endpoint")
    79  
    80  	caller, err := ctx.GetCaller()
    81  	if err != nil {
    82  		return err
    83  	}
    84  
    85  	_, err = caller.GetAttribute("hf.Registrar.Roles")
    86  	if err != nil {
    87  		err = ctx.HasRole("hf.Revoker")
    88  		if err != nil {
    89  			return caerrors.NewAuthorizationErr(caerrors.ErrAuthorizationFailure, "Caller does not posses either hf.Registrar.Roles or hf.Revoker attribute")
    90  		}
    91  	}
    92  
    93  	return nil
    94  }
    95  
    96  func processGetCertificateRequest(ctx ServerRequestContext) error {
    97  	log.Debug("Processing GET certificate request")
    98  	var err error
    99  
   100  	req, err := certificaterequest.NewCertificateRequest(ctx)
   101  	if err != nil {
   102  		return caerrors.NewHTTPErr(400, caerrors.ErrGettingCert, "Invalid Request: %s", err)
   103  	}
   104  
   105  	// Execute DB query and stream response
   106  	err = getCertificates(ctx, req)
   107  	if err != nil {
   108  		return err
   109  	}
   110  
   111  	return nil
   112  }
   113  
   114  // getCertificates executes the DB query and streams the results to client
   115  func getCertificates(ctx ServerRequestContext, req *certificaterequest.Impl) error {
   116  	w := ctx.GetResp()
   117  	flusher, _ := w.(http.Flusher)
   118  
   119  	caller, err := ctx.GetCaller()
   120  	if err != nil {
   121  		return err
   122  	}
   123  
   124  	// Execute DB query
   125  	rows, err := ctx.GetCertificates(req, cadbuser.GetAffiliation(caller))
   126  	if err != nil {
   127  		return err
   128  	}
   129  	defer rows.Close()
   130  
   131  	// Get the number of certificates to return back to client in a chunk based on the environment variable
   132  	// If environment variable not set, default to 100 certificates
   133  	numCerts, err := ctx.ChunksToDeliver(os.Getenv("FABRIC_CA_SERVER_MAX_CERTS_PER_CHUNK"))
   134  	if err != nil {
   135  		return err
   136  	}
   137  	log.Debugf("Number of certs to be delivered in each chunk: %d", numCerts)
   138  
   139  	w.Write([]byte(`{"certs":[`))
   140  
   141  	rowNumber := 0
   142  	for rows.Next() {
   143  		rowNumber++
   144  		var cert certPEM
   145  		err := rows.StructScan(&cert)
   146  		if err != nil {
   147  			return caerrors.NewHTTPErr(500, caerrors.ErrGettingCert, "Failed to get read row: %s", err)
   148  		}
   149  
   150  		if rowNumber > 1 {
   151  			w.Write([]byte(","))
   152  		}
   153  
   154  		resp, err := util.Marshal(cert, "certificate")
   155  		if err != nil {
   156  			return caerrors.NewHTTPErr(500, caerrors.ErrGettingCert, "Failed to marshal certificate: %s", err)
   157  		}
   158  		w.Write(resp)
   159  
   160  		// If hit the number of identities requested then flush
   161  		if rowNumber%numCerts == 0 {
   162  			flusher.Flush() // Trigger "chunked" encoding and send a chunk...
   163  		}
   164  	}
   165  
   166  	log.Debug("Number of certificates found: ", rowNumber)
   167  
   168  	// Close the JSON object
   169  	caname := ctx.GetQueryParm("ca")
   170  	w.Write([]byte(fmt.Sprintf("], \"caname\":\"%s\"}", caname)))
   171  	flusher.Flush()
   172  
   173  	return nil
   174  }