2017-05-06 21:21:39 +02:00
package userService
import (
"errors"
"github.com/ewhal/nyaa/db"
"github.com/ewhal/nyaa/model"
2017-05-09 19:23:21 +02:00
formStruct "github.com/ewhal/nyaa/service/user/form"
2017-05-06 21:37:10 +02:00
"github.com/ewhal/nyaa/util/modelHelper"
2017-05-12 12:40:31 +02:00
"github.com/ewhal/nyaa/util/timeHelper"
2017-05-09 19:23:21 +02:00
"github.com/gorilla/securecookie"
"golang.org/x/crypto/bcrypt"
Consistency, formatting, error checking, cleanup, and a couple bug fixes (#245)
* Checkpoint: it builds
The config, db, model, network, os, and public packages have had some
fixes to glaringly obvious flaws, dead code removed, and stylistic
changes.
* Style changes and old code removal in router
Router needs a lot of work done to its (lack of) error handling.
* Dead code removal and style changes
Now up to util/email/email.go. After I'm finished with the initial sweep
I'll go back and fix error handling and security issues. Then I'll fix
the broken API. Then I'll go through to add documentation and fix code
visibility.
* Finish dead code removal and style changes
Vendored libraries not touched. Everything still needs security fixes
and documentation. There's also one case of broken functionality.
* Fix accidental find-and-replace
* Style, error checking, saftey, bug fix changes
* Redo error checking erased during merge
* Re-add merge-erased fix. Make Safe safe.
2017-05-10 04:34:40 +02:00
"net/http"
2017-05-12 12:40:31 +02:00
"strconv"
"time"
2017-05-06 21:21:39 +02:00
)
2017-05-12 12:40:31 +02:00
const CookieName = "session"
// If you want to keep login cookies between restarts you need to make these permanent
2017-05-06 21:21:39 +02:00
var cookieHandler = securecookie . New (
securecookie . GenerateRandomKey ( 64 ) ,
securecookie . GenerateRandomKey ( 32 ) )
2017-05-12 12:40:31 +02:00
// 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 )
2017-05-06 21:21:39 +02:00
if err != nil {
2017-05-12 12:40:31 +02:00
return 0 , err
2017-05-06 21:21:39 +02:00
}
2017-05-12 12:40:31 +02:00
time_int , _ := strconv . ParseInt ( value [ "t" ] , 10 , 0 )
if timeHelper . IsExpired ( time . Unix ( time_int , 0 ) ) {
return 0 , errors . New ( "Cookie is expired" )
2017-05-06 21:21:39 +02:00
}
2017-05-12 12:40:31 +02:00
ret , err := strconv . ParseUint ( value [ "u" ] , 10 , 0 )
return uint ( ret ) , err
2017-05-06 21:21:39 +02:00
}
2017-05-12 12:40:31 +02:00
func EncodeCookie ( user_id uint ) ( string , error ) {
validUntil := timeHelper . FewDaysLater ( 7 ) // 1 week
2017-05-06 21:21:39 +02:00
value := map [ string ] string {
2017-05-12 12:40:31 +02:00
"u" : strconv . FormatUint ( uint64 ( user_id ) , 10 ) ,
"t" : strconv . FormatInt ( validUntil . Unix ( ) , 10 ) ,
2017-05-06 21:21:39 +02:00
}
2017-05-12 12:40:31 +02:00
return cookieHandler . Encode ( CookieName , value )
2017-05-06 21:21:39 +02:00
}
func ClearCookie ( w http . ResponseWriter ) ( int , error ) {
cookie := & http . Cookie {
2017-05-12 12:40:31 +02:00
Name : CookieName ,
2017-05-06 21:21:39 +02:00
Value : "" ,
Path : "/" ,
2017-05-12 12:40:31 +02:00
HttpOnly : true ,
2017-05-06 21:21:39 +02:00
MaxAge : - 1 ,
}
http . SetCookie ( w , cookie )
return http . StatusOK , nil
}
2017-05-12 12:40:31 +02:00
// SetCookieHandler sets the authentication cookie
2017-05-06 21:21:39 +02:00
func SetCookieHandler ( w http . ResponseWriter , email string , pass string ) ( int , error ) {
2017-05-12 12:40:31 +02:00
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" )
2017-05-12 08:42:15 +02:00
}
2017-05-12 12:40:31 +02:00
} else {
if db . ORM . Where ( "username = ?" , email ) . First ( & user ) . RecordNotFound ( ) {
return http . StatusNotFound , errors . New ( "User not found" )
2017-05-12 08:42:15 +02:00
}
2017-05-06 21:21:39 +02:00
}
2017-05-12 12:40:31 +02:00
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
2017-05-06 21:21:39 +02:00
}
// RegisterHanderFromForm sets cookie from a RegistrationForm.
2017-05-07 00:10:40 +02:00
func RegisterHanderFromForm ( w http . ResponseWriter , registrationForm formStruct . RegistrationForm ) ( int , error ) {
2017-05-10 21:16:30 +02:00
username := registrationForm . Username // email isn't set at this point
2017-05-06 21:21:39 +02:00
pass := registrationForm . Password
2017-05-10 21:16:30 +02:00
return SetCookieHandler ( w , username , pass )
2017-05-06 21:21:39 +02:00
}
// RegisterHandler sets a cookie when user registered.
func RegisterHandler ( w http . ResponseWriter , r * http . Request ) ( int , error ) {
2017-05-07 00:10:40 +02:00
var registrationForm formStruct . RegistrationForm
2017-05-06 21:21:39 +02:00
modelHelper . BindValueForm ( & registrationForm , r )
return RegisterHanderFromForm ( w , registrationForm )
}
2017-05-12 12:40:31 +02:00
// CurrentUser determines the current user from the request
2017-05-06 21:21:39 +02:00
func CurrentUser ( r * http . Request ) ( model . User , error ) {
var user model . User
2017-05-12 12:40:31 +02:00
var encoded string
encoded = r . Header . Get ( "X-Auth-Token" )
if len ( encoded ) == 0 {
// check cookie instead
cookie , err := r . Cookie ( CookieName )
2017-05-06 21:21:39 +02:00
if err != nil {
return user , err
}
2017-05-12 12:40:31 +02:00
encoded = cookie . Value
2017-05-06 21:21:39 +02:00
}
2017-05-12 12:40:31 +02:00
user_id , err := DecodeCookie ( encoded )
if err != nil {
return user , err
}
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" )
2017-05-06 21:21:39 +02:00
}
2017-05-12 12:40:31 +02:00
return user , nil
2017-05-06 21:21:39 +02:00
}