forked from NyaaPantsu/nyaa
-
Notifications
You must be signed in to change notification settings - Fork 0
/
user.go
178 lines (155 loc) · 5.01 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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
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
}