github.com/companieshouse/lfp-pay-api@v0.0.0-20230203133422-0ca455cd79f9/interceptors/payable_resource_authentication_test.go (about) 1 package interceptors 2 3 import ( 4 "context" 5 "fmt" 6 "net/http" 7 "net/http/httptest" 8 "testing" 9 "time" 10 11 "github.com/companieshouse/lfp-pay-api-core/constants" 12 13 "github.com/companieshouse/chs.go/authentication" 14 "github.com/companieshouse/lfp-pay-api-core/models" 15 "github.com/companieshouse/lfp-pay-api/config" 16 "github.com/companieshouse/lfp-pay-api/mocks" 17 "github.com/companieshouse/lfp-pay-api/service" 18 "github.com/golang/mock/gomock" 19 "github.com/gorilla/mux" 20 "github.com/jarcoal/httpmock" 21 22 . "github.com/smartystreets/goconvey/convey" 23 ) 24 25 func GetTestHandler() http.HandlerFunc { 26 fn := func(w http.ResponseWriter, req *http.Request) { 27 w.WriteHeader(http.StatusOK) 28 } 29 return http.HandlerFunc(fn) 30 } 31 32 func createMockPayableResourceService(mockDAO *mocks.MockService, cfg *config.Config) service.PayableResourceService { 33 return service.PayableResourceService{ 34 DAO: mockDAO, 35 Config: cfg, 36 } 37 } 38 39 // Function to create a PayableAuthenticationInterceptor with mock mongo DAO and a mock payment service 40 func createPayableAuthenticationInterceptorWithMockDAOAndService(controller *gomock.Controller, cfg *config.Config) PayableAuthenticationInterceptor { 41 mockDAO := mocks.NewMockService(controller) 42 mockPayableResourceService := createMockPayableResourceService(mockDAO, cfg) 43 return PayableAuthenticationInterceptor{ 44 Service: mockPayableResourceService, 45 } 46 } 47 48 // Function to create a PayableAuthenticationInterceptor with the supplied payment service 49 func createPayableAuthenticationInterceptorWithMockService(PayableResourceService *service.PayableResourceService) PayableAuthenticationInterceptor { 50 return PayableAuthenticationInterceptor{ 51 Service: *PayableResourceService, 52 } 53 } 54 55 func TestUnitUserPaymentInterceptor(t *testing.T) { 56 mockCtrl := gomock.NewController(t) 57 defer mockCtrl.Finish() 58 cfg, _ := config.Get() 59 60 Convey("No payment ID in request", t, func() { 61 path := fmt.Sprintf("/company/12345678/penalties/late-filing/payable/") 62 req, err := http.NewRequest("GET", path, nil) 63 So(err, ShouldBeNil) 64 req.Header.Set("Eric-Identity", "authorised_identity") 65 req.Header.Set("Eric-Identity-Type", "oauth2") 66 req.Header.Set("ERIC-Authorised-User", "test@test.com;test;user") 67 req.Header.Set("ERIC-Authorised-Roles", "noroles") 68 69 payableAuthenticationInterceptor := createPayableAuthenticationInterceptorWithMockDAOAndService(mockCtrl, cfg) 70 71 w := httptest.NewRecorder() 72 test := payableAuthenticationInterceptor.PayableAuthenticationIntercept(GetTestHandler()) 73 test.ServeHTTP(w, req) 74 So(w.Code, ShouldEqual, http.StatusBadRequest) 75 }) 76 77 Convey("Invalid user details in context", t, func() { 78 path := fmt.Sprintf("/company/12345678/penalties/late-filing/payable/%s", "1234") 79 req, err := http.NewRequest("GET", path, nil) 80 So(err, ShouldBeNil) 81 req = mux.SetURLVars(req, map[string]string{"company_number": "12345678", "payable_id": "1234"}) 82 req.Header.Set("Eric-Identity", "authorised_identity") 83 req.Header.Set("Eric-Identity-Type", "oauth2") 84 req.Header.Set("ERIC-Authorised-User", "test@test.com;test;user") 85 req.Header.Set("ERIC-Authorised-Roles", "noroles") 86 // The details have to be in a authUserDetails struct, so pass a different struct to fail 87 authUserDetails := models.PayableResource{ 88 Reference: "test", 89 } 90 ctx := context.WithValue(req.Context(), authentication.ContextKeyUserDetails, authUserDetails) 91 92 payableAuthenticationInterceptor := createPayableAuthenticationInterceptorWithMockDAOAndService(mockCtrl, cfg) 93 94 w := httptest.NewRecorder() 95 test := payableAuthenticationInterceptor.PayableAuthenticationIntercept(GetTestHandler()) 96 test.ServeHTTP(w, req.WithContext(ctx)) 97 So(w.Code, ShouldEqual, http.StatusInternalServerError) 98 }) 99 100 Convey("No authorised identity", t, func() { 101 path := fmt.Sprintf("/company/12345678/penalties/late-filing/payable/%s", "1234") 102 req, err := http.NewRequest("GET", path, nil) 103 So(err, ShouldBeNil) 104 req = mux.SetURLVars(req, map[string]string{"company_number": "12345678", "payable_id": "1234"}) 105 req.Header.Set("Eric-Identity", "authorised_identity") 106 req.Header.Set("Eric-Identity-Type", "oauth2") 107 req.Header.Set("ERIC-Authorised-User", "test@test.com;test;user") 108 req.Header.Set("ERIC-Authorised-Roles", "noroles") 109 // Pass no ID (identity) 110 authUserDetails := authentication.AuthUserDetails{} 111 ctx := context.WithValue(req.Context(), authentication.ContextKeyUserDetails, authUserDetails) 112 113 payableAuthenticationInterceptor := createPayableAuthenticationInterceptorWithMockDAOAndService(mockCtrl, cfg) 114 115 w := httptest.NewRecorder() 116 test := payableAuthenticationInterceptor.PayableAuthenticationIntercept(GetTestHandler()) 117 test.ServeHTTP(w, req.WithContext(ctx)) 118 So(w.Code, ShouldEqual, http.StatusUnauthorized) 119 }) 120 121 Convey("Payment not found in DB", t, func() { 122 path := fmt.Sprintf("/company/12345678/penalties/late-filing/payable/%s", "1234") 123 req, err := http.NewRequest("GET", path, nil) 124 So(err, ShouldBeNil) 125 req = mux.SetURLVars(req, map[string]string{"company_number": "12345678", "payable_id": "1234"}) 126 req.Header.Set("Eric-Identity", "identity") 127 req.Header.Set("Eric-Identity-Type", "oauth2") 128 req.Header.Set("ERIC-Authorised-User", "test@test.com;test;user") 129 req.Header.Set("ERIC-Authorised-Roles", "/admin/payment-lookup") 130 authUserDetails := authentication.AuthUserDetails{ 131 ID: "identity", 132 } 133 ctx := context.WithValue(req.Context(), authentication.ContextKeyUserDetails, authUserDetails) 134 135 mockDAO := mocks.NewMockService(mockCtrl) 136 mockPayableResourceService := createMockPayableResourceService(mockDAO, cfg) 137 payableAuthenticationInterceptor := createPayableAuthenticationInterceptorWithMockService(&mockPayableResourceService) 138 139 mockDAO.EXPECT().GetPayableResource("12345678", "1234").Return(nil, nil) 140 141 w := httptest.NewRecorder() 142 httpmock.Activate() 143 defer httpmock.DeactivateAndReset() 144 145 test := payableAuthenticationInterceptor.PayableAuthenticationIntercept(GetTestHandler()) 146 test.ServeHTTP(w, req.WithContext(ctx)) 147 So(w.Code, ShouldEqual, http.StatusNotFound) 148 }) 149 150 Convey("Error reading from DB", t, func() { 151 path := fmt.Sprintf("/company/12345678/penalties/late-filing/payable/%s", "1234") 152 req, err := http.NewRequest("GET", path, nil) 153 So(err, ShouldBeNil) 154 req = mux.SetURLVars(req, map[string]string{"company_number": "12345678", "payable_id": "1234"}) 155 req.Header.Set("Eric-Identity", "identity") 156 req.Header.Set("Eric-Identity-Type", "oauth2") 157 req.Header.Set("ERIC-Authorised-User", "test@test.com;test;user") 158 req.Header.Set("ERIC-Authorised-Roles", "/admin/payment-lookup") 159 authUserDetails := authentication.AuthUserDetails{ 160 ID: "identity", 161 } 162 ctx := context.WithValue(req.Context(), authentication.ContextKeyUserDetails, authUserDetails) 163 164 mockDAO := mocks.NewMockService(mockCtrl) 165 mockPayableResourceService := createMockPayableResourceService(mockDAO, cfg) 166 payableAuthenticationInterceptor := createPayableAuthenticationInterceptorWithMockService(&mockPayableResourceService) 167 168 mockDAO.EXPECT().GetPayableResource("12345678", "1234").Return(&models.PayableResourceDao{}, fmt.Errorf("error")) 169 170 w := httptest.NewRecorder() 171 httpmock.Activate() 172 defer httpmock.DeactivateAndReset() 173 174 test := payableAuthenticationInterceptor.PayableAuthenticationIntercept(GetTestHandler()) 175 test.ServeHTTP(w, req.WithContext(ctx)) 176 So(w.Code, ShouldEqual, http.StatusInternalServerError) 177 }) 178 179 Convey("Happy path where user is creator", t, func() { 180 path := fmt.Sprintf("/company/12345678/penalties/late-filing/payable/%s", "1234") 181 req, err := http.NewRequest("GET", path, nil) 182 So(err, ShouldBeNil) 183 req = mux.SetURLVars(req, map[string]string{"company_number": "12345678", "payable_id": "1234"}) 184 req.Header.Set("Eric-Identity", "identity") 185 req.Header.Set("Eric-Identity-Type", "oauth2") 186 req.Header.Set("ERIC-Authorised-User", "test@test.com;test;user") 187 req.Header.Set("ERIC-Authorised-Roles", "noroles") 188 authUserDetails := authentication.AuthUserDetails{ 189 ID: "identity", 190 } 191 ctx := context.WithValue(req.Context(), authentication.ContextKeyUserDetails, authUserDetails) 192 193 mockDAO := mocks.NewMockService(mockCtrl) 194 mockPayableResourceService := createMockPayableResourceService(mockDAO, cfg) 195 payableAuthenticationInterceptor := createPayableAuthenticationInterceptorWithMockService(&mockPayableResourceService) 196 197 txs := map[string]models.TransactionDao{ 198 "abcd": models.TransactionDao{Amount: 5}, 199 } 200 createdAt := time.Now().Truncate(time.Millisecond) 201 mockDAO.EXPECT().GetPayableResource("12345678", "1234").Return( 202 &models.PayableResourceDao{ 203 CompanyNumber: "12345678", 204 Reference: "1234", 205 Data: models.PayableResourceDataDao{ 206 Etag: "qwertyetag1234", 207 CreatedAt: &createdAt, 208 CreatedBy: models.CreatedByDao{ 209 ID: "identity", 210 }, 211 Links: models.PayableResourceLinksDao{ 212 Self: "/company/12345678/penalties/late-filing/payable/1234", 213 }, 214 Transactions: txs, 215 Payment: models.PaymentDao{ 216 Status: constants.Pending.String(), 217 Amount: "5", 218 }, 219 }, 220 }, 221 nil, 222 ) 223 224 w := httptest.NewRecorder() 225 httpmock.Activate() 226 defer httpmock.DeactivateAndReset() 227 228 test := payableAuthenticationInterceptor.PayableAuthenticationIntercept(GetTestHandler()) 229 test.ServeHTTP(w, req.WithContext(ctx)) 230 So(w.Code, ShouldEqual, http.StatusOK) 231 }) 232 233 Convey("Happy path where user is admin and request is GET", t, func() { 234 path := fmt.Sprintf("/company/12345678/penalties/late-filing/payable/%s", "1234") 235 req, err := http.NewRequest("GET", path, nil) 236 So(err, ShouldBeNil) 237 req = mux.SetURLVars(req, map[string]string{"company_number": "12345678", "payable_id": "1234"}) 238 req.Header.Set("Eric-Identity", "admin") 239 req.Header.Set("Eric-Identity-Type", "oauth2") 240 req.Header.Set("ERIC-Authorised-User", "test@test.com;test;user") 241 req.Header.Set("ERIC-Authorised-Roles", "/admin/penalty-lookup") 242 authUserDetails := authentication.AuthUserDetails{ 243 ID: "admin", 244 } 245 ctx := context.WithValue(req.Context(), authentication.ContextKeyUserDetails, authUserDetails) 246 247 mockDAO := mocks.NewMockService(mockCtrl) 248 mockPayableResourceService := createMockPayableResourceService(mockDAO, cfg) 249 payableAuthenticationInterceptor := createPayableAuthenticationInterceptorWithMockService(&mockPayableResourceService) 250 251 txs := map[string]models.TransactionDao{ 252 "abcd": models.TransactionDao{Amount: 5}, 253 } 254 createdAt := time.Now().Truncate(time.Millisecond) 255 mockDAO.EXPECT().GetPayableResource("12345678", "1234").Return( 256 &models.PayableResourceDao{ 257 CompanyNumber: "12345678", 258 Reference: "1234", 259 Data: models.PayableResourceDataDao{ 260 Etag: "qwertyetag1234", 261 CreatedAt: &createdAt, 262 CreatedBy: models.CreatedByDao{ 263 ID: "identity", 264 }, 265 Links: models.PayableResourceLinksDao{ 266 Self: "/company/12345678/penalties/late-filing/payable/1234", 267 }, 268 Transactions: txs, 269 Payment: models.PaymentDao{ 270 Status: constants.Pending.String(), 271 Amount: "5", 272 }, 273 }, 274 }, 275 nil, 276 ) 277 278 w := httptest.NewRecorder() 279 httpmock.Activate() 280 defer httpmock.DeactivateAndReset() 281 282 test := payableAuthenticationInterceptor.PayableAuthenticationIntercept(GetTestHandler()) 283 test.ServeHTTP(w, req.WithContext(ctx)) 284 So(w.Code, ShouldEqual, http.StatusOK) 285 }) 286 287 Convey("Unauthorised where user is admin and request is POST", t, func() { 288 path := fmt.Sprintf("/company/12345678/penalties/late-filing/payable/%s", "1234") 289 req, err := http.NewRequest("POST", path, nil) 290 So(err, ShouldBeNil) 291 req = mux.SetURLVars(req, map[string]string{"company_number": "12345678", "payable_id": "1234"}) 292 req.Header.Set("Eric-Identity", "admin") 293 req.Header.Set("Eric-Identity-Type", "oauth2") 294 req.Header.Set("ERIC-Authorised-User", "test@test.com;test;user") 295 req.Header.Set("ERIC-Authorised-Roles", "/admin/payment-lookup") 296 authUserDetails := authentication.AuthUserDetails{ 297 ID: "admin", 298 } 299 ctx := context.WithValue(req.Context(), authentication.ContextKeyUserDetails, authUserDetails) 300 301 mockDAO := mocks.NewMockService(mockCtrl) 302 mockPayableResourceService := createMockPayableResourceService(mockDAO, cfg) 303 payableAuthenticationInterceptor := createPayableAuthenticationInterceptorWithMockService(&mockPayableResourceService) 304 305 txs := map[string]models.TransactionDao{ 306 "abcd": models.TransactionDao{Amount: 5}, 307 } 308 createdAt := time.Now().Truncate(time.Millisecond) 309 mockDAO.EXPECT().GetPayableResource("12345678", "1234").Return( 310 &models.PayableResourceDao{ 311 CompanyNumber: "12345678", 312 Reference: "1234", 313 Data: models.PayableResourceDataDao{ 314 Etag: "qwertyetag1234", 315 CreatedAt: &createdAt, 316 CreatedBy: models.CreatedByDao{ 317 ID: "identity", 318 }, 319 Links: models.PayableResourceLinksDao{ 320 Self: "/company/12345678/penalties/late-filing/payable/1234", 321 }, 322 Transactions: txs, 323 Payment: models.PaymentDao{ 324 Status: constants.Pending.String(), 325 Amount: "5", 326 }, 327 }, 328 }, 329 nil, 330 ) 331 332 w := httptest.NewRecorder() 333 httpmock.Activate() 334 defer httpmock.DeactivateAndReset() 335 336 test := payableAuthenticationInterceptor.PayableAuthenticationIntercept(GetTestHandler()) 337 test.ServeHTTP(w, req.WithContext(ctx)) 338 So(w.Code, ShouldEqual, http.StatusUnauthorized) 339 }) 340 341 Convey("Happy path where user has elevated privileges key accessing a non-creator resource", t, func() { 342 path := fmt.Sprintf("/company/12345678/penalties/late-filing/payable/%s", "1234") 343 req, err := http.NewRequest("GET", path, nil) 344 So(err, ShouldBeNil) 345 req = mux.SetURLVars(req, map[string]string{"company_number": "12345678", "payable_id": "1234"}) 346 req.Header.Set("Eric-Identity", "api_key") 347 req.Header.Set("Eric-Identity-Type", "key") 348 req.Header.Set("ERIC-Authorised-Key-Roles", "*") 349 mockDAO := mocks.NewMockService(mockCtrl) 350 mockPayableResourceService := createMockPayableResourceService(mockDAO, cfg) 351 payableAuthenticationInterceptor := createPayableAuthenticationInterceptorWithMockService(&mockPayableResourceService) 352 353 txs := map[string]models.TransactionDao{ 354 "abcd": models.TransactionDao{Amount: 5}, 355 } 356 createdAt := time.Now().Truncate(time.Millisecond) 357 mockDAO.EXPECT().GetPayableResource("12345678", "1234").Return( 358 &models.PayableResourceDao{ 359 CompanyNumber: "12345678", 360 Reference: "1234", 361 Data: models.PayableResourceDataDao{ 362 Etag: "qwertyetag1234", 363 CreatedAt: &createdAt, 364 CreatedBy: models.CreatedByDao{ 365 ID: "identity", 366 }, 367 Links: models.PayableResourceLinksDao{ 368 Self: "/company/12345678/penalties/late-filing/payable/1234", 369 }, 370 Transactions: txs, 371 Payment: models.PaymentDao{ 372 Status: constants.Pending.String(), 373 Amount: "5", 374 }, 375 }, 376 }, 377 nil, 378 ) 379 380 w := httptest.NewRecorder() 381 httpmock.Activate() 382 defer httpmock.DeactivateAndReset() 383 384 test := payableAuthenticationInterceptor.PayableAuthenticationIntercept(GetTestHandler()) 385 test.ServeHTTP(w, req) 386 So(w.Code, ShouldEqual, http.StatusOK) 387 }) 388 389 Convey("Happy path where Company Number is made uppercase", t, func() { 390 path := fmt.Sprintf("/company/12345678/penalties/late-filing/payable/%s", "1234") 391 req, err := http.NewRequest("GET", path, nil) 392 So(err, ShouldBeNil) 393 req = mux.SetURLVars(req, map[string]string{"company_number": "oc444555", "payable_id": "1234"}) 394 req.Header.Set("Eric-Identity", "api_key") 395 req.Header.Set("Eric-Identity-Type", "key") 396 req.Header.Set("ERIC-Authorised-Key-Roles", "*") 397 mockDAO := mocks.NewMockService(mockCtrl) 398 mockPayableResourceService := createMockPayableResourceService(mockDAO, cfg) 399 payableAuthenticationInterceptor := createPayableAuthenticationInterceptorWithMockService(&mockPayableResourceService) 400 401 txs := map[string]models.TransactionDao{ 402 "abcd": models.TransactionDao{Amount: 5}, 403 } 404 createdAt := time.Now().Truncate(time.Millisecond) 405 mockDAO.EXPECT().GetPayableResource("OC444555", "1234").Return( 406 &models.PayableResourceDao{ 407 CompanyNumber: "OC444555", 408 Reference: "1234", 409 Data: models.PayableResourceDataDao{ 410 Etag: "qwertyetag1234", 411 CreatedAt: &createdAt, 412 CreatedBy: models.CreatedByDao{ 413 ID: "identity", 414 }, 415 Links: models.PayableResourceLinksDao{ 416 Self: "/company/OC444555/penalties/late-filing/payable/1234", 417 }, 418 Transactions: txs, 419 Payment: models.PaymentDao{ 420 Status: constants.Pending.String(), 421 Amount: "5", 422 }, 423 }, 424 }, 425 nil, 426 ) 427 428 w := httptest.NewRecorder() 429 httpmock.Activate() 430 defer httpmock.DeactivateAndReset() 431 432 test := payableAuthenticationInterceptor.PayableAuthenticationIntercept(GetTestHandler()) 433 test.ServeHTTP(w, req) 434 So(w.Code, ShouldEqual, http.StatusOK) 435 }) 436 }