github.com/hyperledger/fabric-ca@v2.0.0-alpha.0.20201120210307-7b4f34729db1+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/internal/pkg/api" 22 "github.com/hyperledger/fabric-ca/internal/pkg/util" 23 "github.com/hyperledger/fabric-ca/lib/caerrors" 24 "github.com/hyperledger/fabric-ca/lib/mocks" 25 "github.com/hyperledger/fabric-ca/lib/server/certificaterequest" 26 dbutil "github.com/hyperledger/fabric-ca/lib/server/db/util" 27 dbuser "github.com/hyperledger/fabric-ca/lib/server/user" 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 }