Merge remote-tracking branch 'upstream/master'
Cette révision appartient à :
révision
9ba3f3c409
9 fichiers modifiés avec 105 ajouts et 91 suppressions
|
@ -10,5 +10,6 @@ var Trackers = []string{
|
||||||
"udp://explodie.org:6969",
|
"udp://explodie.org:6969",
|
||||||
"udp://tracker.opentrackr.org:1337",
|
"udp://tracker.opentrackr.org:1337",
|
||||||
"udp://tracker.internetwarriors.net:1337/announce",
|
"udp://tracker.internetwarriors.net:1337/announce",
|
||||||
|
"udp://eddie4.nl:6969/announce",
|
||||||
"http://mgtracker.org:6969/announce",
|
"http://mgtracker.org:6969/announce",
|
||||||
"http://tracker.baka-sub.cf/announce"}
|
"http://tracker.baka-sub.cf/announce"}
|
||||||
|
|
|
@ -12,9 +12,8 @@ type User struct {
|
||||||
Status int `gorm:"column:status"`
|
Status int `gorm:"column:status"`
|
||||||
CreatedAt time.Time `gorm:"column:created_at"`
|
CreatedAt time.Time `gorm:"column:created_at"`
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at"`
|
UpdatedAt time.Time `gorm:"column:updated_at"`
|
||||||
// Currently unused (auth is stateless now)
|
Token string `gorm:"column:api_token"`
|
||||||
/*Token string `gorm:"column:api_token"`
|
TokenExpiration time.Time `gorm:"column:api_token_expiry"`
|
||||||
TokenExpiration time.Time `gorm:"column:api_token_expiry"`*/
|
|
||||||
Language string `gorm:"column:language"`
|
Language string `gorm:"column:language"`
|
||||||
|
|
||||||
// TODO: move this to PublicUser
|
// TODO: move this to PublicUser
|
||||||
|
@ -43,7 +42,7 @@ func (u User) Size() (s int) {
|
||||||
4*3 + //time.Time
|
4*3 + //time.Time
|
||||||
3*2 + // arrays
|
3*2 + // arrays
|
||||||
// string 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
|
s *= 8
|
||||||
|
|
||||||
// Ignoring foreign key users. Fuck them.
|
// Ignoring foreign key users. Fuck them.
|
||||||
|
|
|
@ -186,6 +186,10 @@ div.container div.blockBody:nth-of-type(2) table tr:first-of-type th:last-of-typ
|
||||||
.navbar {
|
.navbar {
|
||||||
border-radius: 0px;
|
border-radius: 0px;
|
||||||
}
|
}
|
||||||
|
/* the logo needs some space */
|
||||||
|
.navbar-logo {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
:target {
|
:target {
|
||||||
background-color: #e5eecc;
|
background-color: #e5eecc;
|
||||||
|
|
BIN
public/img/logo.png
Fichier normal
BIN
public/img/logo.png
Fichier normal
Fichier binaire non affiché.
Après Largeur: | Hauteur: | Taille: 8 Kio |
|
@ -5,98 +5,96 @@ import (
|
||||||
"github.com/ewhal/nyaa/db"
|
"github.com/ewhal/nyaa/db"
|
||||||
"github.com/ewhal/nyaa/model"
|
"github.com/ewhal/nyaa/model"
|
||||||
formStruct "github.com/ewhal/nyaa/service/user/form"
|
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/modelHelper"
|
||||||
"github.com/ewhal/nyaa/util/timeHelper"
|
|
||||||
"github.com/gorilla/securecookie"
|
"github.com/gorilla/securecookie"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
"net/http"
|
"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(
|
var cookieHandler = securecookie.New(
|
||||||
securecookie.GenerateRandomKey(64),
|
securecookie.GenerateRandomKey(64),
|
||||||
securecookie.GenerateRandomKey(32))
|
securecookie.GenerateRandomKey(32))
|
||||||
|
|
||||||
// Encoding & Decoding of the cookie value
|
func Token(r *http.Request) (string, error) {
|
||||||
func DecodeCookie(cookie_value string) (uint, error) {
|
var token string
|
||||||
value := make(map[string]string)
|
cookie, err := r.Cookie("session")
|
||||||
err := cookieHandler.Decode(CookieName, cookie_value, &value)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return token, err
|
||||||
}
|
}
|
||||||
time_int, _ := strconv.ParseInt(value["t"], 10, 0)
|
cookieValue := make(map[string]string)
|
||||||
if timeHelper.IsExpired(time.Unix(time_int, 0)) {
|
err = cookieHandler.Decode("session", cookie.Value, &cookieValue)
|
||||||
return 0, errors.New("Cookie is expired")
|
if err != nil {
|
||||||
|
return token, err
|
||||||
}
|
}
|
||||||
ret, err := strconv.ParseUint(value["u"], 10, 0)
|
token = cookieValue["token"]
|
||||||
return uint(ret), err
|
if len(token) == 0 {
|
||||||
|
return token, errors.New("token is empty")
|
||||||
|
}
|
||||||
|
return token, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func EncodeCookie(user_id uint) (string, error) {
|
// SetCookie sets a cookie.
|
||||||
validUntil := timeHelper.FewDaysLater(7) // 1 week
|
func SetCookie(w http.ResponseWriter, token string) (int, error) {
|
||||||
value := map[string]string{
|
value := map[string]string{
|
||||||
"u": strconv.FormatUint(uint64(user_id), 10),
|
"token": token,
|
||||||
"t": strconv.FormatInt(validUntil.Unix(), 10),
|
|
||||||
}
|
}
|
||||||
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) {
|
func ClearCookie(w http.ResponseWriter) (int, error) {
|
||||||
cookie := &http.Cookie{
|
cookie := &http.Cookie{
|
||||||
Name: CookieName,
|
Name: "session",
|
||||||
Value: "",
|
Value: "",
|
||||||
Path: "/",
|
Path: "/",
|
||||||
HttpOnly: true,
|
|
||||||
MaxAge: -1,
|
MaxAge: -1,
|
||||||
}
|
}
|
||||||
http.SetCookie(w, cookie)
|
http.SetCookie(w, cookie)
|
||||||
return http.StatusOK, nil
|
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) {
|
func SetCookieHandler(w http.ResponseWriter, email string, pass string) (int, error) {
|
||||||
if email == "" || pass == "" {
|
if email != "" && pass != "" {
|
||||||
return http.StatusNotFound, errors.New("No username/password entered")
|
var user model.User
|
||||||
}
|
isValidEmail, _ := formStruct.EmailValidation(email, formStruct.NewErrors())
|
||||||
|
if isValidEmail {
|
||||||
var user model.User
|
log.Debug("User entered valid email.")
|
||||||
// search by email or username
|
if db.ORM.Where("email = ?", email).First(&user).RecordNotFound() {
|
||||||
isValidEmail, _ := formStruct.EmailValidation(email, formStruct.NewErrors())
|
return http.StatusNotFound, errors.New("User not found")
|
||||||
if isValidEmail {
|
}
|
||||||
if db.ORM.Where("email = ?", email).First(&user).RecordNotFound() {
|
} else {
|
||||||
return http.StatusNotFound, errors.New("User not found")
|
log.Debug("User entered username.")
|
||||||
|
if db.ORM.Where("username = ?", email).First(&user).RecordNotFound() {
|
||||||
|
return http.StatusNotFound, errors.New("User not found")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(pass))
|
||||||
if db.ORM.Where("username = ?", email).First(&user).RecordNotFound() {
|
if err != nil {
|
||||||
return http.StatusNotFound, errors.New("User not found")
|
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))
|
return http.StatusNotFound, errors.New("user not found")
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterHanderFromForm sets cookie from a RegistrationForm.
|
// RegisterHanderFromForm sets cookie from a RegistrationForm.
|
||||||
|
@ -113,31 +111,24 @@ func RegisterHandler(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||||
return RegisterHanderFromForm(w, registrationForm)
|
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) {
|
func CurrentUser(r *http.Request) (model.User, error) {
|
||||||
var user model.User
|
var user model.User
|
||||||
var encoded string
|
var token string
|
||||||
|
var err error
|
||||||
encoded = r.Header.Get("X-Auth-Token")
|
token = r.Header.Get("X-Auth-Token")
|
||||||
if len(encoded) == 0 {
|
if len(token) > 0 {
|
||||||
// check cookie instead
|
log.Debug("header token exists")
|
||||||
cookie, err := r.Cookie(CookieName)
|
} else {
|
||||||
|
token, err = Token(r)
|
||||||
|
log.Debug("header token does not exist")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return user, err
|
return user, err
|
||||||
}
|
}
|
||||||
encoded = cookie.Value
|
|
||||||
}
|
}
|
||||||
user_id, err := DecodeCookie(encoded)
|
if db.ORM.Where("api_token = ?", token).First(&user).RecordNotFound() {
|
||||||
if err != nil {
|
return user, errors.New("user not found")
|
||||||
return user, err
|
|
||||||
}
|
}
|
||||||
if db.ORM.Where("user_id = ?", user_id).First(&user).RecordNotFound() {
|
err = db.ORM.Model(&user).Error
|
||||||
return user, errors.New("User not found")
|
return user, err
|
||||||
}
|
|
||||||
|
|
||||||
if user.Status == -1 {
|
|
||||||
// recheck as user might've been banned in the meantime
|
|
||||||
return user, errors.New("Account banned")
|
|
||||||
}
|
|
||||||
return user, nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ewhal/nyaa/config"
|
||||||
"github.com/ewhal/nyaa/db"
|
"github.com/ewhal/nyaa/db"
|
||||||
"github.com/ewhal/nyaa/model"
|
"github.com/ewhal/nyaa/model"
|
||||||
formStruct "github.com/ewhal/nyaa/service/user/form"
|
formStruct "github.com/ewhal/nyaa/service/user/form"
|
||||||
|
@ -14,6 +15,7 @@ import (
|
||||||
"github.com/ewhal/nyaa/util/crypto"
|
"github.com/ewhal/nyaa/util/crypto"
|
||||||
"github.com/ewhal/nyaa/util/log"
|
"github.com/ewhal/nyaa/util/log"
|
||||||
"github.com/ewhal/nyaa/util/modelHelper"
|
"github.com/ewhal/nyaa/util/modelHelper"
|
||||||
|
"github.com/ewhal/nyaa/util/timeHelper"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -67,8 +69,15 @@ func CreateUserFromForm(registrationForm formStruct.RegistrationForm) (model.Use
|
||||||
return user, err
|
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.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 {
|
if db.ORM.Create(&user).Error != nil {
|
||||||
return user, errors.New("user not created")
|
return user, errors.New("user not created")
|
||||||
}
|
}
|
||||||
|
@ -148,13 +157,17 @@ func UpdateUserCore(user *model.User) (int, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
user.UpdatedAt = time.Now()
|
token, err := crypto.GenerateRandomToken32()
|
||||||
err := db.ORM.Save(user).Error
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return http.StatusInternalServerError, err
|
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
|
return http.StatusOK, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,13 +197,18 @@ func UpdateUser(w http.ResponseWriter, form *formStruct.UserForm, currentUser *m
|
||||||
form.Username = user.Username
|
form.Username = user.Username
|
||||||
}
|
}
|
||||||
if (form.Email != user.Email) {
|
if (form.Email != user.Email) {
|
||||||
// send verification to new email and keep old
|
|
||||||
SendVerificationToUser(user, form.Email)
|
SendVerificationToUser(user, form.Email)
|
||||||
form.Email = user.Email
|
form.Email = user.Email
|
||||||
}
|
}
|
||||||
log.Debugf("form %+v\n", form)
|
log.Debugf("form %+v\n", form)
|
||||||
modelHelper.AssignValue(&user, form)
|
modelHelper.AssignValue(&user, form)
|
||||||
status, err := UpdateUserCore(&user)
|
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
|
return user, status, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,7 @@ udp://tracker.leechers-paradise.org:6969
|
||||||
udp://explodie.org:6969
|
udp://explodie.org:6969
|
||||||
udp://tracker.opentrackr.org:1337
|
udp://tracker.opentrackr.org:1337
|
||||||
udp://tracker.internetwarriors.net:1337/announce
|
udp://tracker.internetwarriors.net:1337/announce
|
||||||
|
udp://eddie4.nl:6969/announce
|
||||||
http://mgtracker.org:6969/announce
|
http://mgtracker.org:6969/announce
|
||||||
http://tracker.baka-sub.cf/announce</pre>
|
http://tracker.baka-sub.cf/announce</pre>
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@
|
||||||
<span class="icon-bar"></span>
|
<span class="icon-bar"></span>
|
||||||
<span class="icon-bar"></span>
|
<span class="icon-bar"></span>
|
||||||
</button>
|
</button>
|
||||||
<a class="navbar-brand" href="{{.URL.Parse "/"}}">Nyaa Pantsu</a>
|
<a href="{{.URL.Parse "/"}}"><img src="/img/logo.png" class ="navbar-logo" alt="Nyaa Pantsu"></a>
|
||||||
<a class="navbar-brand hidden-md pull-right nightswitch" href="javascript:toggleNightMode();" ></a>
|
<a class="navbar-brand hidden-md pull-right nightswitch" href="javascript:toggleNightMode();" ></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
|
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
{{define "title"}}{{ T "sign_in_title" }}{{end}}
|
{{define "title"}}{{ T "sign_in_title" }}{{end}}
|
||||||
{{define "contclass"}}cont-view{{end}}
|
{{define "contclass"}}cont-view{{end}}
|
||||||
{{define "content"}}
|
{{define "content"}}
|
||||||
<div class="blockBody">
|
<div class="blockBody">
|
||||||
|
@ -24,8 +24,8 @@
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
<span class="button-checkbox">
|
<span class="button-checkbox">
|
||||||
<!-- <button type="button" class="btn hidden" data-color="info">{{ T "remember_me"}}</button>
|
<button type="button" class="btn hidden" data-color="info">{{ T "remember_me"}}</button>
|
||||||
<input type="checkbox" name="remember_me" id="remember_me" checked="checked"> -->
|
<input type="checkbox" name="remember_me" id="remember_me" checked="checked">
|
||||||
<a href="" class="btn btn-link pull-right">{{ T "forgot_password"}}</a>
|
<a href="" class="btn btn-link pull-right">{{ T "forgot_password"}}</a>
|
||||||
</span>
|
</span>
|
||||||
<hr class="colorgraph">
|
<hr class="colorgraph">
|
||||||
|
|
Référencer dans un nouveau ticket