@ -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) | |||||
} |
@ -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 | |||||
} | |||||
} |
@ -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(), | |||||
}) | |||||
} |
@ -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 | |||||
} |