Browse Source

redo api testing

Alexander Belanger 5 years ago
parent
commit
62fc7bd050

+ 3 - 4
cmd/app/main.go

@@ -18,7 +18,7 @@ import (
 )
 
 func main() {
-	appConf := config.AppConfig()
+	appConf := config.FromEnv()
 
 	logger := lr.NewConsole(appConf.Debug)
 	db, err := adapter.New(&appConf.Db)
@@ -30,12 +30,11 @@ func main() {
 
 	repo := gorm.NewRepository(db)
 
-	key := []byte("secret") // TODO: change to os.Getenv("SESSION_KEY")
-	store, _ := sessionstore.NewStore(repo, key)
+	store, _ := sessionstore.NewStore(repo, appConf.Server)
 
 	validator := vr.New()
 
-	a := api.New(logger, repo, validator, store)
+	a := api.New(logger, repo, validator, store, appConf.Server.CookieName)
 
 	appRouter := router.New(a)
 

+ 2 - 3
internal/auth/example/authExample.go

@@ -11,13 +11,12 @@ import (
 	sessionstore "github.com/porter-dev/porter/internal/auth"
 )
 
-var appConf = config.AppConfig()
+var appConf = config.FromEnv()
 
 var db, dbErr = dbConn.New(&appConf.Db)
 
 var (
-	key      = []byte("secret") // change to os.Getenv("SESSION_KEY")
-	store, _ = sessionstore.NewStore(gorm.NewRepository(db), key)
+	store, _ = sessionstore.NewStore(gorm.NewRepository(db), appConf.Server)
 )
 
 func secret(w http.ResponseWriter, r *http.Request) {

+ 5 - 1
internal/auth/sessionstore.go

@@ -9,6 +9,8 @@ import (
 	"strings"
 	"time"
 
+	"github.com/porter-dev/porter/internal/config"
+
 	"github.com/gorilla/securecookie"
 	"github.com/gorilla/sessions"
 	"github.com/pkg/errors"
@@ -108,7 +110,9 @@ func (store *PGStore) save(session *sessions.Session) error {
 // Implementation of the interface (Get, New, Save)
 
 // NewStore takes an initialized db and session key pairs to create a session-store in postgres db.
-func NewStore(repo *repository.Repository, keyPairs ...[]byte) (*PGStore, error) {
+func NewStore(repo *repository.Repository, conf config.ServerConf) (*PGStore, error) {
+	keyPairs := conf.CookieSecrets
+
 	dbStore := &PGStore{
 		Codecs: securecookie.CodecsFromPairs(keyPairs...),
 		Options: &sessions.Options{

+ 13 - 3
internal/auth/sessionstore_test.go

@@ -1,13 +1,17 @@
-package sessionstore
+package sessionstore_test
 
 import (
 	"encoding/base64"
 	"net/http"
 	"testing"
 
+	"github.com/porter-dev/porter/internal/config"
+
 	"github.com/gorilla/securecookie"
 	"github.com/gorilla/sessions"
 	"github.com/porter-dev/porter/internal/repository/test"
+
+	sessionstore "github.com/porter-dev/porter/internal/auth"
 )
 
 type headerOnlyResponseWriter http.Header
@@ -29,7 +33,10 @@ var secret = "secret"
 func TestPGStore(t *testing.T) {
 	repo := test.NewRepository(true)
 
-	ss, err := NewStore(repo, []byte(secret))
+	ss, err := sessionstore.NewStore(repo, config.ServerConf{
+		CookieSecrets: [][]byte{[]byte("secret")},
+	})
+
 	if err != nil {
 		t.Fatal("Failed to get store", err)
 	}
@@ -126,7 +133,10 @@ func TestPGStore(t *testing.T) {
 func TestSessionOptionsAreUniquePerSession(t *testing.T) {
 	repo := test.NewRepository(true)
 
-	ss, err := NewStore(repo, []byte(secret))
+	ss, err := sessionstore.NewStore(repo, config.ServerConf{
+		CookieSecrets: [][]byte{[]byte("secret")},
+	})
+
 	if err != nil {
 		t.Fatal("Failed to get store", err)
 	}

+ 18 - 11
internal/config/config.go

@@ -7,28 +7,35 @@ import (
 	"github.com/joeshaw/envdecode"
 )
 
+// Conf is the configuration for the Go server
 type Conf struct {
-	Debug  bool `env:"DEBUG,required"`
+	Debug  bool `env:"DEBUG,default=false"`
 	Server ServerConf
 	Db     DBConf
 }
 
+// ServerConf is the server configuration
 type ServerConf struct {
-	Port         int           `env:"SERVER_PORT,required"`
-	TimeoutRead  time.Duration `env:"SERVER_TIMEOUT_READ,required"`
-	TimeoutWrite time.Duration `env:"SERVER_TIMEOUT_WRITE,required"`
-	TimeoutIdle  time.Duration `env:"SERVER_TIMEOUT_IDLE,required"`
+	Port          int           `env:"SERVER_PORT,default=8080"`
+	CookieName    string        `env:"COOKIE_NAME,default=porter"`
+	CookieSecrets [][]byte      `env:"COOKIE_SECRETS,default=secret"`
+	TimeoutRead   time.Duration `env:"SERVER_TIMEOUT_READ,default=5s"`
+	TimeoutWrite  time.Duration `env:"SERVER_TIMEOUT_WRITE,default=10s"`
+	TimeoutIdle   time.Duration `env:"SERVER_TIMEOUT_IDLE,default=15s"`
 }
 
+// DBConf is the database configuration: if generated from environment variables,
+// it assumes the default docker-compose configuration is used
 type DBConf struct {
-	Host     string `env:"DB_HOST,required"`
-	Port     int    `env:"DB_PORT,required"`
-	Username string `env:"DB_USER,required"`
-	Password string `env:"DB_PASS,required"`
-	DbName   string `env:"DB_NAME,required"`
+	Host     string `env:"DB_HOST,default=postgres"`
+	Port     int    `env:"DB_PORT,default=5432"`
+	Username string `env:"DB_USER,default=porter"`
+	Password string `env:"DB_PASS,default=porter"`
+	DbName   string `env:"DB_NAME,default=porter"`
 }
 
-func AppConfig() *Conf {
+// FromEnv generates a configuration from environment variables
+func FromEnv() *Conf {
 	var c Conf
 
 	if err := envdecode.StrictDecode(&c); err != nil {

+ 0 - 3
internal/forms/user.go

@@ -50,9 +50,6 @@ func (luf *LoginUserForm) ToUser() (*models.User, error) {
 	}
 
 	return &models.User{
-		Model: gorm.Model{
-			ID: luf.ID,
-		},
 		Email:    luf.Email,
 		Password: string(hashed),
 	}, nil

+ 6 - 6
internal/kubernetes/kubeconfig.go

@@ -45,7 +45,7 @@ func GetAllowedClusterConfigsFromBytes(bytes []byte, allowedClusters []string) (
 		return nil, err
 	}
 
-	clusters := conf.ToAllowedClusterConfigs(allowedClusters)
+	clusters := conf.toAllowedClusterConfigs(allowedClusters)
 
 	return clusters, nil
 }
@@ -60,16 +60,16 @@ func GetAllClusterConfigsFromBytes(bytes []byte) ([]models.ClusterConfig, error)
 		return nil, err
 	}
 
-	clusters := conf.ToAllClusterConfigs()
+	clusters := conf.toAllClusterConfigs()
 
 	return clusters, nil
 }
 
-// ToAllowedClusterConfigs converts a KubeConfig to a set of ClusterConfigs by
+// toAllowedClusterConfigs converts a KubeConfig to a set of ClusterConfigs by
 // joining users and clusters on the context.
 //
 // It accepts a list of cluster names that the user wishes to connect to
-func (k *KubeConfig) ToAllowedClusterConfigs(allowedClusters []string) []models.ClusterConfig {
+func (k *KubeConfig) toAllowedClusterConfigs(allowedClusters []string) []models.ClusterConfig {
 	clusters := make([]models.ClusterConfig, 0)
 
 	// convert clusters, contexts, and users to maps for fast lookup
@@ -103,9 +103,9 @@ func (k *KubeConfig) ToAllowedClusterConfigs(allowedClusters []string) []models.
 	return clusters
 }
 
-// ToAllClusterConfigs converts a KubeConfig to a set of ClusterConfigs by
+// toAllClusterConfigs converts a KubeConfig to a set of ClusterConfigs by
 // joining users and clusters on the context.
-func (k *KubeConfig) ToAllClusterConfigs() []models.ClusterConfig {
+func (k *KubeConfig) toAllClusterConfigs() []models.ClusterConfig {
 	clusters := make([]models.ClusterConfig, 0)
 
 	// convert clusters, contexts, and users to maps for fast lookup

+ 42 - 24
internal/kubernetes/kubeconfig_test.go

@@ -6,7 +6,6 @@ import (
 
 	"github.com/porter-dev/porter/internal/kubernetes"
 	"github.com/porter-dev/porter/internal/models"
-	"gopkg.in/yaml.v2"
 )
 
 type KubeConfigTest struct {
@@ -55,18 +54,14 @@ var MissingFieldsTest = []KubeConfigTest{
 	},
 }
 
-func TestToClusterConfigsMissingFields(t *testing.T) {
+func TestMissingFields(t *testing.T) {
 	for _, c := range MissingFieldsTest {
-		// take raw and decode
-		conf := kubernetes.KubeConfig{}
-		err := yaml.Unmarshal(c.raw, &conf)
+		res, err := kubernetes.GetAllowedClusterConfigsFromBytes(c.raw, c.allowedClusters)
 
 		if err != nil {
-			t.Errorf("Testing: %s, Error: %v\n", c.msg, err)
+			t.Fatalf("Testing %s returned an error: %v\n", c.msg, err.Error())
 		}
 
-		res := conf.ToAllowedClusterConfigs(c.allowedClusters)
-
 		isEqual := reflect.DeepEqual(c.expected, res)
 
 		if !isEqual {
@@ -84,19 +79,14 @@ var NoAllowedClustersTests = []KubeConfigTest{
 	},
 }
 
-func TestToClusterConfigsNoAllowedClusters(t *testing.T) {
+func TestNoAllowedClusters(t *testing.T) {
 	for _, c := range NoAllowedClustersTests {
-		// take raw and decode
-
-		conf := kubernetes.KubeConfig{}
-		err := yaml.Unmarshal(c.raw, &conf)
+		res, err := kubernetes.GetAllowedClusterConfigsFromBytes(c.raw, c.allowedClusters)
 
 		if err != nil {
-			t.Errorf("Testing: %s, Error: %v\n", c.msg, err)
+			t.Fatalf("Testing %s returned an error: %v\n", c.msg, err.Error())
 		}
 
-		res := conf.ToAllowedClusterConfigs(c.allowedClusters)
-
 		isEqual := reflect.DeepEqual(c.expected, res)
 
 		if !isEqual {
@@ -105,7 +95,7 @@ func TestToClusterConfigsNoAllowedClusters(t *testing.T) {
 	}
 }
 
-var BasicClustersTests = []KubeConfigTest{
+var BasicClustersAllowedTests = []KubeConfigTest{
 	KubeConfigTest{
 		msg:             "basic test",
 		raw:             []byte(basic),
@@ -121,17 +111,45 @@ var BasicClustersTests = []KubeConfigTest{
 	},
 }
 
-func TestToClusterConfigsBasic(t *testing.T) {
-	for _, c := range BasicClustersTests {
-		// take raw and decode
-		conf := kubernetes.KubeConfig{}
-		err := yaml.Unmarshal(c.raw, &conf)
+func TestBasicAllowed(t *testing.T) {
+	for _, c := range BasicClustersAllowedTests {
+		res, err := kubernetes.GetAllowedClusterConfigsFromBytes(c.raw, c.allowedClusters)
 
 		if err != nil {
-			t.Errorf("Testing: %s, Error: %v\n", c.msg, err)
+			t.Fatalf("Testing %s returned an error: %v\n", c.msg, err.Error())
 		}
 
-		res := conf.ToAllowedClusterConfigs(c.allowedClusters)
+		isEqual := reflect.DeepEqual(c.expected, res)
+
+		if !isEqual {
+			t.Errorf("Testing: %s, Expected: %v, Got: %v\n", c.msg, c.expected, res)
+		}
+	}
+}
+
+var BasicClustersAllTests = []KubeConfigTest{
+	KubeConfigTest{
+		msg:             "basic test",
+		raw:             []byte(basic),
+		allowedClusters: []string{"cluster-test"},
+		expected: []models.ClusterConfig{
+			models.ClusterConfig{
+				Name:    "cluster-test",
+				Server:  "https://localhost",
+				Context: "context-test",
+				User:    "test-admin",
+			},
+		},
+	},
+}
+
+func TestBasicAll(t *testing.T) {
+	for _, c := range BasicClustersAllTests {
+		res, err := kubernetes.GetAllClusterConfigsFromBytes(c.raw)
+
+		if err != nil {
+			t.Fatalf("Testing %s returned an error: %v\n", c.msg, err.Error())
+		}
 
 		isEqual := reflect.DeepEqual(c.expected, res)
 

+ 3 - 0
server/api/api.go

@@ -17,6 +17,7 @@ type App struct {
 	validator  *validator.Validate
 	store      *sessionstore.PGStore
 	translator *ut.Translator
+	cookieName string
 }
 
 // New returns a new App instance
@@ -25,6 +26,7 @@ func New(
 	repo *repository.Repository,
 	validator *validator.Validate,
 	store *sessionstore.PGStore,
+	cookieName string,
 ) *App {
 	// for now, will just support the english translator from the
 	// validator/translations package
@@ -38,6 +40,7 @@ func New(
 		validator:  validator,
 		store:      store,
 		translator: &trans,
+		cookieName: cookieName,
 	}
 }
 

+ 16 - 1
server/api/user_handler.go

@@ -28,6 +28,12 @@ const (
 // HandleCreateUser validates a user form entry, converts the user to a gorm
 // model, and saves the user to the database
 func (app *App) HandleCreateUser(w http.ResponseWriter, r *http.Request) {
+	session, err := app.store.Get(r, app.cookieName)
+
+	if err != nil {
+		app.handleErrorDataRead(err, ErrUserDataRead, w)
+	}
+
 	form := &forms.CreateUserForm{}
 
 	user, err := app.writeUser(
@@ -40,13 +46,21 @@ func (app *App) HandleCreateUser(w http.ResponseWriter, r *http.Request) {
 
 	if err == nil {
 		app.logger.Info().Msgf("New user created: %d", user.ID)
+		session.Values["authenticated"] = true
+		session.Values["user_id"] = user.ID
+		session.Save(r, w)
 		w.WriteHeader(http.StatusCreated)
 	}
 }
 
 // HandleLoginUser checks the request header for cookie and validates the user.
 func (app *App) HandleLoginUser(w http.ResponseWriter, r *http.Request) {
-	session, _ := app.store.Get(r, "cookie-name")
+	session, err := app.store.Get(r, app.cookieName)
+
+	if err != nil {
+		app.handleErrorDataRead(err, ErrUserDataRead, w)
+	}
+
 	form := &forms.LoginUserForm{}
 
 	// decode from JSON to form value
@@ -77,6 +91,7 @@ func (app *App) HandleLoginUser(w http.ResponseWriter, r *http.Request) {
 
 	// Set user as authenticated
 	session.Values["authenticated"] = true
+	session.Values["user_id"] = storedUser.ID
 	session.Save(r, w)
 	w.WriteHeader(http.StatusOK)
 }

+ 325 - 246
server/api/user_handler_test.go

@@ -22,78 +22,154 @@ import (
 	vr "github.com/porter-dev/porter/internal/validator"
 )
 
-func initApi(canQuery bool) (*api.App, *repository.Repository) {
-	appConf := config.Conf{
-		Debug: true,
-		Server: config.ServerConf{
-			Port:         8080,
-			TimeoutRead:  time.Second * 5,
-			TimeoutWrite: time.Second * 10,
-			TimeoutIdle:  time.Second * 15,
-		},
-		// unimportant
-		Db: config.DBConf{},
-	}
-
-	logger := lr.NewConsole(appConf.Debug)
-	validator := vr.New()
+type tester struct {
+	app    *api.App
+	repo   *repository.Repository
+	store  *sessionstore.PGStore
+	router *chi.Mux
+	req    *http.Request
+	rr     *httptest.ResponseRecorder
+	cookie *http.Cookie
+}
 
-	repo := test.NewRepository(canQuery)
+type userTest struct {
+	initializers []func(t *tester)
+	msg          string
+	method       string
+	endpoint     string
+	body         string
+	expStatus    int
+	expBody      string
+	useCookie    bool
+	validators   []func(c *userTest, tester *tester, t *testing.T)
+}
 
-	key := []byte("secret") // TODO: change to os.Getenv("SESSION_KEY")
-	store, _ := sessionstore.NewStore(repo, key)
+func (t *tester) execute() {
+	t.router.ServeHTTP(t.rr, t.req)
+}
 
-	return api.New(logger, repo, validator, store), repo
+func (t *tester) reset() {
+	t.rr = httptest.NewRecorder()
+	t.req = nil
 }
 
-func testUserRequest(t *testing.T, c userTest) {
-	// create a mock API
-	api, repo := initApi(c.canQuery)
-	r := router.New(api)
+func (t *tester) createUserSession(email string, pw string) {
+	req, _ := http.NewRequest(
+		"POST",
+		"/api/users",
+		strings.NewReader(`{"email":"`+email+`","password":"`+pw+`"}`),
+	)
 
-	if c.init != nil {
-		c.init(repo)
+	t.req = req
+	t.execute()
+
+	if cookies := t.rr.Result().Cookies(); len(cookies) > 0 {
+		t.cookie = cookies[0]
 	}
 
-	req, err := http.NewRequest(
-		c.method,
-		c.endpoint,
-		strings.NewReader(c.body),
-	)
+	t.reset()
+}
 
-	if err != nil {
-		t.Fatal(err)
+func initUserDefault(tester *tester) {
+	tester.createUserSession("belanger@getporter.dev", "hello")
+}
+
+func initUserWithClusters(tester *tester) {
+	initUserDefault(tester)
+
+	user, _ := tester.repo.User.ReadUserByEmail("belanger@getporter.dev")
+	user.Clusters = []models.ClusterConfig{
+		models.ClusterConfig{
+			Name:    "cluster-test",
+			Server:  "https://localhost",
+			Context: "context-test",
+			User:    "test-admin",
+		},
 	}
+	user.RawKubeConfig = []byte("apiVersion: v1\nkind: Config\npreferences: {}\ncurrent-context: default\nclusters:\n- cluster:\n    server: https://localhost\n  name: cluster-test\ncontexts:\n- context:\n    cluster: cluster-test\n    user: test-admin\n  name: context-test\nusers:\n- name: test-admin")
 
-	rr := httptest.NewRecorder()
-	r.ServeHTTP(rr, req)
+	tester.repo.User.UpdateUser(user)
+}
 
-	// first, check that the status matches
-	if status := rr.Code; status != c.expStatus {
-		t.Errorf("%s, handler returned wrong status code: got %v want %v",
-			c.msg, status, c.expStatus)
+func newTester(canQuery bool) *tester {
+	appConf := config.Conf{
+		Debug: true,
+		Server: config.ServerConf{
+			Port:          8080,
+			CookieName:    "porter",
+			CookieSecrets: [][]byte{[]byte("secret")},
+			TimeoutRead:   time.Second * 5,
+			TimeoutWrite:  time.Second * 10,
+			TimeoutIdle:   time.Second * 15,
+		},
+		// unimportant here
+		Db: config.DBConf{},
 	}
 
-	// if there's a validator, call it
-	for _, validate := range c.validators {
-		validate(rr, c, r, t)
+	logger := lr.NewConsole(appConf.Debug)
+	validator := vr.New()
+
+	repo := test.NewRepository(canQuery)
+
+	store, _ := sessionstore.NewStore(repo, appConf.Server)
+	app := api.New(logger, repo, validator, store, appConf.Server.CookieName)
+	r := router.New(app)
+
+	return &tester{
+		app:    app,
+		repo:   repo,
+		store:  store,
+		router: r,
+		req:    nil,
+		rr:     httptest.NewRecorder(),
+		cookie: nil,
 	}
 }
 
-type userTest struct {
-	init func(repo *repository.Repository)
-	msg,
-	method,
-	endpoint,
-	body string
-	expStatus  int
-	expBody    string
-	canQuery   bool
-	validators []func(rr *httptest.ResponseRecorder, c userTest, r *chi.Mux, t *testing.T)
+func testUserRequests(t *testing.T, tests []*userTest, canQuery bool) {
+	for _, c := range tests {
+		// create a new tester
+		tester := newTester(canQuery)
+
+		// if there's an initializer, call it
+		for _, init := range c.initializers {
+			init(tester)
+		}
+
+		req, err := http.NewRequest(
+			c.method,
+			c.endpoint,
+			strings.NewReader(c.body),
+		)
+
+		tester.req = req
+
+		if c.useCookie {
+			req.AddCookie(tester.cookie)
+		}
+
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		tester.execute()
+		rr := tester.rr
+
+		// first, check that the status matches
+		if status := rr.Code; status != c.expStatus {
+			t.Errorf("%s, handler returned wrong status code: got %v want %v",
+				c.msg, status, c.expStatus)
+		}
+
+		// if there's a validator, call it
+		for _, validate := range c.validators {
+			validate(c, tester, t)
+		}
+	}
 }
 
-var createUserTests = []userTest{
-	userTest{
+var createUserTests = []*userTest{
+	&userTest{
 		msg:      "Create user",
 		method:   "POST",
 		endpoint: "/api/users",
@@ -103,9 +179,8 @@ var createUserTests = []userTest{
 		}`,
 		expStatus: http.StatusCreated,
 		expBody:   "",
-		canQuery:  true,
 	},
-	userTest{
+	&userTest{
 		msg:      "Create user invalid email",
 		method:   "POST",
 		endpoint: "/api/users",
@@ -115,12 +190,11 @@ var createUserTests = []userTest{
 		}`,
 		expStatus: http.StatusUnprocessableEntity,
 		expBody:   `{"code":601,"errors":["email validation failed"]}`,
-		canQuery:  true,
-		validators: []func(rr *httptest.ResponseRecorder, c userTest, r *chi.Mux, t *testing.T){
+		validators: []func(c *userTest, tester *tester, t *testing.T){
 			BasicBodyValidator,
 		},
 	},
-	userTest{
+	&userTest{
 		msg:      "Create user missing field",
 		method:   "POST",
 		endpoint: "/api/users",
@@ -129,86 +203,146 @@ var createUserTests = []userTest{
 		}`,
 		expStatus: http.StatusUnprocessableEntity,
 		expBody:   `{"code":601,"errors":["required validation failed"]}`,
-		canQuery:  true,
-		validators: []func(rr *httptest.ResponseRecorder, c userTest, r *chi.Mux, t *testing.T){
+		validators: []func(c *userTest, tester *tester, t *testing.T){
 			BasicBodyValidator,
 		},
 	},
-	userTest{
-		msg:      "Create user db connection down",
+	&userTest{
+		initializers: []func(tester *tester){
+			initUserDefault,
+		},
+		msg:      "Create user same email",
 		method:   "POST",
 		endpoint: "/api/users",
 		body: `{
 			"email": "belanger@getporter.dev",
 			"password": "hello"
 		}`,
+		expStatus: http.StatusUnprocessableEntity,
+		expBody:   `{"code":601,"errors":["email already taken"]}`,
+		validators: []func(c *userTest, tester *tester, t *testing.T){
+			BasicBodyValidator,
+		},
+	},
+	&userTest{
+		msg:      "Create user invalid field type",
+		method:   "POST",
+		endpoint: "/api/users",
+		body: `{
+			"email": "belanger@getporter.dev",
+			"password": 0
+		}`,
+		expStatus: http.StatusBadRequest,
+		expBody:   `{"code":600,"errors":["could not process request"]}`,
+		validators: []func(c *userTest, tester *tester, t *testing.T){
+			BasicBodyValidator,
+		},
+	},
+}
+
+func TestHandleCreateUser(t *testing.T) {
+	testUserRequests(t, createUserTests, true)
+}
+
+var createUserTestsWriteFail = []*userTest{
+	&userTest{
+		msg:      "Create user db connection down",
+		method:   "POST",
+		endpoint: "/api/users",
+		body: `{
+		"email": "belanger@getporter.dev",
+		"password": "hello"
+	}`,
 		expStatus: http.StatusInternalServerError,
 		expBody:   `{"code":500,"errors":["could not read from database"]}`,
-		canQuery:  false,
-		validators: []func(rr *httptest.ResponseRecorder, c userTest, r *chi.Mux, t *testing.T){
+		validators: []func(c *userTest, tester *tester, t *testing.T){
 			BasicBodyValidator,
 		},
 	},
-	userTest{
-		init: func(repo *repository.Repository) {
-			repo.User.CreateUser(&models.User{
-				Email:    "belanger@getporter.dev",
-				Password: "hello",
-			})
+}
+
+func TestHandleCreateUserWriteFail(t *testing.T) {
+	testUserRequests(t, createUserTestsWriteFail, false)
+}
+
+var loginUserTests = []*userTest{
+	&userTest{
+		initializers: []func(tester *tester){
+			initUserDefault,
 		},
-		msg:      "Create user same email",
+		msg:      "Login user successful",
 		method:   "POST",
-		endpoint: "/api/users",
+		endpoint: "/api/login",
 		body: `{
 			"email": "belanger@getporter.dev",
 			"password": "hello"
 		}`,
-		expStatus: http.StatusUnprocessableEntity,
-		expBody:   `{"code":601,"errors":["email already taken"]}`,
-		canQuery:  true,
-		validators: []func(rr *httptest.ResponseRecorder, c userTest, r *chi.Mux, t *testing.T){
+		expStatus: http.StatusOK,
+		expBody:   ``,
+		validators: []func(c *userTest, tester *tester, t *testing.T){
 			BasicBodyValidator,
 		},
 	},
-	userTest{
-		msg:      "Create user invalid field type",
+	&userTest{
+		initializers: []func(tester *tester){
+			initUserDefault,
+		},
+		msg:      "Login user already logged in",
 		method:   "POST",
-		endpoint: "/api/users",
+		endpoint: "/api/login",
 		body: `{
 			"email": "belanger@getporter.dev",
-			"password": 0
+			"password": "hello"
 		}`,
-		expStatus: http.StatusBadRequest,
-		expBody:   `{"code":600,"errors":["could not process request"]}`,
-		canQuery:  true,
-		validators: []func(rr *httptest.ResponseRecorder, c userTest, r *chi.Mux, t *testing.T){
+		expStatus: http.StatusOK,
+		expBody:   ``,
+		useCookie: true,
+		validators: []func(c *userTest, tester *tester, t *testing.T){
+			BasicBodyValidator,
+		},
+	},
+	&userTest{
+		msg:      "Login user unregistered email",
+		method:   "POST",
+		endpoint: "/api/login",
+		body: `{
+			"email": "belanger@getporter.dev",
+			"password": "hello"
+		}`,
+		expStatus: http.StatusUnauthorized,
+		expBody:   `{"code":401,"errors":["email not registered"]}`,
+		validators: []func(c *userTest, tester *tester, t *testing.T){
+			BasicBodyValidator,
+		},
+	},
+	&userTest{
+		initializers: []func(tester *tester){
+			initUserDefault,
+		},
+		msg:      "Login user incorrect password",
+		method:   "POST",
+		endpoint: "/api/login",
+		body: `{
+			"email": "belanger@getporter.dev",
+			"password": "notthepassword"
+		}`,
+		expStatus: http.StatusUnauthorized,
+		expBody:   `{"code":401,"errors":["incorrect password"]}`,
+		useCookie: true,
+		validators: []func(c *userTest, tester *tester, t *testing.T){
 			BasicBodyValidator,
 		},
 	},
 }
 
-func TestHandleCreateUser(t *testing.T) {
-	for _, c := range createUserTests {
-		testUserRequest(t, c)
-	}
+func TestHandleLoginUser(t *testing.T) {
+	testUserRequests(t, loginUserTests, true)
 }
 
-var readUserTests = []userTest{
-	userTest{
-		init: func(repo *repository.Repository) {
-			repo.User.CreateUser(&models.User{
-				Email:    "belanger@getporter.dev",
-				Password: "hello",
-				Clusters: []models.ClusterConfig{
-					models.ClusterConfig{
-						Name:    "cluster-test",
-						Server:  "https://localhost",
-						Context: "context-test",
-						User:    "test-admin",
-					},
-				},
-				RawKubeConfig: []byte("apiVersion: v1\nkind: Config\npreferences: {}\ncurrent-context: default\nclusters:\n- cluster:\n    server: https://localhost\n  name: cluster-test\ncontexts:\n- context:\n    cluster: cluster-test\n    user: test-admin\n  name: context-test\nusers:\n- name: test-admin"),
-			})
+var readUserTests = []*userTest{
+	&userTest{
+		initializers: []func(tester *tester){
+			initUserWithClusters,
 		},
 		msg:       "Read user successful",
 		method:    "GET",
@@ -216,17 +350,13 @@ var readUserTests = []userTest{
 		body:      "",
 		expStatus: http.StatusOK,
 		expBody:   `{"id":1,"email":"belanger@getporter.dev","clusters":[{"name":"cluster-test","server":"https://localhost","context":"context-test","user":"test-admin"}],"rawKubeConfig":"apiVersion: v1\nkind: Config\npreferences: {}\ncurrent-context: default\nclusters:\n- cluster:\n    server: https://localhost\n  name: cluster-test\ncontexts:\n- context:\n    cluster: cluster-test\n    user: test-admin\n  name: context-test\nusers:\n- name: test-admin"}`,
-		canQuery:  true,
-		validators: []func(rr *httptest.ResponseRecorder, c userTest, r *chi.Mux, t *testing.T){
+		validators: []func(c *userTest, tester *tester, t *testing.T){
 			UserModelBodyValidator,
 		},
 	},
-	userTest{
-		init: func(repo *repository.Repository) {
-			repo.User.CreateUser(&models.User{
-				Email:    "belanger@getporter.dev",
-				Password: "hello",
-			})
+	&userTest{
+		initializers: []func(tester *tester){
+			initUserDefault,
 		},
 		msg:       "Read user bad id field",
 		method:    "GET",
@@ -234,17 +364,13 @@ var readUserTests = []userTest{
 		body:      "",
 		expStatus: http.StatusBadRequest,
 		expBody:   `{"code":600,"errors":["could not process request"]}`,
-		canQuery:  true,
-		validators: []func(rr *httptest.ResponseRecorder, c userTest, r *chi.Mux, t *testing.T){
+		validators: []func(c *userTest, tester *tester, t *testing.T){
 			BasicBodyValidator,
 		},
 	},
-	userTest{
-		init: func(repo *repository.Repository) {
-			repo.User.CreateUser(&models.User{
-				Email:    "belanger@getporter.dev",
-				Password: "hello",
-			})
+	&userTest{
+		initializers: []func(tester *tester){
+			initUserDefault,
 		},
 		msg:       "Read user not found",
 		method:    "GET",
@@ -252,35 +378,20 @@ var readUserTests = []userTest{
 		body:      "",
 		expStatus: http.StatusNotFound,
 		expBody:   `{"code":602,"errors":["could not find requested object"]}`,
-		canQuery:  true,
-		validators: []func(rr *httptest.ResponseRecorder, c userTest, r *chi.Mux, t *testing.T){
+		validators: []func(c *userTest, tester *tester, t *testing.T){
 			BasicBodyValidator,
 		},
 	},
 }
 
 func TestHandleReadUser(t *testing.T) {
-	for _, c := range readUserTests {
-		testUserRequest(t, c)
-	}
+	testUserRequests(t, readUserTests, true)
 }
 
-var readUserClustersTests = []userTest{
-	userTest{
-		init: func(repo *repository.Repository) {
-			repo.User.CreateUser(&models.User{
-				Email:    "belanger@getporter.dev",
-				Password: "hello",
-				Clusters: []models.ClusterConfig{
-					models.ClusterConfig{
-						Name:    "cluster-test",
-						Server:  "https://localhost",
-						Context: "context-test",
-						User:    "test-admin",
-					},
-				},
-				RawKubeConfig: []byte("apiVersion: v1\nkind: Config\npreferences: {}\ncurrent-context: default\nclusters:\n- cluster:\n    server: https://localhost\n  name: cluster-test\ncontexts:\n- context:\n    cluster: cluster-test\n    user: test-admin\n  name: context-test\nusers:\n- name: test-admin"),
-			})
+var readUserClustersTests = []*userTest{
+	&userTest{
+		initializers: []func(tester *tester){
+			initUserWithClusters,
 		},
 		msg:       "Read user successful",
 		method:    "GET",
@@ -288,28 +399,20 @@ var readUserClustersTests = []userTest{
 		body:      "",
 		expStatus: http.StatusOK,
 		expBody:   `[{"name":"cluster-test","server":"https://localhost","context":"context-test","user":"test-admin"}]`,
-		canQuery:  true,
-		validators: []func(rr *httptest.ResponseRecorder, c userTest, r *chi.Mux, t *testing.T){
+		validators: []func(c *userTest, tester *tester, t *testing.T){
 			ClusterBodyValidator,
 		},
 	},
 }
 
 func TestHandleReadUserClusters(t *testing.T) {
-	for _, c := range readUserClustersTests {
-		testUserRequest(t, c)
-	}
+	testUserRequests(t, readUserClustersTests, true)
 }
 
-var readUserClustersAllTests = []userTest{
-	userTest{
-		init: func(repo *repository.Repository) {
-			repo.User.CreateUser(&models.User{
-				Email:         "belanger@getporter.dev",
-				Password:      "hello",
-				Clusters:      []models.ClusterConfig{},
-				RawKubeConfig: []byte("apiVersion: v1\nkind: Config\npreferences: {}\ncurrent-context: default\nclusters:\n- cluster:\n    server: https://localhost\n  name: cluster-test\ncontexts:\n- context:\n    cluster: cluster-test\n    user: test-admin\n  name: context-test\nusers:\n- name: test-admin"),
-			})
+var readUserClustersAllTests = []*userTest{
+	&userTest{
+		initializers: []func(tester *tester){
+			initUserWithClusters,
 		},
 		msg:       "Read user successful",
 		method:    "GET",
@@ -317,19 +420,23 @@ var readUserClustersAllTests = []userTest{
 		body:      "",
 		expStatus: http.StatusOK,
 		expBody:   `[{"name":"cluster-test","server":"https://localhost","context":"context-test","user":"test-admin"}]`,
-		canQuery:  true,
-		validators: []func(rr *httptest.ResponseRecorder, c userTest, r *chi.Mux, t *testing.T){
+		validators: []func(c *userTest, tester *tester, t *testing.T){
 			ClusterBodyValidator,
 		},
 	},
-	userTest{
-		init: func(repo *repository.Repository) {
-			repo.User.CreateUser(&models.User{
-				Email:         "belanger@getporter.dev",
-				Password:      "hello",
-				Clusters:      []models.ClusterConfig{},
-				RawKubeConfig: []byte("apiVersion: \xc5\n"),
-			})
+	&userTest{
+		initializers: []func(tester *tester){
+			initUserWithClusters,
+			func(tester *tester) {
+				initUserDefault(tester)
+
+				user, _ := tester.repo.User.ReadUserByEmail("belanger@getporter.dev")
+				user.Clusters = []models.ClusterConfig{}
+				user.RawKubeConfig = []byte("apiVersion: \xc5\n")
+
+				tester.repo.User.UpdateUser(user)
+
+			},
 		},
 		msg:       "Read user with invalid utf-8 \xc5 in kubeconfig",
 		method:    "GET",
@@ -337,26 +444,20 @@ var readUserClustersAllTests = []userTest{
 		body:      "",
 		expStatus: http.StatusBadRequest,
 		expBody:   `{"code":600,"errors":["could not process request"]}`,
-		canQuery:  true,
-		validators: []func(rr *httptest.ResponseRecorder, c userTest, r *chi.Mux, t *testing.T){
-			BasicBodyValidator,
+		validators: []func(c *userTest, tester *tester, t *testing.T){
+			ClusterBodyValidator,
 		},
 	},
 }
 
 func TestHandleReadUserClustersAll(t *testing.T) {
-	for _, c := range readUserClustersAllTests {
-		testUserRequest(t, c)
-	}
+	testUserRequests(t, readUserClustersAllTests, true)
 }
 
-var updateUserTests = []userTest{
-	userTest{
-		init: func(repo *repository.Repository) {
-			repo.User.CreateUser(&models.User{
-				Email:    "belanger@getporter.dev",
-				Password: "hello",
-			})
+var updateUserTests = []*userTest{
+	&userTest{
+		initializers: []func(tester *tester){
+			initUserDefault,
 		},
 		msg:       "Update user successful",
 		method:    "PUT",
@@ -364,9 +465,8 @@ var updateUserTests = []userTest{
 		body:      `{"rawKubeConfig":"apiVersion: v1\nkind: Config\npreferences: {}\ncurrent-context: default\nclusters:\n- cluster:\n    server: https://localhost\n  name: cluster-test\ncontexts:\n- context:\n    cluster: cluster-test\n    user: test-admin\n  name: context-test\nusers:\n- name: test-admin", "allowedClusters":[]}`,
 		expStatus: http.StatusNoContent,
 		expBody:   "",
-		canQuery:  true,
-		validators: []func(rr *httptest.ResponseRecorder, c userTest, r *chi.Mux, t *testing.T){
-			func(rr *httptest.ResponseRecorder, c userTest, r *chi.Mux, t *testing.T) {
+		validators: []func(c *userTest, tester *tester, t *testing.T){
+			func(c *userTest, tester *tester, t *testing.T) {
 				req, err := http.NewRequest(
 					"GET",
 					"/api/users/1",
@@ -378,7 +478,7 @@ var updateUserTests = []userTest{
 				}
 
 				rr2 := httptest.NewRecorder()
-				r.ServeHTTP(rr2, req)
+				tester.router.ServeHTTP(rr2, req)
 
 				gotBody := &models.UserExternal{}
 				expBody := &models.UserExternal{}
@@ -393,12 +493,9 @@ var updateUserTests = []userTest{
 			},
 		},
 	},
-	userTest{
-		init: func(repo *repository.Repository) {
-			repo.User.CreateUser(&models.User{
-				Email:    "belanger@getporter.dev",
-				Password: "hello",
-			})
+	&userTest{
+		initializers: []func(tester *tester){
+			initUserDefault,
 		},
 		msg:       "Update user invalid id",
 		method:    "PUT",
@@ -406,17 +503,13 @@ var updateUserTests = []userTest{
 		body:      `{"rawKubeConfig":"apiVersion: v1\nkind: Config\npreferences: {}\ncurrent-context: default\nclusters:\n- cluster:\n    server: https://localhost\n  name: cluster-test\ncontexts:\n- context:\n    cluster: cluster-test\n    user: test-admin\n  name: context-test\nusers:\n- name: test-admin", "allowedClusters":[]}`,
 		expStatus: http.StatusBadRequest,
 		expBody:   `{"code":600,"errors":["could not process request"]}`,
-		canQuery:  true,
-		validators: []func(rr *httptest.ResponseRecorder, c userTest, r *chi.Mux, t *testing.T){
+		validators: []func(c *userTest, tester *tester, t *testing.T){
 			BasicBodyValidator,
 		},
 	},
-	userTest{
-		init: func(repo *repository.Repository) {
-			repo.User.CreateUser(&models.User{
-				Email:    "belanger@getporter.dev",
-				Password: "hello",
-			})
+	&userTest{
+		initializers: []func(tester *tester){
+			initUserDefault,
 		},
 		msg:       "Update user bad kubeconfig",
 		method:    "PUT",
@@ -424,17 +517,20 @@ var updateUserTests = []userTest{
 		body:      `{"rawKubeConfig":"notvalidyaml", "allowedClusters":[]}`,
 		expStatus: http.StatusBadRequest,
 		expBody:   `{"code":600,"errors":["could not process request"]}`,
-		canQuery:  true,
-		validators: []func(rr *httptest.ResponseRecorder, c userTest, r *chi.Mux, t *testing.T){
+		validators: []func(c *userTest, tester *tester, t *testing.T){
 			BasicBodyValidator,
 		},
 	},
-	userTest{
-		init: func(repo *repository.Repository) {
-			repo.User.CreateUser(&models.User{
-				Email:    "belanger@getporter.dev",
-				Password: "hello",
-			})
+}
+
+func TestHandleUpdateUser(t *testing.T) {
+	testUserRequests(t, updateUserTests, true)
+}
+
+var updateUserTestsWriteFail = []*userTest{
+	&userTest{
+		initializers: []func(tester *tester){
+			initUserDefault,
 		},
 		msg:       "Update user db connection down",
 		method:    "PUT",
@@ -442,26 +538,20 @@ var updateUserTests = []userTest{
 		body:      `{"rawKubeConfig":"apiVersion: v1\nkind: Config\npreferences: {}\ncurrent-context: default\nclusters:\n- cluster:\n    server: https://localhost\n  name: cluster-test\ncontexts:\n- context:\n    cluster: cluster-test\n    user: test-admin\n  name: context-test\nusers:\n- name: test-admin", "allowedClusters":[]}`,
 		expStatus: http.StatusInternalServerError,
 		expBody:   `{"code":500,"errors":["could not write to database"]}`,
-		canQuery:  false,
-		validators: []func(rr *httptest.ResponseRecorder, c userTest, r *chi.Mux, t *testing.T){
+		validators: []func(c *userTest, tester *tester, t *testing.T){
 			BasicBodyValidator,
 		},
 	},
 }
 
-func TestHandleUpdateUser(t *testing.T) {
-	for _, c := range updateUserTests {
-		testUserRequest(t, c)
-	}
+func TestHandleUpdateUserWriteFail(t *testing.T) {
+	testUserRequests(t, updateUserTestsWriteFail, false)
 }
 
-var deleteUserTests = []userTest{
-	userTest{
-		init: func(repo *repository.Repository) {
-			repo.User.CreateUser(&models.User{
-				Email:    "belanger@getporter.dev",
-				Password: "hello",
-			})
+var deleteUserTests = []*userTest{
+	&userTest{
+		initializers: []func(tester *tester){
+			initUserDefault,
 		},
 		msg:       "Delete user successful",
 		method:    "DELETE",
@@ -469,9 +559,8 @@ var deleteUserTests = []userTest{
 		body:      `{"password":"hello"}`,
 		expStatus: http.StatusNoContent,
 		expBody:   "",
-		canQuery:  true,
-		validators: []func(rr *httptest.ResponseRecorder, c userTest, r *chi.Mux, t *testing.T){
-			func(rr *httptest.ResponseRecorder, c userTest, r *chi.Mux, t *testing.T) {
+		validators: []func(c *userTest, tester *tester, t *testing.T){
+			func(c *userTest, tester *tester, t *testing.T) {
 				req, err := http.NewRequest(
 					"GET",
 					"/api/users/1",
@@ -484,7 +573,7 @@ var deleteUserTests = []userTest{
 
 				rr2 := httptest.NewRecorder()
 
-				r.ServeHTTP(rr2, req)
+				tester.router.ServeHTTP(rr2, req)
 
 				gotBody := &models.UserExternal{}
 				expBody := &models.UserExternal{}
@@ -504,12 +593,9 @@ var deleteUserTests = []userTest{
 			},
 		},
 	},
-	userTest{
-		init: func(repo *repository.Repository) {
-			repo.User.CreateUser(&models.User{
-				Email:    "belanger@getporter.dev",
-				Password: "hello",
-			})
+	&userTest{
+		initializers: []func(tester *tester){
+			initUserDefault,
 		},
 		msg:       "Delete user invalid id",
 		method:    "DELETE",
@@ -517,17 +603,13 @@ var deleteUserTests = []userTest{
 		body:      `{"password":"hello"}`,
 		expStatus: http.StatusBadRequest,
 		expBody:   `{"code":600,"errors":["could not process request"]}`,
-		canQuery:  true,
-		validators: []func(rr *httptest.ResponseRecorder, c userTest, r *chi.Mux, t *testing.T){
+		validators: []func(c *userTest, tester *tester, t *testing.T){
 			BasicBodyValidator,
 		},
 	},
-	userTest{
-		init: func(repo *repository.Repository) {
-			repo.User.CreateUser(&models.User{
-				Email:    "belanger@getporter.dev",
-				Password: "hello",
-			})
+	&userTest{
+		initializers: []func(tester *tester){
+			initUserDefault,
 		},
 		msg:       "Delete user missing password",
 		method:    "DELETE",
@@ -535,31 +617,28 @@ var deleteUserTests = []userTest{
 		body:      `{}`,
 		expStatus: http.StatusUnprocessableEntity,
 		expBody:   `{"code":601,"errors":["required validation failed"]}`,
-		canQuery:  true,
-		validators: []func(rr *httptest.ResponseRecorder, c userTest, r *chi.Mux, t *testing.T){
+		validators: []func(c *userTest, tester *tester, t *testing.T){
 			BasicBodyValidator,
 		},
 	},
 }
 
 func TestHandleDeleteUser(t *testing.T) {
-	for _, c := range deleteUserTests {
-		testUserRequest(t, c)
-	}
+	testUserRequests(t, deleteUserTests, true)
 }
 
-func BasicBodyValidator(rr *httptest.ResponseRecorder, c userTest, r *chi.Mux, t *testing.T) {
-	if body := rr.Body.String(); body != c.expBody {
+func BasicBodyValidator(c *userTest, tester *tester, t *testing.T) {
+	if body := tester.rr.Body.String(); body != c.expBody {
 		t.Errorf("%s, handler returned wrong body: got %v want %v",
 			c.msg, body, c.expBody)
 	}
 }
 
-func UserModelBodyValidator(rr *httptest.ResponseRecorder, c userTest, r *chi.Mux, t *testing.T) {
+func UserModelBodyValidator(c *userTest, tester *tester, t *testing.T) {
 	gotBody := &models.UserExternal{}
 	expBody := &models.UserExternal{}
 
-	json.Unmarshal(rr.Body.Bytes(), gotBody)
+	json.Unmarshal(tester.rr.Body.Bytes(), gotBody)
 	json.Unmarshal([]byte(c.expBody), expBody)
 
 	if !reflect.DeepEqual(gotBody, expBody) {
@@ -568,12 +647,12 @@ func UserModelBodyValidator(rr *httptest.ResponseRecorder, c userTest, r *chi.Mu
 	}
 }
 
-func ClusterBodyValidator(rr *httptest.ResponseRecorder, c userTest, r *chi.Mux, t *testing.T) {
+func ClusterBodyValidator(c *userTest, tester *tester, t *testing.T) {
 	// if status is expected to be 200, parse the body for UserExternal
 	gotBody := &[]models.ClusterConfigExternal{}
 	expBody := &[]models.ClusterConfigExternal{}
 
-	json.Unmarshal(rr.Body.Bytes(), gotBody)
+	json.Unmarshal(tester.rr.Body.Bytes(), gotBody)
 	json.Unmarshal([]byte(c.expBody), expBody)
 
 	if !reflect.DeepEqual(gotBody, expBody) {

+ 45 - 10
server/router/middleware/auth.go

@@ -2,19 +2,45 @@ package middleware
 
 import (
 	"net/http"
+	"strconv"
 
-	"github.com/gorilla/sessions"
+	"github.com/go-chi/chi"
+	sessionstore "github.com/porter-dev/porter/internal/auth"
 )
 
-var (
-	key   = []byte("secret")             // change to os.Getenv("SESSION_KEY")
-	store = sessions.NewCookieStore(key) // Swap out with custom store
-)
+type Auth struct {
+	store      *sessionstore.PGStore
+	cookieName string
+}
+
+func NewAuth(
+	store *sessionstore.PGStore,
+	cookieName string,
+) *Auth {
+	return &Auth{store, cookieName}
+}
+
+// BasicAuthenticate just checks that a user is logged in
+func (auth *Auth) BasicAuthenticate(next http.Handler) http.Handler {
+	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		if auth.isLoggedIn(r) {
+			next.ServeHTTP(w, r)
+		} else {
+			http.Error(w, http.StatusText(403), 403)
+			return
+		}
 
-// Authenticate is middleware for authentication
-func Authenticate(next http.Handler) http.Handler {
+		return
+	})
+}
+
+// DoesUserIDMatch checks the id URL parameter and verifies that it matches
+// the one stored in the session
+func (auth *Auth) DoesUserIDMatch(next http.Handler) http.Handler {
 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		if isLoggedIn(r) {
+		id, err := strconv.ParseUint(chi.URLParam(r, "id"), 0, 64)
+
+		if err == nil && auth.doesSessionMatchID(r, uint(id)) {
 			next.ServeHTTP(w, r)
 		} else {
 			http.Error(w, http.StatusText(403), 403)
@@ -26,9 +52,18 @@ func Authenticate(next http.Handler) http.Handler {
 }
 
 // Helpers
+func (auth *Auth) doesSessionMatchID(r *http.Request, id uint) bool {
+	session, _ := auth.store.Get(r, auth.cookieName)
+
+	if sessID, ok := session.Values["user_id"].(uint); !ok || sessID != id {
+		return false
+	}
+
+	return true
+}
 
-func isLoggedIn(r *http.Request) bool {
-	session, _ := store.Get(r, "session-id")
+func (auth *Auth) isLoggedIn(r *http.Request) bool {
+	session, _ := auth.store.Get(r, auth.cookieName)
 
 	if auth, ok := session.Values["authenticated"].(bool); !auth || !ok {
 		return false