713ab02450
* Added configor as a new library
Now config is a singleton. You only need to do config.Get() instead of doing config.Conf.
* Forgot godep save 🐤
* Fix accidental removal of }
178 lignes
5 Kio
Go
178 lignes
5 Kio
Go
package cookies
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/NyaaPantsu/nyaa/config"
|
|
"github.com/NyaaPantsu/nyaa/models"
|
|
"github.com/NyaaPantsu/nyaa/models/users"
|
|
"github.com/NyaaPantsu/nyaa/utils/timeHelper"
|
|
"github.com/NyaaPantsu/nyaa/utils/validator/user"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/gorilla/context"
|
|
"github.com/gorilla/securecookie"
|
|
)
|
|
|
|
const (
|
|
// CookieName : Name of cookie
|
|
CookieName = "session"
|
|
|
|
// UserContextKey : key for user context
|
|
UserContextKey = "nyaapantsu.user"
|
|
)
|
|
|
|
// NewCurrentUserRetriever create CurrentUserRetriever Struct for languages
|
|
func NewCurrentUserRetriever() *CurrentUserRetriever {
|
|
return &CurrentUserRetriever{}
|
|
}
|
|
|
|
// CurrentUserRetriever struct for languages
|
|
type CurrentUserRetriever struct{}
|
|
|
|
// RetrieveCurrentUser retrieve current user for languages
|
|
func (*CurrentUserRetriever) RetrieveCurrentUser(c *gin.Context) (*models.User, error) {
|
|
user, _, err := CurrentUser(c)
|
|
if user == nil {
|
|
return &models.User{}, err
|
|
}
|
|
return user, err
|
|
}
|
|
|
|
// CreateUserAuthentication creates user authentication.
|
|
func CreateUserAuthentication(c *gin.Context, form *userValidator.LoginForm) (*models.User, int, error) {
|
|
username := form.Username
|
|
pass := form.Password
|
|
user, status, err := users.Exists(username, pass)
|
|
if err != nil {
|
|
return user, status, err
|
|
}
|
|
status, err = SetLogin(c, user)
|
|
return user, status, err
|
|
}
|
|
|
|
// If you want to keep login cookies between restarts you need to make these permanent
|
|
var cookieHandler = securecookie.New(
|
|
getOrGenerateKey(config.Get().Cookies.HashKey, 64),
|
|
getOrGenerateKey(config.Get().Cookies.EncryptionKey, 32))
|
|
|
|
func getOrGenerateKey(key string, requiredLen int) []byte {
|
|
data := []byte(key)
|
|
if len(data) == 0 {
|
|
data = securecookie.GenerateRandomKey(requiredLen)
|
|
} else if len(data) != requiredLen {
|
|
panic(fmt.Sprintf("failed to load cookie key. required key length is %d bytes and the provided key length is %d bytes.", requiredLen, len(data)))
|
|
}
|
|
return data
|
|
}
|
|
|
|
// Decode : Encoding & Decoding of the cookie value
|
|
func Decode(cookieValue string) (uint, error) {
|
|
value := make(map[string]string)
|
|
err := cookieHandler.Decode(CookieName, cookieValue, &value)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
timeInt, _ := strconv.ParseInt(value["t"], 10, 0)
|
|
if timeHelper.IsExpired(time.Unix(timeInt, 0)) {
|
|
return 0, errors.New("Cookie is expired")
|
|
}
|
|
ret, err := strconv.ParseUint(value["u"], 10, 0)
|
|
return uint(ret), err
|
|
}
|
|
|
|
// Encode : Encoding of the cookie value
|
|
func Encode(userID uint, validUntil time.Time) (string, error) {
|
|
value := map[string]string{
|
|
"u": strconv.FormatUint(uint64(userID), 10),
|
|
"t": strconv.FormatInt(validUntil.Unix(), 10),
|
|
}
|
|
return cookieHandler.Encode(CookieName, value)
|
|
}
|
|
|
|
// Clear : Erase cookie session
|
|
func Clear(c *gin.Context) {
|
|
c.SetCookie(CookieName, "", -1, "/", getDomainName(), false, true)
|
|
}
|
|
|
|
// SetLogin sets the authentication cookie
|
|
func SetLogin(c *gin.Context, user *models.User) (int, error) {
|
|
maxAge := getMaxAge()
|
|
validUntil := timeHelper.FewDurationLater(time.Duration(maxAge) * time.Second)
|
|
encoded, err := Encode(user.ID, validUntil)
|
|
if err != nil {
|
|
return http.StatusInternalServerError, err
|
|
}
|
|
|
|
c.SetCookie(CookieName, encoded, maxAge, "/", getDomainName(), false, true)
|
|
// also set response header for convenience
|
|
c.Header("X-Auth-Token", encoded)
|
|
return http.StatusOK, nil
|
|
}
|
|
|
|
// CurrentUser retrieves a current user.
|
|
func CurrentUser(c *gin.Context) (*models.User, int, error) {
|
|
encoded := c.Request.Header.Get("X-Auth-Token")
|
|
var user = &models.User{}
|
|
if len(encoded) == 0 {
|
|
// check cookie instead
|
|
cookie, err := c.Cookie(CookieName)
|
|
if err != nil {
|
|
return user, http.StatusInternalServerError, err
|
|
}
|
|
encoded = cookie
|
|
}
|
|
userID, err := Decode(encoded)
|
|
if err != nil {
|
|
return user, http.StatusInternalServerError, err
|
|
}
|
|
|
|
userFromContext := getUserFromContext(c)
|
|
|
|
if userFromContext.ID > 0 && userID == userFromContext.ID {
|
|
user = userFromContext
|
|
} else {
|
|
user, _, _ = users.SessionByID(userID)
|
|
setUserToContext(c, user)
|
|
}
|
|
|
|
if user.IsBanned() {
|
|
// recheck as user might've been banned in the meantime
|
|
return user, http.StatusUnauthorized, errors.New("account_banned")
|
|
}
|
|
if err != nil {
|
|
return user, http.StatusInternalServerError, err
|
|
}
|
|
return user, http.StatusOK, nil
|
|
}
|
|
func getUserFromContext(c *gin.Context) *models.User {
|
|
if rv := context.Get(c.Request, UserContextKey); rv != nil {
|
|
return rv.(*models.User)
|
|
}
|
|
return &models.User{}
|
|
}
|
|
|
|
func setUserToContext(c *gin.Context, val *models.User) {
|
|
context.Set(c.Request, UserContextKey, val)
|
|
}
|
|
|
|
// RetrieveUser retrieves a user.
|
|
func RetrieveUserFromRequest(c *gin.Context, id uint) (*models.User, bool, uint, int, error) {
|
|
var user models.User
|
|
var currentUserID uint
|
|
var isAuthor bool
|
|
|
|
if models.ORM.First(&user, id).RecordNotFound() {
|
|
return nil, isAuthor, currentUserID, http.StatusNotFound, errors.New("user_not_found")
|
|
}
|
|
currentUser, _, err := CurrentUser(c)
|
|
if err == nil {
|
|
currentUserID = currentUser.ID
|
|
isAuthor = currentUser.ID == user.ID
|
|
}
|
|
|
|
return &user, isAuthor, currentUserID, http.StatusOK, nil
|
|
}
|