User Package (WIP)
Added services and utils needed
Cette révision appartient à :
Parent
e23ceeab37
révision
5ef953ff55
11 fichiers modifiés avec 909 ajouts et 0 suppressions
|
@ -5,4 +5,5 @@ const (
|
||||||
// DEVELOPMENT | TEST | PRODUCTION
|
// DEVELOPMENT | TEST | PRODUCTION
|
||||||
Environment = "DEVELOPMENT"
|
Environment = "DEVELOPMENT"
|
||||||
// Environment = "PRODUCTION"
|
// Environment = "PRODUCTION"
|
||||||
|
WebAddress = "nyaa.pantsu.cat"
|
||||||
)
|
)
|
||||||
|
|
47
router/userHandler.go
Fichier normal
47
router/userHandler.go
Fichier normal
|
@ -0,0 +1,47 @@
|
||||||
|
package router
|
||||||
|
|
||||||
|
import(
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"net/http"
|
||||||
|
"html"
|
||||||
|
"strconv"
|
||||||
|
"github.com/ewhal/nyaa/model"
|
||||||
|
"github.com/ewhal/nyaa/service/user"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Getting View User Registration
|
||||||
|
|
||||||
|
func UserRegisterFormHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getting View User Login
|
||||||
|
func UserLoginFormHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getting User Profile
|
||||||
|
func UserProfileHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getting View User Profile Update
|
||||||
|
func UserProfileFormHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post Registration controller
|
||||||
|
func UserRegisterPostHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post Login controller
|
||||||
|
func UserLoginPostHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post Profule Update controller
|
||||||
|
func UserProfilePostHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
157
service/user/cookieHelper.go
Fichier normal
157
service/user/cookieHelper.go
Fichier normal
|
@ -0,0 +1,157 @@
|
||||||
|
package userService
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/securecookie"
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
|
||||||
|
"github.com/ewhal/nyaa/config"
|
||||||
|
"github.com/ewhal/nyaa/db"
|
||||||
|
"github.com/ewhal/nyaa/model"
|
||||||
|
"github.com/ewhal/nyaa/util/log"
|
||||||
|
"github.com/ewhal/nyaa/util/validation"
|
||||||
|
"github.com//modelHelper"
|
||||||
|
)
|
||||||
|
|
||||||
|
var cookieHandler = securecookie.New(
|
||||||
|
securecookie.GenerateRandomKey(64),
|
||||||
|
securecookie.GenerateRandomKey(32))
|
||||||
|
|
||||||
|
// // UserName get username from a cookie.
|
||||||
|
// func UserName(c *gin.Context) (string, error) {
|
||||||
|
// var userName string
|
||||||
|
// request := c.Request
|
||||||
|
// cookie, err := request.Cookie("session")
|
||||||
|
// if err != nil {
|
||||||
|
// return userName, err
|
||||||
|
// }
|
||||||
|
// cookieValue := make(map[string]string)
|
||||||
|
// err = cookieHandler.Decode("session", cookie.Value, &cookieValue)
|
||||||
|
// if err != nil {
|
||||||
|
// return userName, err
|
||||||
|
// }
|
||||||
|
// userName = cookieValue["name"]
|
||||||
|
// return userName, nil
|
||||||
|
// }
|
||||||
|
|
||||||
|
func Token(w http.ResponseWriter, r *http.Request) (string, error) {
|
||||||
|
var token string
|
||||||
|
request := c.Request
|
||||||
|
cookie, err := request.Cookie("session")
|
||||||
|
if err != nil {
|
||||||
|
return token, err
|
||||||
|
}
|
||||||
|
cookieValue := make(map[string]string)
|
||||||
|
err = cookieHandler.Decode("session", cookie.Value, &cookieValue)
|
||||||
|
if err != nil {
|
||||||
|
return token, err
|
||||||
|
}
|
||||||
|
token = cookieValue["token"]
|
||||||
|
if len(token) == 0 {
|
||||||
|
return token, errors.New("Token is empty.")
|
||||||
|
}
|
||||||
|
return token, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCookie sets a cookie.
|
||||||
|
func SetCookie(w http.ResponseWriter, token string) (int, error) {
|
||||||
|
value := map[string]string{
|
||||||
|
"token": token,
|
||||||
|
}
|
||||||
|
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: "session",
|
||||||
|
Value: "",
|
||||||
|
Path: "/",
|
||||||
|
MaxAge: -1,
|
||||||
|
}
|
||||||
|
http.SetCookie(w, cookie)
|
||||||
|
return http.StatusOK, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCookieHandler sets a cookie with email and password.
|
||||||
|
func SetCookieHandler(w http.ResponseWriter, email string, pass string) (int, error) {
|
||||||
|
if email != "" && pass != "" {
|
||||||
|
log.Debugf("User email : %s , password : %s", email, pass)
|
||||||
|
var user model.User
|
||||||
|
isValidEmail := validation.EmailValidation(email)
|
||||||
|
if isValidEmail {
|
||||||
|
log.Debug("User entered valid email.")
|
||||||
|
if db.ORM.Where("email = ?", email).First(&user).RecordNotFound() {
|
||||||
|
return http.StatusNotFound, errors.New("User is not found.")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Debug("User entered username.")
|
||||||
|
if db.ORM.Where("username = ?", email).First(&user).RecordNotFound() {
|
||||||
|
return http.StatusNotFound, errors.New("User is not found.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(pass))
|
||||||
|
if err != nil {
|
||||||
|
return http.StatusUnauthorized, errors.New("Password incorrect.")
|
||||||
|
}
|
||||||
|
status, err := SetCookie(w, user.Token)
|
||||||
|
if err != nil {
|
||||||
|
return status, err
|
||||||
|
}
|
||||||
|
w.Header().Set("X-Auth-Token", user.Token)
|
||||||
|
return http.StatusOK, nil
|
||||||
|
} else {
|
||||||
|
return http.StatusNotFound, errors.New("User is not found.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterHanderFromForm sets cookie from a RegistrationForm.
|
||||||
|
func RegisterHanderFromForm(w http.ResponseWriter, registrationForm RegistrationForm) (int, error) {
|
||||||
|
email := registrationForm.Email
|
||||||
|
pass := registrationForm.Password
|
||||||
|
log.Debugf("RegisterHandler UserEmail : %s", email)
|
||||||
|
log.Debugf("RegisterHandler UserPassword : %s", pass)
|
||||||
|
return SetCookieHandler(w, email, pass)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterHandler sets a cookie when user registered.
|
||||||
|
func RegisterHandler(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||||
|
var registrationForm RegistrationForm
|
||||||
|
modelHelper.BindValueForm(®istrationForm, r)
|
||||||
|
return RegisterHanderFromForm(w, registrationForm)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CurrentUser get a current user.
|
||||||
|
func CurrentUser(r *http.Request) (model.User, error) {
|
||||||
|
var user model.User
|
||||||
|
var token string
|
||||||
|
var err error
|
||||||
|
token = r.Header.Get("X-Auth-Token")
|
||||||
|
if len(token) > 0 {
|
||||||
|
log.Debug("header token exist.")
|
||||||
|
} else {
|
||||||
|
token, err = Token(c)
|
||||||
|
log.Debug("header token not exist.")
|
||||||
|
if err != nil {
|
||||||
|
return user, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if db.ORM.Select(config.UserPublicFields+", email").Where("token = ?", token).First(&user).RecordNotFound() {
|
||||||
|
return user, errors.New("User is not found.")
|
||||||
|
}
|
||||||
|
db.ORM.Model(&user).Association("Languages").Find(&user.Languages)
|
||||||
|
db.ORM.Model(&user).Association("Roles").Find(&user.Roles)
|
||||||
|
return user, nil
|
||||||
|
}
|
69
service/user/formValidator.go
Fichier normal
69
service/user/formValidator.go
Fichier normal
|
@ -0,0 +1,69 @@
|
||||||
|
package userService
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
"github.com/ewhal/nyaa/util/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
const EMAIL_REGEX = `(\w[-._\w]*\w@\w[-._\w]*\w\.\w{2,3})`
|
||||||
|
|
||||||
|
func EmailValidation(email string) bool {
|
||||||
|
exp, err := regexp.Compile(EMAIL_REGEX)
|
||||||
|
if regexpCompiled := log.CheckError(err); regexpCompiled {
|
||||||
|
if exp.MatchString(email) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegistrationForm is used when creating a user.
|
||||||
|
type RegistrationForm struct {
|
||||||
|
Username string `form:"registrationUsername" binding:"required"`
|
||||||
|
Email string `form:"registrationEmail" binding:"required"`
|
||||||
|
Password string `form:"registrationPassword" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegistrationForm is used when creating a user authentication.
|
||||||
|
type LoginForm struct {
|
||||||
|
Email string `form:"loginEmail" binding:"required"`
|
||||||
|
Password string `form:"loginPassword" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserForm is used when updating a user.
|
||||||
|
type UserForm struct {
|
||||||
|
Email string `form:"email" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PasswordForm is used when updating a user password.
|
||||||
|
type PasswordForm struct {
|
||||||
|
CurrentPassword string `form:"currentPassword" binding:"required"`
|
||||||
|
Password string `form:"newPassword" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendPasswordResetForm is used when sending a password reset token.
|
||||||
|
type SendPasswordResetForm struct {
|
||||||
|
Email string `form:"email" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PasswordResetForm is used when reseting a password.
|
||||||
|
type PasswordResetForm struct {
|
||||||
|
PasswordResetToken string `form:"token" binding:"required"`
|
||||||
|
Password string `form:"newPassword" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyEmailForm is used when verifying an email.
|
||||||
|
type VerifyEmailForm struct {
|
||||||
|
ActivationToken string `form:"token" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ActivateForm is used when activating user.
|
||||||
|
type ActivateForm struct {
|
||||||
|
Activation bool `form:"activation" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserRoleForm is used when adding or removing a role from a user.
|
||||||
|
type UserRoleForm struct {
|
||||||
|
UserId int `form:"userId" binding:"required"`
|
||||||
|
RoleId int `form:"roleId" binding:"required"`
|
||||||
|
}
|
22
service/user/locale/en-us.all.json
Fichier normal
22
service/user/locale/en-us.all.json
Fichier normal
|
@ -0,0 +1,22 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "link",
|
||||||
|
"translation": "link"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "verify_email_title",
|
||||||
|
"translation": "Verify your email address for Goyangi."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "verify_email_content",
|
||||||
|
"translation": "Please click below link to verify your email."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "reset_password_title",
|
||||||
|
"translation": "Reset your password for goyangi."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "reset_password_content",
|
||||||
|
"translation": "Please click below link to reset your password."
|
||||||
|
}
|
||||||
|
]
|
42
service/user/permission/permission.go
Fichier normal
42
service/user/permission/permission.go
Fichier normal
|
@ -0,0 +1,42 @@
|
||||||
|
package userPermission
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/ewhal/nyaa/model"
|
||||||
|
"github.com/ewhal/nyaa/service/user"
|
||||||
|
"github.com/ewhal/nyaa/util/log"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HasAdmin checks that user has an admin permission.
|
||||||
|
func HasAdmin(user *model.User) bool {
|
||||||
|
name := "admin"
|
||||||
|
for _, role := range user.Roles {
|
||||||
|
log.Debugf("HasAdmin role.Name : %s", role.Name)
|
||||||
|
if role.Name == name {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// CurrentOrAdmin check that user has admin permission or user is the current user.
|
||||||
|
func CurrentOrAdmin(user *model.User, userId uint) bool {
|
||||||
|
log.Debugf("user.Id == userId %d %d %s", user.Id, userId, user.Id == userId)
|
||||||
|
return (HasAdmin(user) || user.Id == userId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CurrentUserIdentical check that userId is same as current user's Id.
|
||||||
|
func CurrentUserIdentical(userId uint) (bool, error) {
|
||||||
|
currentUser, err := userService.CurrentUser(c)
|
||||||
|
if err != nil {
|
||||||
|
return false, errors.New("Auth failed.")
|
||||||
|
}
|
||||||
|
if currentUser.Id != userId {
|
||||||
|
return false, errors.New("User is not identical.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
341
service/user/user.go
Fichier normal
341
service/user/user.go
Fichier normal
|
@ -0,0 +1,341 @@
|
||||||
|
package userService
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
|
||||||
|
"github.com/ewhal/nyaa/config"
|
||||||
|
"github.com/ewhal/nyaa/db"
|
||||||
|
"github.com/ewhal/nyaa/model"
|
||||||
|
"github.com/ewhal/nyaa/util/crypto"
|
||||||
|
"github.com/ewhal/nyaa/util/log"
|
||||||
|
"github.com/ewhal/nyaa/util/modelHelper"
|
||||||
|
"github.com/ewhal/nyaa/util/timeHelper"
|
||||||
|
)
|
||||||
|
|
||||||
|
var userFields []string = []string{"name", "email", "createdAt", "updatedAt"}
|
||||||
|
|
||||||
|
// SuggestUsername suggest user's name if user's name already occupied.
|
||||||
|
func SuggestUsername(username string) string {
|
||||||
|
var count int
|
||||||
|
var usernameCandidate string
|
||||||
|
db.ORM.Model(model.User{}).Where(&model.User{Username: username}).Count(&count)
|
||||||
|
log.Debugf("count Before : %d", count)
|
||||||
|
if count == 0 {
|
||||||
|
return username
|
||||||
|
} else {
|
||||||
|
var postfix int
|
||||||
|
for {
|
||||||
|
usernameCandidate = username + strconv.Itoa(postfix)
|
||||||
|
log.Debugf("usernameCandidate: %s\n", usernameCandidate)
|
||||||
|
db.ORM.Model(model.User{}).Where(&model.User{Username: usernameCandidate}).Count(&count)
|
||||||
|
log.Debugf("count after : %d\n", count)
|
||||||
|
postfix = postfix + 1
|
||||||
|
if count == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return usernameCandidate
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateUserFromForm creates a user from a registration form.
|
||||||
|
func CreateUserFromForm(registrationForm RegistrationForm) (model.User, error) {
|
||||||
|
var user model.User
|
||||||
|
log.Debugf("registrationForm %+v\n", registrationForm)
|
||||||
|
modelHelper.AssignValue(&user, ®istrationForm)
|
||||||
|
user.Md5 = crypto.GenerateMD5Hash(user.Email) // Gravatar
|
||||||
|
token, err := crypto.GenerateRandomToken32()
|
||||||
|
if err != nil {
|
||||||
|
return user, errors.New("Token not generated.")
|
||||||
|
}
|
||||||
|
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 is not created.")
|
||||||
|
}
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateUser creates a user.
|
||||||
|
func CreateUser(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||||
|
var user model.User
|
||||||
|
var registrationForm RegistrationForm
|
||||||
|
var status int
|
||||||
|
var err error
|
||||||
|
|
||||||
|
modelHelper.BindValueForm(®istrationForm, r)
|
||||||
|
|
||||||
|
password, err := bcrypt.GenerateFromPassword([]byte(registrationForm.Password), 10)
|
||||||
|
if err != nil {
|
||||||
|
return http.StatusInternalServerError, err
|
||||||
|
}
|
||||||
|
registrationForm.Password = string(password)
|
||||||
|
user, err = CreateUserFromForm(registrationForm)
|
||||||
|
if err != nil {
|
||||||
|
return http.StatusInternalServerError, err
|
||||||
|
}
|
||||||
|
SendVerificationToUser(user)
|
||||||
|
status, err = RegisterHandler(w)
|
||||||
|
return status, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetrieveUser retrieves a user.
|
||||||
|
func RetrieveUser(r *http.Request, id string) (*model.PublicUser, bool, uint, int, error) {
|
||||||
|
var user model.User
|
||||||
|
var currentUserId uint
|
||||||
|
var isAuthor bool
|
||||||
|
// var publicUser *model.PublicUser
|
||||||
|
// publicUser.User = &user
|
||||||
|
if db.ORM.Select(config.UserPublicFields).First(&user, id).RecordNotFound() {
|
||||||
|
return &model.PublicUser{User: &user}, isAuthor, currentUserId, http.StatusNotFound, errors.New("User is not found.")
|
||||||
|
}
|
||||||
|
currentUser, err := CurrentUser(r)
|
||||||
|
if err == nil {
|
||||||
|
currentUserId = currentUser.Id
|
||||||
|
isAuthor = currentUser.Id == user.Id
|
||||||
|
}
|
||||||
|
var currentPage int
|
||||||
|
|
||||||
|
currentPage = 1
|
||||||
|
|
||||||
|
var likings []model.User
|
||||||
|
var likingCount int
|
||||||
|
db.ORM.Table("users_followers").Where("users_followers.user_id=?", user.Id).Count(&likingCount)
|
||||||
|
if err = db.ORM.Limit(config.LikingPerPage).Order(config.LikingOrder).Offset(offset).Select(config.UserPublicFields).
|
||||||
|
Joins("JOIN users_followers on users_followers.user_id=?", user.Id).
|
||||||
|
Where("users.id = users_followers.follower_id").
|
||||||
|
Group("users.id").Find(&likings).Error; err != nil {
|
||||||
|
log.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
user.Likings = likings
|
||||||
|
var likingList model.LikingList
|
||||||
|
|
||||||
|
var liked []model.User
|
||||||
|
var likedCount int
|
||||||
|
db.ORM.Table("users_followers").Where("users_followers.follower_id=?", user.Id).Count(&likedCount)
|
||||||
|
if err = db.ORM.Limit(config.LikedPerPage).Order(config.LikedOrder).Offset(offset).Select(config.UserPublicFields).
|
||||||
|
Joins("JOIN users_followers on users_followers.follower_id=?", user.Id).
|
||||||
|
Where("users.id = users_followers.user_id").
|
||||||
|
Group("users.id").Find(&liked).Error; err != nil {
|
||||||
|
log.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
user.Liked = liked
|
||||||
|
|
||||||
|
log.Debugf("user liking %v\n", user.Likings)
|
||||||
|
log.Debugf("user liked %v\n", user.Liked)
|
||||||
|
return &model.PublicUser{User: &user}, isAuthor, currentUserId, http.StatusOK, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetrieveUsers retrieves users.
|
||||||
|
func RetrieveUsers() []*model.PublicUser {
|
||||||
|
var users []*model.User
|
||||||
|
var userArr []*model.PublicUser
|
||||||
|
db.ORM.Select(config.UserPublicFields).Find(&users)
|
||||||
|
for _, user := range users {
|
||||||
|
userArr = append(userArr, &model.PublicUser{User: user})
|
||||||
|
}
|
||||||
|
return userArr
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateUserCore updates a user. (Applying the modifed data of user).
|
||||||
|
func UpdateUserCore(user *model.User) (int, error) {
|
||||||
|
user.Md5 = crypto.GenerateMD5Hash(user.Email)
|
||||||
|
token, err := crypto.GenerateRandomToken32()
|
||||||
|
if err != nil {
|
||||||
|
return http.StatusInternalServerError, errors.New("Token not generated.")
|
||||||
|
}
|
||||||
|
user.Token = token
|
||||||
|
user.TokenExpiration = timeHelper.FewDaysLater(config.AuthTokenExpirationDay)
|
||||||
|
if db.ORM.Save(user).Error != nil {
|
||||||
|
return http.StatusInternalServerError, errors.New("User is not updated.")
|
||||||
|
}
|
||||||
|
return http.StatusOK, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateUser updates a user.
|
||||||
|
func UpdateUser(w http.ResponseWriter, r *http.Request, id string) (*model.User, int, error) {
|
||||||
|
var user model.User
|
||||||
|
if db.ORM.First(&user, id).RecordNotFound() {
|
||||||
|
return &user, http.StatusNotFound, errors.New("User is not found.")
|
||||||
|
}
|
||||||
|
switch r.FormValue("type") {
|
||||||
|
case "password":
|
||||||
|
var passwordForm PasswordForm
|
||||||
|
modelHelper.BindValueForm(&passwordForm, r)
|
||||||
|
log.Debugf("form %+v\n", passwordForm)
|
||||||
|
err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(passwordForm.CurrentPassword))
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Password Incorrect.")
|
||||||
|
return &user, http.StatusInternalServerError, errors.New("User is not updated. Password Incorrect.")
|
||||||
|
} else {
|
||||||
|
newPassword, err := bcrypt.GenerateFromPassword([]byte(passwordForm.Password), 10)
|
||||||
|
if err != nil {
|
||||||
|
return &user, http.StatusInternalServerError, errors.New("User is not updated. Password not Generated.")
|
||||||
|
} else {
|
||||||
|
passwordForm.Password = string(newPassword)
|
||||||
|
modelHelper.AssignValue(&user, &passwordForm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
var form UserForm
|
||||||
|
modelHelper.BindValueForm(&form, binding.Form)
|
||||||
|
log.Debugf("form %+v\n", form)
|
||||||
|
modelHelper.AssignValue(&user, &form)
|
||||||
|
}
|
||||||
|
|
||||||
|
status, err := UpdateUserCore(&user)
|
||||||
|
if err != nil {
|
||||||
|
return &user, status, err
|
||||||
|
}
|
||||||
|
status, err = SetCookie(w, user.Token)
|
||||||
|
return &user, status, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteUser deletes a user.
|
||||||
|
func DeleteUser(w http.ResponseWriter, id string) (int, error) {
|
||||||
|
var user model.User
|
||||||
|
if db.ORM.First(&user, id).RecordNotFound() {
|
||||||
|
return http.StatusNotFound, errors.New("User is not found.")
|
||||||
|
}
|
||||||
|
if db.ORM.Delete(&user).Error != nil {
|
||||||
|
return http.StatusInternalServerError, errors.New("User is not deleted.")
|
||||||
|
}
|
||||||
|
status, err := ClearCookie(w)
|
||||||
|
return status, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRoleToUser adds a role to a user.
|
||||||
|
func AddRoleToUser(r *http.Request) (int, error) {
|
||||||
|
var form UserRoleForm
|
||||||
|
var user model.User
|
||||||
|
var role model.Role
|
||||||
|
var roles []model.Role
|
||||||
|
modelHelper.BindValueForm(&form, r)
|
||||||
|
|
||||||
|
if db.ORM.First(&user, form.UserId).RecordNotFound() {
|
||||||
|
return http.StatusNotFound, errors.New("User is not found.")
|
||||||
|
}
|
||||||
|
if db.ORM.First(&role, form.RoleId).RecordNotFound() {
|
||||||
|
return http.StatusNotFound, errors.New("Role is not found.")
|
||||||
|
}
|
||||||
|
log.Debugf("user email : %s", user.Email)
|
||||||
|
log.Debugf("Role name : %s", role.Name)
|
||||||
|
db.ORM.Model(&user).Association("Roles").Append(role)
|
||||||
|
db.ORM.Model(&user).Association("Roles").Find(&roles)
|
||||||
|
if db.ORM.Save(&user).Error != nil {
|
||||||
|
return http.StatusInternalServerError, errors.New("Role not appended to user.")
|
||||||
|
}
|
||||||
|
return http.StatusOK, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveRoleFromUser removes a role from a user.
|
||||||
|
func RemoveRoleFromUser(w http.ResponseWriter, r *http.Request, userId string, roleId string) (int, error) {
|
||||||
|
var user model.User
|
||||||
|
var role model.Role
|
||||||
|
if db.ORM.First(&user, userId).RecordNotFound() {
|
||||||
|
return http.StatusNotFound, errors.New("User is not found.")
|
||||||
|
}
|
||||||
|
if db.ORM.First(&role, roleId).RecordNotFound() {
|
||||||
|
return http.StatusNotFound, errors.New("Role is not found.")
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("user : %v\n", user)
|
||||||
|
log.Debugf("role : %v\n", role)
|
||||||
|
if db.ORM.Model(&user).Association("Roles").Delete(role).Error != nil {
|
||||||
|
return http.StatusInternalServerError, errors.New("Role is not deleted from user.")
|
||||||
|
}
|
||||||
|
return http.StatusOK, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetrieveCurrentUser retrieves a current user.
|
||||||
|
func RetrieveCurrentUser(r *http.Request) (model.User, int, error) {
|
||||||
|
user, err := CurrentUser(r)
|
||||||
|
if err != nil {
|
||||||
|
return user, http.StatusInternalServerError, err
|
||||||
|
}
|
||||||
|
return user, http.StatusOK, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetrieveUserByEmail retrieves a user by an email
|
||||||
|
func RetrieveUserByEmail(email string) (*model.PublicUser, string, int, error) {
|
||||||
|
var user model.User
|
||||||
|
if db.ORM.Unscoped().Select(config.UserPublicFields).Where("email like ?", "%"+email+"%").First(&user).RecordNotFound() {
|
||||||
|
return &model.PublicUser{User: &user}, email, http.StatusNotFound, errors.New("User is not found.")
|
||||||
|
}
|
||||||
|
return &model.PublicUser{User: &user}, email, http.StatusOK, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetrieveUsersByEmail retrieves users by an email
|
||||||
|
func RetrieveUsersByEmail(email string) []*model.PublicUser {
|
||||||
|
var users []*model.User
|
||||||
|
var userArr []*model.PublicUser
|
||||||
|
db.ORM.Select(config.UserPublicFields).Where("email like ?", "%"+email+"%").Find(&users)
|
||||||
|
for _, user := range users {
|
||||||
|
userArr = append(userArr, &model.PublicUser{User: user})
|
||||||
|
}
|
||||||
|
return userArr
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetrieveUserByUsername retrieves a user by username.
|
||||||
|
func RetrieveUserByUsername(username string) (*model.PublicUser, string, int, error) {
|
||||||
|
var user model.User
|
||||||
|
if db.ORM.Unscoped().Select(config.UserPublicFields).Where("username like ?", "%"+username+"%").First(&user).RecordNotFound() {
|
||||||
|
return &model.PublicUser{User: &user}, username, http.StatusNotFound, errors.New("User is not found.")
|
||||||
|
}
|
||||||
|
return &model.PublicUser{User: &user}, username, http.StatusOK, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetrieveUserForAdmin retrieves a user for an administrator.
|
||||||
|
func RetrieveUserForAdmin(id string) (model.User, int, error) {
|
||||||
|
var user model.User
|
||||||
|
if db.ORM.First(&user, id).RecordNotFound() {
|
||||||
|
return user, http.StatusNotFound, errors.New("User is not found.")
|
||||||
|
}
|
||||||
|
db.ORM.Model(&user).Association("Languages").Find(&user.Languages)
|
||||||
|
db.ORM.Model(&user).Association("Roles").Find(&user.Roles)
|
||||||
|
return user, http.StatusOK, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetrieveUsersForAdmin retrieves users for an administrator.
|
||||||
|
func RetrieveUsersForAdmin() []model.User {
|
||||||
|
var users []model.User
|
||||||
|
var userArr []model.User
|
||||||
|
db.ORM.Find(&users)
|
||||||
|
for _, user := range users {
|
||||||
|
db.ORM.Model(&user).Association("Languages").Find(&user.Languages)
|
||||||
|
db.ORM.Model(&user).Association("Roles").Find(&user.Roles)
|
||||||
|
userArr = append(userArr, user)
|
||||||
|
}
|
||||||
|
return userArr
|
||||||
|
}
|
||||||
|
|
||||||
|
// ActivateUser toggle activation of a user.
|
||||||
|
func ActivateUser(r *http.Request, id string) (model.User, int, error) {
|
||||||
|
id := c.Params.ByName("id")
|
||||||
|
var user model.User
|
||||||
|
var form ActivateForm
|
||||||
|
modelHelper.BindValueForm(&form, r)
|
||||||
|
if db.ORM.First(&user, id).RecordNotFound() {
|
||||||
|
return user, http.StatusNotFound, errors.New("User is not found.")
|
||||||
|
}
|
||||||
|
user.Activation = form.Activation
|
||||||
|
if db.ORM.Save(&user).Error != nil {
|
||||||
|
return user, http.StatusInternalServerError, errors.New("User not activated.")
|
||||||
|
}
|
||||||
|
return user, http.StatusOK, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateUserAuthentication creates user authentication.
|
||||||
|
func CreateUserAuthentication(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||||
|
var form LoginForm
|
||||||
|
modelHelper.BindValueForm(&form, r)
|
||||||
|
email := form.Email
|
||||||
|
pass := form.Password
|
||||||
|
status, err := SetCookieHandler(w, email, pass)
|
||||||
|
return status, err
|
||||||
|
}
|
56
service/user/userHelper.go
Fichier normal
56
service/user/userHelper.go
Fichier normal
|
@ -0,0 +1,56 @@
|
||||||
|
package userService
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ewha/nyaa/db"
|
||||||
|
"github.com/ewha/nyaa/model"
|
||||||
|
// "github.com/gin-gonic/gin"
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/ewha/nyaa/util/log"
|
||||||
|
"github.com/ewha/nyaa/util/retrieveHelper"
|
||||||
|
// "github.com/dorajistyle/goyangi/util/crypto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FindUserByUserName creates a user.
|
||||||
|
func FindUserByUserName(appID int64, userName string) (model.User, int, error) {
|
||||||
|
var user model.User
|
||||||
|
var err error
|
||||||
|
// token := c.Request.Header.Get("X-Auth-Token")
|
||||||
|
if db.ORM.Where("app_id=? and name=?", appID, userName).First(&user).RecordNotFound() {
|
||||||
|
return user, http.StatusUnauthorized, err
|
||||||
|
}
|
||||||
|
return user, http.StatusOK, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindOrCreateUser creates a user.
|
||||||
|
func FindOrCreateUser(appID int64, userName string) (model.User, int, error) {
|
||||||
|
var user model.User
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// if len(token) > 0 {
|
||||||
|
// log.Debug("header token exist.")
|
||||||
|
// } else {
|
||||||
|
// token, err = Token(c)
|
||||||
|
// log.Debug("header token not exist.")
|
||||||
|
// if err != nil {
|
||||||
|
// return user, http.StatusUnauthorized, err
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
log.Debugf("userName : %s\n", userName)
|
||||||
|
// log.Debugf("Error : %s\n", err.Error())
|
||||||
|
if db.ORM.Where("app_id=? and name=?", appID, userName).First(&user).RecordNotFound() {
|
||||||
|
var user model.User
|
||||||
|
// return user, http.StatusBadRequest, err
|
||||||
|
user.Name = userName
|
||||||
|
// user.Token = token
|
||||||
|
user.AppID = appID
|
||||||
|
log.Debugf("user %+v\n", user)
|
||||||
|
if db.ORM.Create(&user).Error != nil {
|
||||||
|
return user, http.StatusBadRequest, errors.New("User is not created.")
|
||||||
|
}
|
||||||
|
log.Debugf("retrived User %v\n", user)
|
||||||
|
return user, http.StatusOK, nil
|
||||||
|
}
|
||||||
|
return user, http.StatusBadRequest, nil
|
||||||
|
}
|
92
service/user/verification.go
Fichier normal
92
service/user/verification.go
Fichier normal
|
@ -0,0 +1,92 @@
|
||||||
|
package userService
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ewhal/nyaa/config"
|
||||||
|
"github.com/ewhal/nyaa/db"
|
||||||
|
"github.com/ewhal/nyaa/model"
|
||||||
|
"github.com/ewhal/nyaa/util/modelHandler"
|
||||||
|
"github.com/ewhal/nyaa/util/crypto"
|
||||||
|
"github.com/ewhal/nyaa/util/email"
|
||||||
|
"github.com/ewhal/nyaa/util/log"
|
||||||
|
"github.com/ewhal/nyaa/util/timeHelper"
|
||||||
|
|
||||||
|
"github.com/nicksnyder/go-i18n/i18n"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SendEmailVerfication sends an email verification token via email.
|
||||||
|
func SendEmailVerfication(to string, token string, locale string) error {
|
||||||
|
T, _ := i18n.Tfunc(locale)
|
||||||
|
err := email.SendEmailFromAdmin(to,
|
||||||
|
T("verify_email_title"),
|
||||||
|
T("link")+" : "+config.WebAddress+"/verify/email/"+token,
|
||||||
|
T("verify_email_content")+"<br/><a href=\""+config.WebAddress+"/verify/email/"+token+"\" target=\"_blank\">"+config.WebAddress+"/verify/email/"+token+"</a>")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendVerificationToUser sends an email verification token to user.
|
||||||
|
func SendVerificationToUser(user model.User) (int, error) {
|
||||||
|
var status int
|
||||||
|
var err error
|
||||||
|
user.ActivateUntil = timeHelper.TwentyFourHoursLater()
|
||||||
|
user.ActivationToken, err = crypto.GenerateRandomToken32()
|
||||||
|
if err != nil {
|
||||||
|
return http.StatusInternalServerError, err
|
||||||
|
}
|
||||||
|
user.Activation = false
|
||||||
|
log.Debugf("generated token : %s", user.ActivationToken)
|
||||||
|
status, err = UpdateUserCore(&user)
|
||||||
|
if err != nil {
|
||||||
|
return status, err
|
||||||
|
}
|
||||||
|
err = SendEmailVerfication(user.Email, user.ActivationToken, "en-us")
|
||||||
|
if err != nil {
|
||||||
|
return http.StatusInternalServerError, err
|
||||||
|
}
|
||||||
|
return http.StatusOK, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendVerification sends an email verification token.
|
||||||
|
func SendVerification(r *http.Request) (int, error) {
|
||||||
|
|
||||||
|
var user model.User
|
||||||
|
currentUser, err := CurrentUser(r)
|
||||||
|
if err != nil {
|
||||||
|
return http.StatusUnauthorized, errors.New("Unauthorized.")
|
||||||
|
}
|
||||||
|
if db.ORM.First(&user, currentUser.Id).RecordNotFound() {
|
||||||
|
return http.StatusNotFound, errors.New("User is not found.")
|
||||||
|
}
|
||||||
|
status, err := SendVerificationToUser(user)
|
||||||
|
return status, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// EmailVerification verifies an email of user.
|
||||||
|
func EmailVerification(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||||
|
var user model.User
|
||||||
|
var verifyEmailForm VerifyEmailForm
|
||||||
|
modelHandler.BindValueForm(&verifyEmailForm, r)
|
||||||
|
log.Debugf("verifyEmailForm.ActivationToken : %s", verifyEmailForm.ActivationToken)
|
||||||
|
if db.ORM.Where(&model.User{ActivationToken: verifyEmailForm.ActivationToken}).First(&user).RecordNotFound() {
|
||||||
|
return http.StatusNotFound, errors.New("User is not found.")
|
||||||
|
}
|
||||||
|
isExpired := timeHelper.IsExpired(user.ActivateUntil)
|
||||||
|
log.Debugf("passwordResetUntil : %s", user.ActivateUntil.UTC())
|
||||||
|
log.Debugf("expired : %t", isExpired)
|
||||||
|
if isExpired {
|
||||||
|
return http.StatusForbidden, errors.New("token not valid.")
|
||||||
|
}
|
||||||
|
user.ActivationToken = ""
|
||||||
|
user.ActivateUntil = time.Now()
|
||||||
|
user.ActivatedAt = time.Now()
|
||||||
|
user.Activation = true
|
||||||
|
status, err := UpdateUserCore(&user)
|
||||||
|
if err != nil {
|
||||||
|
return status, err
|
||||||
|
}
|
||||||
|
status, err = SetCookie(<, user.Token)
|
||||||
|
return status, err
|
||||||
|
}
|
29
util/crypto/crypto.go
Fichier normal
29
util/crypto/crypto.go
Fichier normal
|
@ -0,0 +1,29 @@
|
||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GenerateMD5Hash(email string) string {
|
||||||
|
email = strings.ToLower(strings.TrimSpace(email))
|
||||||
|
hash := md5.New()
|
||||||
|
hash.Write([]byte(email))
|
||||||
|
return fmt.Sprintf("%x", hash.Sum(nil))
|
||||||
|
}
|
||||||
|
func GenerateRandomToken16() (string, error) {
|
||||||
|
return GenerateRandomToken(16)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateRandomToken32() (string, error) {
|
||||||
|
return GenerateRandomToken(32)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateRandomToken(n int) (string, error) {
|
||||||
|
token := make([]byte, n)
|
||||||
|
_, err := rand.Read(token)
|
||||||
|
// %x base 16, lower-case, two characters per byte
|
||||||
|
return fmt.Sprintf("%x", token), err
|
||||||
|
|
||||||
|
}
|
53
util/timeHelper/timeHelper.go
Fichier normal
53
util/timeHelper/timeHelper.go
Fichier normal
|
@ -0,0 +1,53 @@
|
||||||
|
package timeHelper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ewhal/nyaa/util/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func FewDaysLater(day int) time.Time {
|
||||||
|
return FewDurationLater(time.Duration(day) * 24 * time.Hour)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TwentyFourHoursLater() time.Time {
|
||||||
|
return FewDurationLater(time.Duration(24) * time.Hour)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SixHoursLater() time.Time {
|
||||||
|
return FewDurationLater(time.Duration(6) * time.Hour)
|
||||||
|
}
|
||||||
|
|
||||||
|
func InTimeSpan(start, end, check time.Time) bool {
|
||||||
|
log.Debugf("check after before: %s %t %t\n", check, check.After(start), check.Before(end))
|
||||||
|
return check.After(start) && check.Before(end)
|
||||||
|
}
|
||||||
|
|
||||||
|
func InTimeSpanNow(start, end time.Time) bool {
|
||||||
|
now := time.Now()
|
||||||
|
return InTimeSpan(start, end, now)
|
||||||
|
}
|
||||||
|
|
||||||
|
func FewDurationLater(duration time.Duration) time.Time {
|
||||||
|
// When Save time should considering UTC
|
||||||
|
// baseTime := time.Now()
|
||||||
|
// log.Debugf("basetime : %s", baseTime)
|
||||||
|
fewDurationLater := time.Now().Add(duration)
|
||||||
|
log.Debugf("time : %s", fewDurationLater)
|
||||||
|
return fewDurationLater
|
||||||
|
}
|
||||||
|
|
||||||
|
func FewDurationLaterMillisecond(duration time.Duration) int64 {
|
||||||
|
return FewDurationLater(duration).UnixNano() / int64(time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsExpired(expirationTime time.Time) bool {
|
||||||
|
// baseTime := time.Now()
|
||||||
|
// log.Debugf("basetime : %s", baseTime)
|
||||||
|
log.Debugf("expirationTime : %s", expirationTime)
|
||||||
|
// elapsed := time.Since(expirationTime)
|
||||||
|
// log.Debugf("elapsed : %s", elapsed)
|
||||||
|
after := time.Now().After(expirationTime)
|
||||||
|
log.Debugf("after : %t", after)
|
||||||
|
return after
|
||||||
|
}
|
Référencer dans un nouveau ticket