From bcaac0961f604b04cb9ac61e20556261e7f068b0 Mon Sep 17 00:00:00 2001 From: Eliot Whalan Date: Fri, 12 May 2017 16:42:15 +1000 Subject: [PATCH] Revert "Stateless cookies (#372)" This reverts commit 2f06fb8fa1403e127e7024d69138a589eaca0d53. --- config/trackers.go | 1 + model/user.go | 7 +- service/user/cookieHelper.go | 149 ++++++++++++++++------------------- service/user/user.go | 26 +++++- templates/FAQ.html | 1 + templates/user/login.html | 6 +- 6 files changed, 100 insertions(+), 90 deletions(-) diff --git a/config/trackers.go b/config/trackers.go index 4f38177f..8fdbb9b7 100644 --- a/config/trackers.go +++ b/config/trackers.go @@ -10,5 +10,6 @@ var Trackers = []string{ "udp://explodie.org:6969", "udp://tracker.opentrackr.org:1337", "udp://tracker.internetwarriors.net:1337/announce", + "udp://eddie4.nl:6969/announce", "http://mgtracker.org:6969/announce", "http://tracker.baka-sub.cf/announce"} diff --git a/model/user.go b/model/user.go index bf1fd654..1e1e3ca7 100644 --- a/model/user.go +++ b/model/user.go @@ -12,9 +12,8 @@ type User struct { Status int `gorm:"column:status"` CreatedAt time.Time `gorm:"column:created_at"` UpdatedAt time.Time `gorm:"column:updated_at"` - // Currently unused (auth is stateless now) - /*Token string `gorm:"column:api_token"` - TokenExpiration time.Time `gorm:"column:api_token_expiry"`*/ + Token string `gorm:"column:api_token"` + TokenExpiration time.Time `gorm:"column:api_token_expiry"` Language string `gorm:"column:language"` // TODO: move this to PublicUser @@ -43,7 +42,7 @@ func (u User) Size() (s int) { 4*3 + //time.Time 3*2 + // arrays // string arrays - len(u.Username) + len(u.Password) + len(u.Email) + len(u.MD5) + len(u.Language) + len(u.Username) + len(u.Password) + len(u.Email) + len(u.Token) + len(u.MD5) + len(u.Language) s *= 8 // Ignoring foreign key users. Fuck them. diff --git a/service/user/cookieHelper.go b/service/user/cookieHelper.go index 940d5b3d..b28e5a98 100644 --- a/service/user/cookieHelper.go +++ b/service/user/cookieHelper.go @@ -5,98 +5,96 @@ import ( "github.com/ewhal/nyaa/db" "github.com/ewhal/nyaa/model" formStruct "github.com/ewhal/nyaa/service/user/form" + "github.com/ewhal/nyaa/util/log" "github.com/ewhal/nyaa/util/modelHelper" - "github.com/ewhal/nyaa/util/timeHelper" "github.com/gorilla/securecookie" "golang.org/x/crypto/bcrypt" "net/http" - "strconv" - "time" ) -const CookieName = "session" - -// If you want to keep login cookies between restarts you need to make these permanent var cookieHandler = securecookie.New( securecookie.GenerateRandomKey(64), securecookie.GenerateRandomKey(32)) -// Encoding & Decoding of the cookie value -func DecodeCookie(cookie_value string) (uint, error) { - value := make(map[string]string) - err := cookieHandler.Decode(CookieName, cookie_value, &value) +func Token(r *http.Request) (string, error) { + var token string + cookie, err := r.Cookie("session") if err != nil { - return 0, err + return token, err } - time_int, _ := strconv.ParseInt(value["t"], 10, 0) - if timeHelper.IsExpired(time.Unix(time_int, 0)) { - return 0, errors.New("Cookie is expired") + cookieValue := make(map[string]string) + err = cookieHandler.Decode("session", cookie.Value, &cookieValue) + if err != nil { + return token, err } - ret, err := strconv.ParseUint(value["u"], 10, 0) - return uint(ret), err + token = cookieValue["token"] + if len(token) == 0 { + return token, errors.New("token is empty") + } + return token, nil } -func EncodeCookie(user_id uint) (string, error) { - validUntil := timeHelper.FewDaysLater(7) // 1 week +// SetCookie sets a cookie. +func SetCookie(w http.ResponseWriter, token string) (int, error) { value := map[string]string{ - "u": strconv.FormatUint(uint64(user_id), 10), - "t": strconv.FormatInt(validUntil.Unix(), 10), + "token": token, } - return cookieHandler.Encode(CookieName, value) + encoded, err := cookieHandler.Encode("session", value) + if err != nil { + return http.StatusInternalServerError, err + } + cookie := &http.Cookie{ + Name: "session", + Value: encoded, + Path: "/", + } + http.SetCookie(w, cookie) + return http.StatusOK, nil } +// ClearCookie clears a cookie. func ClearCookie(w http.ResponseWriter) (int, error) { cookie := &http.Cookie{ - Name: CookieName, + Name: "session", Value: "", Path: "/", - HttpOnly: true, MaxAge: -1, } http.SetCookie(w, cookie) return http.StatusOK, nil } -// SetCookieHandler sets the authentication cookie +// SetCookieHandler sets a cookie with email and password. func SetCookieHandler(w http.ResponseWriter, email string, pass string) (int, error) { - if email == "" || pass == "" { - return http.StatusNotFound, errors.New("No username/password entered") - } - - var user model.User - // search by email or username - isValidEmail, _ := formStruct.EmailValidation(email, formStruct.NewErrors()) - if isValidEmail { - if db.ORM.Where("email = ?", email).First(&user).RecordNotFound() { - return http.StatusNotFound, errors.New("User not found") + if email != "" && pass != "" { + var user model.User + isValidEmail, _ := formStruct.EmailValidation(email, formStruct.NewErrors()) + if isValidEmail { + log.Debug("User entered valid email.") + if db.ORM.Where("email = ?", email).First(&user).RecordNotFound() { + return http.StatusNotFound, errors.New("User not found") + } + } else { + log.Debug("User entered username.") + if db.ORM.Where("username = ?", email).First(&user).RecordNotFound() { + return http.StatusNotFound, errors.New("User not found") + } } - } else { - if db.ORM.Where("username = ?", email).First(&user).RecordNotFound() { - return http.StatusNotFound, errors.New("User not found") + err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(pass)) + if err != nil { + return http.StatusUnauthorized, errors.New("Password incorrect") } + if user.Status == -1 { + return http.StatusUnauthorized, errors.New("Account banned") + } + status, err := SetCookie(w, user.Token) + if err != nil { + return status, err + } + w.Header().Set("X-Auth-Token", user.Token) + return http.StatusOK, nil } - err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(pass)) - if err != nil { - return http.StatusUnauthorized, errors.New("Password incorrect") - } - if user.Status == -1 { - return http.StatusUnauthorized, errors.New("Account banned") - } - - encoded, err := EncodeCookie(user.ID) - if err != nil { - return http.StatusInternalServerError, err - } - cookie := &http.Cookie{ - Name: CookieName, - Value: encoded, - Path: "/", - HttpOnly: true, - } - http.SetCookie(w, cookie) - // also set response header for convenience - w.Header().Set("X-Auth-Token", encoded) - return http.StatusOK, nil + return http.StatusNotFound, errors.New("user not found") } // RegisterHanderFromForm sets cookie from a RegistrationForm. @@ -113,31 +111,24 @@ func RegisterHandler(w http.ResponseWriter, r *http.Request) (int, error) { return RegisterHanderFromForm(w, registrationForm) } -// CurrentUser determines the current user from the request +// CurrentUser get a current user. func CurrentUser(r *http.Request) (model.User, error) { var user model.User - var encoded string - - encoded = r.Header.Get("X-Auth-Token") - if len(encoded) == 0 { - // check cookie instead - cookie, err := r.Cookie(CookieName) + var token string + var err error + token = r.Header.Get("X-Auth-Token") + if len(token) > 0 { + log.Debug("header token exists") + } else { + token, err = Token(r) + log.Debug("header token does not exist") if err != nil { return user, err } - encoded = cookie.Value } - user_id, err := DecodeCookie(encoded) - if err != nil { - return user, err + if db.ORM.Where("api_token = ?", token).First(&user).RecordNotFound() { + return user, errors.New("user not found") } - if db.ORM.Where("user_id = ?", user_id).First(&user).RecordNotFound() { - return user, errors.New("User not found") - } - - if user.Status == -1 { - // recheck as user might've been banned in the meantime - return user, errors.New("Account banned") - } - return user, nil + err = db.ORM.Model(&user).Error + return user, err } diff --git a/service/user/user.go b/service/user/user.go index 27100c40..88b2ada6 100644 --- a/service/user/user.go +++ b/service/user/user.go @@ -7,6 +7,7 @@ import ( "strconv" "time" + "github.com/ewhal/nyaa/config" "github.com/ewhal/nyaa/db" "github.com/ewhal/nyaa/model" formStruct "github.com/ewhal/nyaa/service/user/form" @@ -14,6 +15,7 @@ import ( "github.com/ewhal/nyaa/util/crypto" "github.com/ewhal/nyaa/util/log" "github.com/ewhal/nyaa/util/modelHelper" + "github.com/ewhal/nyaa/util/timeHelper" "golang.org/x/crypto/bcrypt" ) @@ -67,8 +69,15 @@ func CreateUserFromForm(registrationForm formStruct.RegistrationForm) (model.Use return user, err } } + token, err := crypto.GenerateRandomToken32() + if err != nil { + return user, errors.New("token not generated") + } user.Email = "" // unset email because it will be verified later + user.Token = token + user.TokenExpiration = timeHelper.FewDaysLater(config.AuthTokenExpirationDay) + log.Debugf("user %+v\n", user) if db.ORM.Create(&user).Error != nil { return user, errors.New("user not created") } @@ -148,13 +157,17 @@ func UpdateUserCore(user *model.User) (int, error) { } } - user.UpdatedAt = time.Now() - err := db.ORM.Save(user).Error + token, err := crypto.GenerateRandomToken32() if err != nil { return http.StatusInternalServerError, err } + user.Token = token + user.TokenExpiration = timeHelper.FewDaysLater(config.AuthTokenExpirationDay) + if db.ORM.Save(user).Error != nil { + return http.StatusInternalServerError, err + } - + user.UpdatedAt = time.Now() return http.StatusOK, nil } @@ -184,13 +197,18 @@ func UpdateUser(w http.ResponseWriter, form *formStruct.UserForm, currentUser *m form.Username = user.Username } if (form.Email != user.Email) { - // send verification to new email and keep old SendVerificationToUser(user, form.Email) form.Email = user.Email } log.Debugf("form %+v\n", form) modelHelper.AssignValue(&user, form) status, err := UpdateUserCore(&user) + if err != nil { + return user, status, err + } + if userPermission.CurrentUserIdentical(currentUser, user.ID) { + status, err = SetCookie(w, user.Token) + } return user, status, err } diff --git a/templates/FAQ.html b/templates/FAQ.html index f511f968..b0a58191 100644 --- a/templates/FAQ.html +++ b/templates/FAQ.html @@ -45,6 +45,7 @@ udp://tracker.leechers-paradise.org:6969 udp://explodie.org:6969 udp://tracker.opentrackr.org:1337 udp://tracker.internetwarriors.net:1337/announce +udp://eddie4.nl:6969/announce http://mgtracker.org:6969/announce http://tracker.baka-sub.cf/announce diff --git a/templates/user/login.html b/templates/user/login.html index 1721b96c..6e3e4f2a 100644 --- a/templates/user/login.html +++ b/templates/user/login.html @@ -1,4 +1,4 @@ -{{define "title"}}{{ T "sign_in_title" }}{{end}} +{{define "title"}}{{ T "sign_in_title" }}{{end}} {{define "contclass"}}cont-view{{end}} {{define "content"}}
@@ -24,8 +24,8 @@ {{end}}
- + + {{ T "forgot_password"}}