Browse Source

Add Login and Logout routes

pull/2/head
Tovi Jaeschke-Rogers 3 years ago
parent
commit
946c4913fa
7 changed files with 281 additions and 1 deletions
  1. +65
    -0
      Api/Auth/Login.go
  2. +111
    -0
      Api/Auth/Login_test.go
  3. +34
    -0
      Api/Auth/Logout.go
  4. +51
    -0
      Api/Auth/Session.go
  5. +6
    -0
      Api/Routes.go
  6. +1
    -1
      Api/Users_test.go
  7. +13
    -0
      Database/Users.go

+ 65
- 0
Api/Auth/Login.go View File

@ -0,0 +1,65 @@
package Auth
import (
"encoding/json"
"net/http"
"time"
"git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Database"
"git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Models"
"github.com/gofrs/uuid"
)
type Credentials struct {
Email string `json:"email"`
Password string `json:"password"`
}
func Login(w http.ResponseWriter, r *http.Request) {
var (
creds Credentials
userData Models.User
sessionToken uuid.UUID
expiresAt time.Time
err error
)
err = json.NewDecoder(r.Body).Decode(&creds)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
userData, err = Database.GetUserByEmail(creds.Email)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
return
}
if !CheckPasswordHash(creds.Password, userData.Password) {
w.WriteHeader(http.StatusUnauthorized)
return
}
sessionToken, err = uuid.NewV4()
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
expiresAt = time.Now().Add(1 * time.Hour)
Sessions[sessionToken.String()] = Session{
Username: userData.Email,
Expiry: expiresAt,
}
http.SetCookie(w, &http.Cookie{
Name: "session_token",
Value: sessionToken.String(),
Expires: expiresAt,
})
w.WriteHeader(http.StatusOK)
}

+ 111
- 0
Api/Auth/Login_test.go View File

@ -0,0 +1,111 @@
package Auth
import (
"fmt"
"math/rand"
"net/http"
"net/http/httptest"
"os"
"path"
"runtime"
"strings"
"testing"
"time"
"git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Database"
"git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Models"
"github.com/gorilla/mux"
)
var (
r *mux.Router
letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
)
func init() {
// Fix working directory for tests
_, filename, _, _ := runtime.Caller(0)
dir := path.Join(path.Dir(filename), "..")
err := os.Chdir(dir)
if err != nil {
panic(err)
}
Database.InitTest()
r = mux.NewRouter()
}
func randString(n int) string {
b := make([]rune, n)
for i := range b {
b[i] = letterRunes[rand.Intn(len(letterRunes))]
}
return string(b)
}
func createTestUser(random bool) (Models.User, error) {
now := time.Now()
email := "email@email.com"
if random {
email = fmt.Sprintf("%s@email.com", randString(16))
}
password, err := HashPassword("password")
if err != nil {
return Models.User{}, err
}
userData := Models.User{
Email: email,
Password: password,
LastLogin: &now,
FirstName: "Hugh",
LastName: "Mann",
}
err = Database.CreateUser(&userData)
return userData, err
}
func Test_Login(t *testing.T) {
t.Log("Testing Login...")
r.HandleFunc("/admin/login", Login).Methods("POST")
ts := httptest.NewServer(r)
defer ts.Close()
userData, err := createTestUser(true)
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
t.FailNow()
}
postJson := `
{
"email": "%s",
"password": "password"
}
`
postJson = fmt.Sprintf(postJson, userData.Email)
res, err := http.Post(ts.URL+"/admin/login", "application/json", strings.NewReader(postJson))
if err != nil {
t.Errorf("Expected nil, recieved %s", err.Error())
return
}
if res.StatusCode != http.StatusOK {
t.Errorf("Expected %d, recieved %d", http.StatusOK, res.StatusCode)
return
}
if len(res.Cookies()) != 1 {
t.Errorf("Expected cookies len 1, recieved %d", len(res.Cookies()))
return
}
}

+ 34
- 0
Api/Auth/Logout.go View File

@ -0,0 +1,34 @@
package Auth
import (
"net/http"
"time"
)
func Logout(w http.ResponseWriter, r *http.Request) {
var (
c *http.Cookie
sessionToken string
err error
)
c, err = r.Cookie("session_token")
if err != nil {
if err == http.ErrNoCookie {
w.WriteHeader(http.StatusUnauthorized)
return
}
w.WriteHeader(http.StatusBadRequest)
return
}
sessionToken = c.Value
delete(Sessions, sessionToken)
http.SetCookie(w, &http.Cookie{
Name: "session_token",
Value: "",
Expires: time.Now(),
})
}

+ 51
- 0
Api/Auth/Session.go View File

@ -0,0 +1,51 @@
package Auth
import (
"errors"
"net/http"
"time"
)
var (
Sessions = map[string]Session{}
)
type Session struct {
Username string
Expiry time.Time
}
func (s Session) IsExpired() bool {
return s.Expiry.Before(time.Now())
}
func CheckCookie(r *http.Request) (Session, error) {
var (
c *http.Cookie
sessionToken string
userSession Session
exists bool
err error
)
c, err = r.Cookie("session_token")
if err != nil {
return userSession, err
}
sessionToken = c.Value
// We then get the session from our session map
userSession, exists = Sessions[sessionToken]
if !exists {
return userSession, errors.New("Cookie not found")
}
// If the session is present, but has expired, we can delete the session, and return
// an unauthorized status
if userSession.IsExpired() {
delete(Sessions, sessionToken)
return userSession, errors.New("Cookie expired")
}
return userSession, nil
}

+ 6
- 0
Api/Routes.go View File

@ -3,6 +3,8 @@ package Api
import (
"log"
"git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Api/Auth"
"github.com/gorilla/mux"
)
@ -34,6 +36,10 @@ func InitApiEndpoints() *mux.Router {
router.HandleFunc("/user/{userID}", updatePost).Methods("PUT")
router.HandleFunc("/user/{userID}", deletePost).Methods("DELETE")
// Define routes for authentication
router.HandleFunc("/admin/login", Auth.Login).Methods("POST")
router.HandleFunc("/admin/logout", Auth.Logout).Methods("GET")
//router.PathPrefix("/").Handler(http.StripPrefix("/images/", http.FileServer(http.Dir("./uploads"))))
return router


+ 1
- 1
Api/Users_test.go View File

@ -69,7 +69,7 @@ func createTestUser(random bool) (Models.User, error) {
}
func Test_getUser(t *testing.T) {
t.Log("Testing getPost...")
t.Log("Testing getUser...")
r.HandleFunc("/user/{userID}", getUser).Methods("GET")


+ 13
- 0
Database/Users.go View File

@ -24,6 +24,19 @@ func GetUserById(id string) (Models.User, error) {
return userData, err
}
func GetUserByEmail(email string) (Models.User, error) {
var (
userData Models.User
err error
)
err = DB.Preload(clause.Associations).
First(&userData, "email = ?", email).
Error
return userData, err
}
func GetUsers(page, pageSize int) ([]Models.User, error) {
var (
users []Models.User


Loading…
Cancel
Save