github.com/tw-bc-group/fabric-ca@v2.0.0-alpha+incompatible/lib/servercertificates_test.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  	"bytes"
    11  	"fmt"
    12  	"io/ioutil"
    13  	"net/http"
    14  	"net/http/httptest"
    15  	"os"
    16  	"path/filepath"
    17  	"strings"
    18  	"testing"
    19  
    20  	"github.com/cloudflare/cfssl/certdb"
    21  	"github.com/hyperledger/fabric-ca/api"
    22  	"github.com/hyperledger/fabric-ca/lib/caerrors"
    23  	"github.com/hyperledger/fabric-ca/lib/mocks"
    24  	"github.com/hyperledger/fabric-ca/lib/server/certificaterequest"
    25  	dbutil "github.com/hyperledger/fabric-ca/lib/server/db/util"
    26  	dbuser "github.com/hyperledger/fabric-ca/lib/server/user"
    27  	"github.com/hyperledger/fabric-ca/util"
    28  	"github.com/hyperledger/fabric/common/metrics/metricsfakes"
    29  	"github.com/pkg/errors"
    30  	"github.com/stretchr/testify/assert"
    31  )
    32  
    33  func TestCertificatesHandler(t *testing.T) {
    34  	ctx := new(serverRequestContextImpl)
    35  	req, err := http.NewRequest("GET", "", bytes.NewReader([]byte{}))
    36  	ctx.req = req
    37  	_, err = certificatesHandler(ctx)
    38  	util.ErrorContains(t, err, "No authorization header", "Failed to catch error")
    39  }
    40  
    41  func TestAuthChecks(t *testing.T) {
    42  	ctxMock := new(mocks.ServerRequestContext)
    43  	ctxMock.On("GetCaller").Return(nil, errors.New("Failed to get caller"))
    44  	err := authChecks(ctxMock)
    45  	util.ErrorContains(t, err, "Failed to get caller", "Expected to catch error from GetCaller() func")
    46  
    47  	ctx := new(serverRequestContextImpl)
    48  	user := &dbuser.Record{
    49  		Name: "NotRegistrar",
    50  	}
    51  	ctx.caller = dbuser.New(user, nil)
    52  	err = authChecks(ctx)
    53  	assert.Error(t, err, "Caller does not possess the appropriate attributes to request manage certificates")
    54  
    55  	attributes := []api.Attribute{
    56  		api.Attribute{
    57  			Name:  "hf.Registrar.Roles",
    58  			Value: "peer,client",
    59  		},
    60  	}
    61  
    62  	attr, err := util.Marshal(attributes, "attributes")
    63  	util.FatalError(t, err, "Failed to marshal attributes")
    64  	user = &dbuser.Record{
    65  		Name:       "Registrar",
    66  		Attributes: string(attr),
    67  	}
    68  	ctx.caller = dbuser.New(user, nil)
    69  	err = authChecks(ctx)
    70  	assert.NoError(t, err, "Should not fail, caller has 'hf.Registrar.Roles' attribute")
    71  
    72  	attributes = []api.Attribute{
    73  		api.Attribute{
    74  			Name:  "hf.Revoker",
    75  			Value: "true",
    76  		},
    77  	}
    78  	attr, err = util.Marshal(attributes, "attributes")
    79  	util.FatalError(t, err, "Failed to marshal attributes")
    80  	user = &dbuser.Record{
    81  		Name:       "Revoker",
    82  		Attributes: string(attr),
    83  	}
    84  	ctx.caller = dbuser.New(user, nil)
    85  	err = authChecks(ctx)
    86  	assert.NoError(t, err, "Should not fail, caller has 'hf.Revoker' with a value of 'true' attribute")
    87  
    88  	ctx = new(serverRequestContextImpl)
    89  	attributes = []api.Attribute{
    90  		api.Attribute{
    91  			Name:  "hf.Revoker",
    92  			Value: "false",
    93  		},
    94  	}
    95  	attr, err = util.Marshal(attributes, "attributes")
    96  	util.FatalError(t, err, "Failed to marshal attributes")
    97  	user = &dbuser.Record{
    98  		Name:       "NotRevoker",
    99  		Attributes: string(attr),
   100  	}
   101  	ctx.caller = dbuser.New(user, nil)
   102  	err = authChecks(ctx)
   103  	assert.Error(t, err, "Should fail, caller has 'hf.Revoker' but with a value of 'false' attribute")
   104  }
   105  
   106  func TestProcessCertificateRequest(t *testing.T) {
   107  	ctx := new(mocks.ServerRequestContext)
   108  	ctx.On("TokenAuthentication").Return("", errors.New("Token Auth Failed"))
   109  	err := processCertificateRequest(ctx)
   110  	util.ErrorContains(t, err, "Token Auth Failed", "Should have failed token auth")
   111  
   112  	ctx = new(mocks.ServerRequestContext)
   113  	ctx.On("TokenAuthentication").Return("", nil)
   114  	ctx.On("HasRole", "hf.Revoker").Return(errors.New("Does not have attribute"))
   115  	attr, err := util.Marshal([]api.Attribute{}, "attributes")
   116  	util.FatalError(t, err, "Failed to marshal attributes")
   117  	user := &dbuser.Record{
   118  		Name:       "NotRevoker",
   119  		Attributes: string(attr),
   120  	}
   121  	ctx.On("GetCaller").Return(dbuser.New(user, nil), nil)
   122  
   123  	err = processCertificateRequest(ctx)
   124  	t.Log("Error: ", err)
   125  	util.ErrorContains(t, err, fmt.Sprintf("%d", caerrors.ErrAuthorizationFailure), "Should have failed to due improper permissions")
   126  
   127  	ctx = new(mocks.ServerRequestContext)
   128  	ctx.On("TokenAuthentication").Return("", nil)
   129  	ctx.On("HasRole", "hf.Revoker").Return(nil)
   130  	ctx.On("GetCaller").Return(dbuser.New(user, nil), nil)
   131  	req, err := http.NewRequest("POST", "", bytes.NewReader([]byte{}))
   132  	util.FatalError(t, err, "Failed to get HTTP request")
   133  	ctx.On("GetReq").Return(req)
   134  	err = processCertificateRequest(ctx)
   135  	t.Log("Error: ", err)
   136  	util.ErrorContains(t, err, "Invalid request", "Should have failed to incorrect method type on HTTP request")
   137  
   138  }
   139  
   140  func TestProcessGetCertificateRequest(t *testing.T) {
   141  	ctx := new(serverRequestContextImpl)
   142  	req, err := http.NewRequest("GET", "", bytes.NewReader([]byte{}))
   143  	util.FatalError(t, err, "Failed to get GET HTTP request")
   144  
   145  	url := req.URL.Query()
   146  	url.Add("revoked_end", "2012-12-123")
   147  	req.URL.RawQuery = url.Encode()
   148  	ctx.req = req
   149  	err = processGetCertificateRequest(ctx)
   150  	assert.Error(t, err, "Invalid time format, should have failed")
   151  
   152  	req, err = http.NewRequest("GET", "", bytes.NewReader([]byte{}))
   153  	util.FatalError(t, err, "Failed to get GET HTTP request")
   154  	url = req.URL.Query()
   155  	url.Add("notrevoked", "not_bool")
   156  	req.URL.RawQuery = url.Encode()
   157  	ctx.req = req
   158  
   159  	err = processGetCertificateRequest(ctx)
   160  	assert.Error(t, err, "Invalid boolean value, should have failed")
   161  
   162  	req, err = http.NewRequest("GET", "", bytes.NewReader([]byte{}))
   163  	util.FatalError(t, err, "Failed to get GET HTTP request")
   164  	url = req.URL.Query()
   165  	url.Add("revoked_end", "2012-12-12")
   166  	url.Add("notrevoked", "true")
   167  	req.URL.RawQuery = url.Encode()
   168  	ctx.req = req
   169  
   170  	err = processGetCertificateRequest(ctx)
   171  	assert.Error(t, err, "Invalid combination of filters, should have failed")
   172  }
   173  
   174  type mockHTTPWriter struct {
   175  	http.ResponseWriter
   176  	t *testing.T
   177  }
   178  
   179  // Header returns the header map that will be sent by WriteHeader.
   180  func (m *mockHTTPWriter) Header() http.Header {
   181  	return m.ResponseWriter.Header()
   182  }
   183  
   184  // WriteHeader sends an HTTP response header with status code.
   185  func (m *mockHTTPWriter) WriteHeader(scode int) {
   186  	m.WriteHeader(1)
   187  }
   188  
   189  // Write writes the data to the connection as part of an HTTP reply.
   190  func (m *mockHTTPWriter) Write(buf []byte) (int, error) {
   191  	w := m.ResponseWriter
   192  	if !strings.Contains(string(buf), "certs") && !strings.Contains(string(buf), "BEGIN CERTIFICATE") && !strings.Contains(string(buf), "caname") {
   193  		m.t.Error("Invalid response being sent back from certificates endpoint")
   194  	}
   195  	return w.Write(buf)
   196  }
   197  
   198  // Write writes the data to the connection as part of an HTTP reply.
   199  func (m *mockHTTPWriter) Flush() {}
   200  
   201  func TestServerGetCertificates(t *testing.T) {
   202  	os.RemoveAll("getCertTest")
   203  	defer os.RemoveAll("getCertTest")
   204  	var err error
   205  
   206  	level := &dbutil.Levels{
   207  		Affiliation: 1,
   208  		Identity:    1,
   209  		Certificate: 1,
   210  	}
   211  	mockOperationsServer := &mocks.OperationsServer{}
   212  	fakeCounter := &metricsfakes.Counter{}
   213  	fakeCounter.WithReturns(fakeCounter)
   214  	mockOperationsServer.NewCounterReturns(fakeCounter)
   215  	fakeHistogram := &metricsfakes.Histogram{}
   216  	fakeHistogram.WithReturns(fakeHistogram)
   217  	mockOperationsServer.NewHistogramReturns(fakeHistogram)
   218  	srv := &Server{
   219  		Operations: mockOperationsServer,
   220  		levels:     level,
   221  	}
   222  	ca, err := newCA("getCertTest/config.yaml", &CAConfig{}, srv, false)
   223  	util.FatalError(t, err, "Failed to get CA")
   224  
   225  	ctx := new(serverRequestContextImpl)
   226  	req, err := http.NewRequest("GET", "", bytes.NewReader([]byte{}))
   227  	util.FatalError(t, err, "Failed to get GET HTTP request")
   228  
   229  	user := &dbuser.Record{
   230  		Name: "NotRevoker",
   231  	}
   232  	ctx.caller = dbuser.New(user, nil)
   233  
   234  	ctx.req = req
   235  	ctx.ca = ca
   236  	w := httptest.NewRecorder()
   237  	ctx.resp = &mockHTTPWriter{w, t}
   238  
   239  	err = testInsertCertificate(&certdb.CertificateRecord{
   240  		Serial: "1111",
   241  		AKI:    "9876",
   242  	}, "testCertificate", ca)
   243  	util.FatalError(t, err, "Failed to insert certificate with serial/AKI")
   244  
   245  	err = getCertificates(ctx, &certificaterequest.Impl{})
   246  	assert.NoError(t, err, "Should not have returned error, failed to process GET certificate request")
   247  
   248  	mockCtx := new(mocks.ServerRequestContext)
   249  	mockCtx.On("GetResp").Return(nil)
   250  	mockCtx.On("GetCaller").Return(nil, errors.New("failed to get caller"))
   251  	err = getCertificates(mockCtx, nil)
   252  	util.ErrorContains(t, err, "failed to get caller", "did not get correct error response")
   253  
   254  	testUser := dbuser.New(&dbuser.Record{Name: "testuser"}, nil)
   255  	mockCtx = new(mocks.ServerRequestContext)
   256  	mockCtx.On("GetResp").Return(nil)
   257  	mockCtx.On("GetCaller").Return(testUser, nil)
   258  	mockCtx.On("GetCertificates", (*certificaterequest.Impl)(nil), "").Return(nil, errors.New("failed to get certificates"))
   259  	err = getCertificates(mockCtx, nil)
   260  	util.ErrorContains(t, err, "failed to get certificates", "did not get correct error response")
   261  }
   262  
   263  func TestStoreCert(t *testing.T) {
   264  	dir, err := ioutil.TempDir("", "testStoreCert")
   265  	assert.NoError(t, err, "failed to create temp directory")
   266  	defer os.RemoveAll(dir)
   267  
   268  	cd := NewCertificateDecoder(dir)
   269  	err = cd.StoreCert("testID", dir, []byte("testing store cert function"))
   270  	assert.NoError(t, err, "failed to store cert")
   271  
   272  	filePath := filepath.Join(dir, "testID.pem")
   273  	assert.Equal(t, true, util.FileExists(filePath))
   274  
   275  	cert, err := ioutil.ReadFile(filePath)
   276  	assert.NoError(t, err, "failed to read certificate file")
   277  	assert.Equal(t, "testing store cert function", string(cert))
   278  
   279  	err = cd.StoreCert("testID", dir, []byte("testing store cert function - second cert"))
   280  	assert.NoError(t, err, "failed to store cert")
   281  
   282  	filePath = filepath.Join(dir, "testID-1.pem")
   283  	assert.Equal(t, true, util.FileExists(filePath))
   284  	cert, err = ioutil.ReadFile(filePath)
   285  	assert.NoError(t, err, "failed to read certificate file")
   286  	assert.Equal(t, "testing store cert function", string(cert))
   287  
   288  	filePath = filepath.Join(dir, "testID-2.pem")
   289  	assert.Equal(t, true, util.FileExists(filePath))
   290  	cert, err = ioutil.ReadFile(filePath)
   291  	assert.NoError(t, err, "failed to read certificate file")
   292  	assert.Equal(t, "testing store cert function - second cert", string(cert))
   293  
   294  	err = cd.StoreCert("testID", dir, []byte("testing store cert function - third cert"))
   295  	assert.NoError(t, err, "failed to store cert")
   296  	filePath = filepath.Join(dir, "testID-3.pem")
   297  	assert.Equal(t, true, util.FileExists(filePath))
   298  
   299  	// Error case - renaming a certificate file that does not exist should fail
   300  	cd = NewCertificateDecoder(dir)
   301  	cd.certIDCount["testID2"] = 1
   302  	err = cd.StoreCert("testID2", dir, []byte("testing store cert function"))
   303  	util.ErrorContains(t, err, "Failed to rename certificate", "Should have failed")
   304  }