/
user.go
90 lines (72 loc) · 1.82 KB
/
user.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
package main
import (
"errors"
"log"
"strconv"
"strings"
"time"
"github.com/garyburd/redigo/redis"
"golang.org/x/crypto/bcrypt"
)
//User is a hashed password
type User struct {
Password []byte `redis:"password"`
}
const loginAttempts int64 = 10
var errExceededAttempts = errors.New("login count has been exceeded")
type userPass struct {
Username, Pass string
}
func (up *userPass) path(path ...string) string {
u := "oauth:users:" + up.Username
if len(path) > 0 {
u += ":" + strings.Join(path, ":")
}
return u
}
//Validate a user/pass within redis
func validateUser(up userPass) (bool, error) {
ct, err := rds.Do("HINCRBY", up.path(), "attempts", 1)
if err != nil {
return false, err
}
if ct.(int64) > loginAttempts {
rds.Send("MULTI")
rds.Send("HSETNX", up.path(), "reset", time.Now().Add(10*time.Second).Unix())
rds.Send("HGET", up.path(), "reset")
res, err := rds.Do("EXEC")
if err != nil {
return false, err
}
unix, err := strconv.ParseInt(string((res.([]interface{}))[1].([]byte)), 10, 64)
//If the reset time is not in the past, return err
if err == nil && time.Unix(int64(unix), 0).Before(time.Now()) {
defer func() {
//Clear out password attempts
rds.Send("MULTI")
rds.Send("HDEL", up.path(), "reset")
rds.Send("HDEL", up.path(), "attempts")
rds.Do("EXEC")
}()
} else {
if err != nil {
log.Println("login attempt reset error", err)
}
return false, errExceededAttempts
}
}
vals, err := redis.Values(rds.Do("HGETALL", up.path()))
if err != nil || vals == nil {
return false, err
}
var u User
if err := redis.ScanStruct(vals, &u); err != nil {
return false, err
}
bcErr := bcrypt.CompareHashAndPassword(u.Password, []byte(up.Pass))
valid := bcErr == nil
if valid {
defer rds.Do("HDECRBY", up.path(), "attempts", 1)
}
return valid, bcErr
}