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