| @ -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 | |||||
| } | |||||