github.com/gophish/gophish@v0.12.2-0.20230915144530-8e7929441393/middleware/middleware_test.go (about)

     1  package middleware
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  	"net/http/httptest"
     7  	"testing"
     8  
     9  	"github.com/gophish/gophish/config"
    10  	ctx "github.com/gophish/gophish/context"
    11  	"github.com/gophish/gophish/models"
    12  )
    13  
    14  var successHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    15  	w.Write([]byte("success"))
    16  })
    17  
    18  type testContext struct {
    19  	apiKey string
    20  }
    21  
    22  func setupTest(t *testing.T) *testContext {
    23  	conf := &config.Config{
    24  		DBName:         "sqlite3",
    25  		DBPath:         ":memory:",
    26  		MigrationsPath: "../db/db_sqlite3/migrations/",
    27  	}
    28  	err := models.Setup(conf)
    29  	if err != nil {
    30  		t.Fatalf("Failed creating database: %v", err)
    31  	}
    32  	// Get the API key to use for these tests
    33  	u, err := models.GetUser(1)
    34  	if err != nil {
    35  		t.Fatalf("error getting user: %v", err)
    36  	}
    37  	ctx := &testContext{}
    38  	ctx.apiKey = u.ApiKey
    39  	return ctx
    40  }
    41  
    42  // MiddlewarePermissionTest maps an expected HTTP Method to an expected HTTP
    43  // status code
    44  type MiddlewarePermissionTest map[string]int
    45  
    46  // TestEnforceViewOnly ensures that only users with the ModifyObjects
    47  // permission have the ability to send non-GET requests.
    48  func TestEnforceViewOnly(t *testing.T) {
    49  	setupTest(t)
    50  	permissionTests := map[string]MiddlewarePermissionTest{
    51  		models.RoleAdmin: MiddlewarePermissionTest{
    52  			http.MethodGet:     http.StatusOK,
    53  			http.MethodHead:    http.StatusOK,
    54  			http.MethodOptions: http.StatusOK,
    55  			http.MethodPost:    http.StatusOK,
    56  			http.MethodPut:     http.StatusOK,
    57  			http.MethodDelete:  http.StatusOK,
    58  		},
    59  		models.RoleUser: MiddlewarePermissionTest{
    60  			http.MethodGet:     http.StatusOK,
    61  			http.MethodHead:    http.StatusOK,
    62  			http.MethodOptions: http.StatusOK,
    63  			http.MethodPost:    http.StatusOK,
    64  			http.MethodPut:     http.StatusOK,
    65  			http.MethodDelete:  http.StatusOK,
    66  		},
    67  	}
    68  	for r, checks := range permissionTests {
    69  		role, err := models.GetRoleBySlug(r)
    70  		if err != nil {
    71  			t.Fatalf("error getting role by slug: %v", err)
    72  		}
    73  
    74  		for method, expected := range checks {
    75  			req := httptest.NewRequest(method, "/", nil)
    76  			response := httptest.NewRecorder()
    77  
    78  			req = ctx.Set(req, "user", models.User{
    79  				Role:   role,
    80  				RoleID: role.ID,
    81  			})
    82  
    83  			EnforceViewOnly(successHandler).ServeHTTP(response, req)
    84  			got := response.Code
    85  			if got != expected {
    86  				t.Fatalf("incorrect status code received. expected %d got %d", expected, got)
    87  			}
    88  		}
    89  	}
    90  }
    91  
    92  func TestRequirePermission(t *testing.T) {
    93  	setupTest(t)
    94  	middleware := RequirePermission(models.PermissionModifySystem)
    95  	handler := middleware(successHandler)
    96  
    97  	permissionTests := map[string]int{
    98  		models.RoleUser:  http.StatusForbidden,
    99  		models.RoleAdmin: http.StatusOK,
   100  	}
   101  
   102  	for role, expected := range permissionTests {
   103  		req := httptest.NewRequest(http.MethodGet, "/", nil)
   104  		response := httptest.NewRecorder()
   105  		// Test that with the requested permission, the request succeeds
   106  		role, err := models.GetRoleBySlug(role)
   107  		if err != nil {
   108  			t.Fatalf("error getting role by slug: %v", err)
   109  		}
   110  		req = ctx.Set(req, "user", models.User{
   111  			Role:   role,
   112  			RoleID: role.ID,
   113  		})
   114  		handler.ServeHTTP(response, req)
   115  		got := response.Code
   116  		if got != expected {
   117  			t.Fatalf("incorrect status code received. expected %d got %d", expected, got)
   118  		}
   119  	}
   120  }
   121  
   122  func TestRequireAPIKey(t *testing.T) {
   123  	setupTest(t)
   124  	req := httptest.NewRequest(http.MethodGet, "/", nil)
   125  	req.Header.Set("Content-Type", "application/json")
   126  	response := httptest.NewRecorder()
   127  	// Test that making a request without an API key is denied
   128  	RequireAPIKey(successHandler).ServeHTTP(response, req)
   129  	expected := http.StatusUnauthorized
   130  	got := response.Code
   131  	if got != expected {
   132  		t.Fatalf("incorrect status code received. expected %d got %d", expected, got)
   133  	}
   134  }
   135  
   136  func TestCORSHeaders(t *testing.T) {
   137  	setupTest(t)
   138  	req := httptest.NewRequest(http.MethodOptions, "/", nil)
   139  	response := httptest.NewRecorder()
   140  	RequireAPIKey(successHandler).ServeHTTP(response, req)
   141  	expected := "POST, GET, OPTIONS, PUT, DELETE"
   142  	got := response.Result().Header.Get("Access-Control-Allow-Methods")
   143  	if got != expected {
   144  		t.Fatalf("incorrect cors options received. expected %s got %s", expected, got)
   145  	}
   146  }
   147  
   148  func TestInvalidAPIKey(t *testing.T) {
   149  	setupTest(t)
   150  	req := httptest.NewRequest(http.MethodGet, "/", nil)
   151  	query := req.URL.Query()
   152  	query.Set("api_key", "bogus-api-key")
   153  	req.URL.RawQuery = query.Encode()
   154  	req.Header.Set("Content-Type", "application/json")
   155  	response := httptest.NewRecorder()
   156  	RequireAPIKey(successHandler).ServeHTTP(response, req)
   157  	expected := http.StatusUnauthorized
   158  	got := response.Code
   159  	if got != expected {
   160  		t.Fatalf("incorrect status code received. expected %d got %d", expected, got)
   161  	}
   162  }
   163  
   164  func TestBearerToken(t *testing.T) {
   165  	testCtx := setupTest(t)
   166  	req := httptest.NewRequest(http.MethodGet, "/", nil)
   167  	req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", testCtx.apiKey))
   168  	req.Header.Set("Content-Type", "application/json")
   169  	response := httptest.NewRecorder()
   170  	RequireAPIKey(successHandler).ServeHTTP(response, req)
   171  	expected := http.StatusOK
   172  	got := response.Code
   173  	if got != expected {
   174  		t.Fatalf("incorrect status code received. expected %d got %d", expected, got)
   175  	}
   176  }
   177  
   178  func TestPasswordResetRequired(t *testing.T) {
   179  	req := httptest.NewRequest(http.MethodGet, "/", nil)
   180  	req = ctx.Set(req, "user", models.User{
   181  		PasswordChangeRequired: true,
   182  	})
   183  	response := httptest.NewRecorder()
   184  	RequireLogin(successHandler).ServeHTTP(response, req)
   185  	gotStatus := response.Code
   186  	expectedStatus := http.StatusTemporaryRedirect
   187  	if gotStatus != expectedStatus {
   188  		t.Fatalf("incorrect status code received. expected %d got %d", expectedStatus, gotStatus)
   189  	}
   190  	expectedLocation := "/reset_password?next=%2F"
   191  	gotLocation := response.Header().Get("Location")
   192  	if gotLocation != expectedLocation {
   193  		t.Fatalf("incorrect location header received. expected %s got %s", expectedLocation, gotLocation)
   194  	}
   195  }
   196  
   197  func TestApplySecurityHeaders(t *testing.T) {
   198  	expected := map[string]string{
   199  		"Content-Security-Policy": "frame-ancestors 'none';",
   200  		"X-Frame-Options":         "DENY",
   201  	}
   202  	req := httptest.NewRequest(http.MethodGet, "/", nil)
   203  	response := httptest.NewRecorder()
   204  	ApplySecurityHeaders(successHandler).ServeHTTP(response, req)
   205  	for header, value := range expected {
   206  		got := response.Header().Get(header)
   207  		if got != value {
   208  			t.Fatalf("incorrect security header received for %s: expected %s got %s", header, value, got)
   209  		}
   210  	}
   211  }