aboutsummaryrefslogblamecommitdiff
path: root/api/user.go
blob: b285a20d9a35b8d8d6d761efabeb4c75bd120688 (plain) (tree)
1
2
3
4
5
6
7
8
9




                

                 
 
                                         

                                  
                                                   





                                                                          













                                                                                                                              













                                                                                                          





                                      











                                                  

                                       















































                                                                                                                                       






                                                                                                                       








                                                                          







                                                                                                                                                                          
                                                                   
























                                                                                                                            




                                                 
                                                                

 



















































                                                                                                                    


















                                                       
package api

import (
	"fmt"
	"regexp"
	"strconv"
	"time"

	"github.com/dchest/passwordreset"
	"github.com/gin-gonic/gin"

	"git.immae.eu/Cryptoportfolio/Front.git/db"
)

const (
	VALID_EMAIL_REGEX = `(?i)^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$`
)

func UserConfirmed(c *gin.Context) *Error {
	user, exists := c.Get("user")

	if !exists {
		return &Error{NotAuthorized, "not authorized", fmt.Errorf("no user key in context")}
	}

	if user.(db.User).Status != db.Confirmed {
		return &Error{UserNotConfirmed, "user awaiting admin validation", fmt.Errorf("user '%v' not confirmed", user)}
	}

	return nil
}

func UserIsAdmin(c *gin.Context) *Error {
	user, exists := c.Get("user")

	if !exists {
		return &Error{NotAuthorized, "not authorized", fmt.Errorf("no user key in context")}
	}

	if user.(db.User).Role != db.RoleAdmin {
		return &Error{NotAuthorized, "not authorized", fmt.Errorf("user '%v' is not admin", user)}
	}

	return nil
}

func GetUser(c *gin.Context) db.User {
	user, _ := c.Get("user")

	return user.(db.User)
}

func IsValidEmailAddress(email string) bool {
	r := regexp.MustCompile(VALID_EMAIL_REGEX)

	return r.MatchString(email)
}

type SignParams struct {
	Email    string
	Password string
}

type SignResult struct {
	Token   string `json:"token"`
	IsAdmin bool   `json:"isAdmin"`
}

func (s SignParams) Validate() *Error {
	if !IsValidEmailAddress(s.Email) {
		return &Error{InvalidEmail, "invalid email", fmt.Errorf("'%v' is not a valid email", s.Email)}
	}

	if s.Password == "" {
		return &Error{InvalidPassword, "invalid password", fmt.Errorf("invalid password")}
	}

	return nil
}

type SignupQuery struct {
	In SignParams
}

func (q SignupQuery) ValidateParams() *Error {
	return q.In.Validate()
}

func (q SignupQuery) Run() (interface{}, *Error) {
	user, err := db.GetUserByEmail(q.In.Email)
	if err != nil {
		return nil, NewInternalError(err)
	}

	if user != nil {
		return nil, &Error{EmailExists, "email already taken", fmt.Errorf("'%v' is already registered '%v'", q.In.Email, user)}
	}

	newUser := db.User{Email: q.In.Email, Status: db.AwaitingConfirmation}
	newUser.PasswordHash, err = db.HashPassword(q.In.Password)
	if err != nil {
		return nil, NewInternalError(err)
	}

	err = db.InsertUser(&newUser)
	if err != nil {
		return nil, NewInternalError(err)
	}

	token, err := CreateJwtToken(newUser.Id)
	if err != nil {
		return nil, NewInternalError(fmt.Errorf("cannot create jwt token %v", err))
	}

	if CONFIG.FreeSMSUser != "" {
		err := SendSMS(CONFIG.FreeSMSUser, CONFIG.FreeSMSPass, fmt.Sprintf("New user signup '%v'", q.In.Email))
		if err != nil {
			return nil, NewInternalError(err)
		}
	}

	configMap := make(map[string]string)
	configMap["key"] = ""
	configMap["secret"] = ""

	_, err = db.SetUserMarketConfig(newUser.Id, "poloniex", configMap)
	if err != nil {
		return nil, NewInternalError(err)
	}

	if MAIL_CONFIG.IsEnabled {
		mailConfirmationToken := passwordreset.NewToken(q.In.Email, time.Hour*24*1, []byte(strconv.FormatUint(uint64(newUser.Status), 10)), PASSWORD_RESET_SECRET)
		err = SendConfirmationMail(q.In.Email, mailConfirmationToken)
		if err != nil {
			return nil, NewInternalError(err)
		}
	}

	return SignResult{token, newUser.Role == db.RoleAdmin}, nil
}

type SigninQuery struct {
	In SignParams
}

func (q SigninQuery) ValidateParams() *Error {
	return q.In.Validate()
}

func (q SigninQuery) Run() (interface{}, *Error) {
	user, err := db.GetUserByEmail(q.In.Email)
	if err != nil {
		return nil, NewInternalError(err)
	}

	if user == nil {
		return nil, &Error{InvalidCredentials, "invalid credentials", fmt.Errorf("no email '%v' found", q.In.Email)}
	}

	err = db.ValidatePassword(q.In.Password, user.PasswordHash)
	if err != nil {
		return nil, &Error{InvalidCredentials, "invalid credentials", err}
	}

	token, err := CreateJwtToken(user.Id)
	if err != nil {
		return nil, NewInternalError(err)
	}

	return SignResult{token, user.Role == db.RoleAdmin}, nil
}

type ConfirmEmailQuery struct {
	In struct {
		Token string
	}
}

func (q ConfirmEmailQuery) ValidateParams() *Error {

	if q.In.Token == "" {
		return &Error{BadRequest, "invalid token", fmt.Errorf("invalid token")}
	}

	return nil
}

func (q ConfirmEmailQuery) Run() (interface{}, *Error) {
	var user *db.User

	email, err := passwordreset.VerifyToken(q.In.Token, func(email string) ([]byte, error) {
		var err error
		user, err = db.GetUserByEmail(email)
		if err != nil {
			return nil, err
		}

		if user == nil {
			return nil, fmt.Errorf("'%v' is not registered", email)
		}

		return []byte(strconv.FormatUint(uint64(user.Status), 10)), nil

	}, PASSWORD_RESET_SECRET)

	if err != nil && (err == passwordreset.ErrExpiredToken) {
		return nil, &Error{BadRequest, "expired token", fmt.Errorf("expired token")}
	} else if err != nil && (err == passwordreset.ErrMalformedToken || err == passwordreset.ErrWrongSignature) {
		return nil, &Error{BadRequest, "wrong token", fmt.Errorf("wrong token")}
	} else if err != nil {
		return nil, NewInternalError(err)
	}

	if user == nil {
		return nil, &Error{BadRequest, "bad request", fmt.Errorf("no user found for email '%v'", email)}
	}

	err = db.SetUserStatus(user, db.Confirmed)
	if err != nil {
		return nil, NewInternalError(err)
	}

	return nil, nil
}

type UserAccountQuery struct {
	In struct {
		User db.User
	}
	Out struct {
		Email string `json:"email"`
	}
}

func (q UserAccountQuery) ValidateParams() *Error {
	return nil
}

func (q UserAccountQuery) Run() (interface{}, *Error) {
	q.Out.Email = q.In.User.Email

	return q.Out, nil
}