/* * Password rules: * at least 7 letters * at least 1 number * at least 1 upper case * at least 1 special character */ func ValidatePassword(value, local string) error { fmt.Println("Validate password", value) if len(value) < 7 { return errors.New(i18n.Translate(local, i18nSec, "text03")) } var num, lower, upper, spec bool for _, r := range value { switch { case unicode.IsDigit(r): num = true case unicode.IsUpper(r): upper = true case unicode.IsLower(r): lower = true case unicode.IsSymbol(r), unicode.IsPunct(r): spec = true } } if num && lower && upper && spec { return nil } return errors.New(i18n.Translate(local, i18nSec, "text03")) }
func ValidateEmail(value, local string) error { fmt.Println("Validate email", value) pattern := regexp.MustCompile(`(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$`) if !pattern.MatchString(value) { return errors.New(i18n.Translate(local, i18nSec, "text04")) } // Validate, if email already available in db result := []struct { Email string `json: "acc.email"` }{} cq := &neoism.CypherQuery{ Statement: ` MATCH (acc:Account {email: {email}}) RETURN acc.email`, Parameters: neoism.Props{"email": value}, Result: &result, } db.Cypher(cq) if len(result) > 0 { return errors.New(i18n.Translate(local, i18nSec, "text05")) } return nil }
// Activate account, after user confirm email func Activate(email, local string) error { //Check if account is available if _, err := Read(email, local); err != nil { return err } //Check if account is already activated res := []struct { Email string `json:"n.email"` Activated bool `json:"n.activated,bool"` }{} cp := &neoism.CypherQuery{ Statement: ` MATCH (n:Account {email: {email}}) RETURN n.email, n.activated `, Parameters: neoism.Props{ "email": email, }, Result: &res, } if err := db.Cypher(cp); err != nil { return err } if len(res) > 0 && res[0].Activated { return errors.New(i18n.Translate(local, i18nSec, "text08")) } //Activate account and the activation date cp = &neoism.CypherQuery{ Statement: ` MATCH (n:Account {email: {email}}) SET n.activated = true, n.activated_at = {activated_at} RETURN n.email, n.activated `, Parameters: neoism.Props{ "email": email, "activated_at": time.Now().Unix(), }, Result: &res, } if err := db.Cypher(cp); err != nil { return err } if !res[0].Activated { return errors.New(i18n.Translate(local, i18nSec, "text09")) } return nil }
// Write last sign in from user func SignIn(email, password, local string) (*SignedUser, error) { //Check if account is available if _, err := Read(email, local); err != nil { return nil, err } res := []struct { Name string `json:"n.name"` Email string `json:"n.email"` HashedPassword string `json:"n.hashed_password"` Activated bool `json:"n.activated,bool"` }{} cp := &neoism.CypherQuery{ Statement: ` MATCH (n:Account {email: {email}}) RETURN n.name, n.email, n.hashed_password, n.activated `, Parameters: neoism.Props{ "email": email, }, Result: &res, } if err := db.Cypher(cp); err != nil { return nil, err } if !res[0].Activated { return nil, errors.New(i18n.Translate(local, "models/account", "text14")) } if !compareHashWithPassword(res[0].HashedPassword, password) { return nil, errors.New(i18n.Translate(local, "models/account", "text13")) } // Set sign in date cp = &neoism.CypherQuery{ Statement: ` MATCH (n:Account {email: {email}}) SET n.last_signin = {last_signin} `, Parameters: neoism.Props{ "email": email, "last_signin": time.Now().Unix(), }, } if err := db.Cypher(cp); err != nil { return nil, err } return &SignedUser{Name: res[0].Name, Email: res[0].Email}, nil }
func filterDirtyName(name, local string) error { fileIsRead := false // Check if file with dirty name has already read for _, value := range prohibitedName { if value != "" { fileIsRead = true } } if !fileIsRead { contents, err := ioutil.ReadFile(dirtyNameFile) if err != nil { panic("Could not find file for dirtyname validation.") } prohibitedName = strings.Split(string(contents[:]), "\n") } for _, value := range prohibitedName { if strings.Contains(name, value) { return errors.New(i18n.Translate(local, i18nSec, "text11")) } } return nil }
func Read(email, local string) (*SignedUser, error) { res := []struct { Name string `json:"n.name"` Email string `json:"n.email"` }{} cp := &neoism.CypherQuery{ Statement: ` MATCH (n:Account{ email: {email} }) RETURN n.name, n.email `, Parameters: neoism.Props{ "email": email, }, Result: &res, } if err := db.Cypher(cp); err != nil { return nil, err } //Could not find account if len(res) == 0 { return nil, errors.New(i18n.Translate(local, i18nSec, "text07")) } return &SignedUser{Name: res[0].Name, Email: res[0].Email}, nil }
// Read jwt token from cookie func (rcv *reader) readCookie() (string, error) { c, err := rcv.request.Cookie(cookie) if err != nil { return "", errors.New(i18n.Translate(rcv.local, i18nSec, "text12")) } return c.Value, nil }
// Set page title func (rcv *Controller) SetTitle(text string, para ...string) { t := i18n.Translate(rcv.Local, "page/title", text) if len(para) > 0 { rcv.title = t + " " + para[0] return } rcv.title = t }
func (rcv *writer) validateEmail() error { pattern := regexp.MustCompile(`(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$`) if !pattern.MatchString(rcv.signedUser.Email) { return errors.New(i18n.Translate(rcv.local, "models/account", "text04")) } return nil }
func ChangePassword(email, oldPassword, newPassword, local string) error { //Check if account is available if _, err := Read(email, local); err != nil { return err } res := []struct { Email string `json:"n.email"` HashedPassword string `json:"n.hashed_password"` }{} cp := &neoism.CypherQuery{ Statement: ` MATCH (n:Account {email: {email}}) RETURN n.email, n.hashed_password `, Parameters: neoism.Props{ "email": email, }, Result: &res, } if err := db.Cypher(cp); err != nil { return err } if !compareHashWithPassword(res[0].HashedPassword, oldPassword) { return errors.New(i18n.Translate(local, "models/account", "text12")) } //Testing security requirements for new Password if err := ValidatePassword(newPassword, local); err != nil { return err } cp = &neoism.CypherQuery{ Statement: ` MATCH (n:Account {email: {email}}) SET n.hashed_password = {hashed_password} `, Parameters: neoism.Props{ "email": email, "hashed_password": createHashPassword(newPassword), }, } if err := db.Cypher(cp); err != nil { return err } return nil }
/* Name rules: * at min 6 and max 15 character * will convert name to lower case * local is for translating, if error occurs */ func ValidateName(value, local string) error { fmt.Println("Validate name: ", value) pattern := regexp.MustCompile(`^[a-zA-Z0-9]{6,15}$`) nameLowerCase := strings.ToLower(value) if !pattern.MatchString(nameLowerCase) { return errors.New(i18n.Translate(local, i18nSec, "text01")) } if err := filterDirtyName(nameLowerCase, local); err != nil { return err } // Validate, if name already available in db res := []struct { Name string `json: "acc.name"` }{} cq := &neoism.CypherQuery{ Statement: ` MATCH (acc:Account {name: {name}}) RETURN acc.name `, Parameters: neoism.Props{"name": nameLowerCase}, Result: &res, } db.Cypher(cq) if len(res) > 0 { return errors.New(i18n.Translate(local, i18nSec, "text02")) } return nil }
// Validate, if the user is authorized to use the handler. // Will use like middleware. func AllowVisit(handler http.Handler) http.Handler { n := negroni.New(negroni.HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { signed := IsSignedInUser(r) if !signed { flash.Set(r, "E", i18n.Translate(httphead.GetLang(r), i18nSec, "text12")) http.Redirect(rw, r, "/user/signin", 303) return } // If authorization is passed next(rw, r) })) n.UseHandler(handler) return n }
// Validate if the entered numbers match to stored number func Validate(r *http.Request, certification, human string) error { // Error object err := errors.New(i18n.Translate(httphead.GetLang(r), "controller/account", "text09")) if human == "" { return err } decoded, err := base64.StdEncoding.DecodeString(certification) if err != nil { return err } c := redis.Get() // Configure out, if the image still available. values, err := goredis.Bytes(c.Do("GET", string(decoded))) if err != nil { return err } ns := make([]byte, len(human)) for i := range ns { d := human[i] switch { case '0' <= d && d <= '9': ns[i] = d - '0' case d == ' ' || d == ',': // ignore default: return err } } if !bytes.Equal(values, ns) { return err } return nil }
// Close account, if user did some prohibited func Close(email, reason, local string) error { //Check if account is available if _, err := Read(email, local); err != nil { return err } res := []struct { Email string `json:"n.email"` Closed bool `json:"n.closed,bool"` }{} cp := &neoism.CypherQuery{ Statement: ` MATCH (n:Account {email: {email}}) SET n.closed = true, n.closed_at = {closed_at}, n.closed_reason = {closed_reason} RETURN n.email, n.closed `, Parameters: neoism.Props{ "email": email, "closed_at": time.Now().Unix(), "closed_reason": reason, }, Result: &res, } if err := db.Cypher(cp); err != nil { return err } if !res[0].Closed { return errors.New(i18n.Translate(local, "models/account", "text10")) } return nil }
// Translate text in html file func (rcv *Controller) setHtmlText(section, text string) string { return i18n.Translate(rcv.Local, section, text) }
func (rcv *notSignedError) Error() string { return i18n.Translate(rcv.local, i18nSec, "text12") }
// Read text definition from i18n. Parameter text // is the key, that defined in language file. func (rcv *Controller) Translate(text string) string { return i18n.Translate(rcv.Local, rcv.section, text) }
/* * Account rules: * name will be saved lower case in database */ func Create(name, email, password, local string, termOf bool) []error { wait := new(sync.WaitGroup) mutex := new(sync.Mutex) errs := make([]error, 3) if !termOf { errs = append(errs, errors.New(i18n.Translate(local, i18nSec, "text06"))) } wait.Add(1) go func() { if err := ValidateName(name, local); err != nil { mutex.Lock() errs = append(errs, err) mutex.Unlock() } wait.Done() }() wait.Add(1) go func() { if err := ValidateEmail(email, local); err != nil { mutex.Lock() errs = append(errs, err) mutex.Unlock() } wait.Done() }() wait.Add(1) go func() { if err := ValidatePassword(password, local); err != nil { mutex.Lock() errs = append(errs, err) mutex.Unlock() } wait.Done() }() wait.Wait() // If errors appear if len(errs) > 0 { return errs } newAccount := new(account) newAccount.name = strings.ToLower(name) newAccount.email = email newAccount.hashedPassword = createHashPassword(password) newAccount.createdAt = time.Now().Unix() newAccount.termOf = termOf res := []struct { N neoism.Node }{} cp := &neoism.CypherQuery{ Statement: ` CREATE (n:Account {name: {name}, email: {email}, hashed_password: {hashed_password}, term_of: {term_of}, created_at: {created_at}, activated: {activated}, activated_at: {activated_at}, closed: {closed}, closed_at: {closed_at}, closed_reason: {closed_reason}, last_signin: {last_signin}, recovery_at: {recovery_at} }) RETURN n `, Parameters: neoism.Props{ "name": newAccount.name, "email": newAccount.email, "hashed_password": newAccount.hashedPassword, "created_at": newAccount.createdAt, "term_of": newAccount.termOf, "activated": newAccount.activated, "activated_at": newAccount.activatedAt, "closed": newAccount.closed, "closed_at": newAccount.closedAt, "closed_reason": newAccount.closedReason, "last_signin": newAccount.lastSignIn, "recovery_at": newAccount.recoveryAt, }, Result: &res, } if err := db.Cypher(cp); err != nil { return []error{err} } return nil }