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  }