Passed
Pull Request — main (#42)
by Yume
01:11
created

controllers.ResetPasswordConfirm   B

Complexity

Conditions 6

Size

Total Lines 48
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 30
nop 1
dl 0
loc 48
rs 8.2266
c 0
b 0
f 0
1
package controllers
2
3
import (
4
	"bytes"
5
	"fmt"
6
	"github.com/memnix/memnixrest/app/models"
7
	"github.com/memnix/memnixrest/app/queries"
8
	"github.com/memnix/memnixrest/pkg/database"
9
	"github.com/memnix/memnixrest/pkg/utils"
10
	"golang.org/x/crypto/bcrypt"
11
	"net/http"
12
	"strconv"
13
	"strings"
14
	"time"
15
16
	"github.com/gofiber/fiber/v2"
17
)
18
19
// GET
20
21
// GetAllUsers method to get all users
22
// @Description Get all users.  Shouldn't really be used
23
// @Summary gets a list of user
24
// @Tags User
25
// @Produce json
26
// @Success 200 {object} models.User
27
// @Security Admin
28
// @Deprecated
29
// @Router /v1/users [get]
30
func GetAllUsers(c *fiber.Ctx) error {
31
	db := database.DBConn // DB Conn
32
33
	auth := CheckAuth(c, models.PermAdmin) // Check auth
34
	if !auth.Success {
35
		return queries.AuthError(c, &auth)
36
	}
37
38
	var users []models.User
39
40
	if res := db.Find(&users); res.Error != nil {
41
		return queries.RequestError(c, http.StatusInternalServerError, res.Error.Error())
42
	}
43
44
	return c.Status(http.StatusOK).JSON(models.ResponseHTTP{
45
		Success: true,
46
		Message: "Get all users",
47
		Data:    users,
48
		Count:   len(users),
49
	})
50
}
51
52
// GetUserByID method to get a user
53
// @Description Get a user by ID.
54
// @Summary gets a user
55
// @Tags User
56
// @Produce json
57
// @Param id path int true "ID"
58
// @Success 200 {object} models.User
59
// @Security Admin
60
// @Router /v1/users/id/{id} [get]
61
func GetUserByID(c *fiber.Ctx) error {
62
	db := database.DBConn // DB Conn
63
64
	auth := CheckAuth(c, models.PermAdmin) // Check auth
65
	if !auth.Success {
66
		return queries.AuthError(c, &auth)
67
	}
68
69
	// Params
70
	id := c.Params("id")
71
72
	user := new(models.User)
73
74
	if err := db.First(&user, id).Error; err != nil {
75
		return queries.RequestError(c, http.StatusInternalServerError, err.Error())
76
	}
77
78
	return c.Status(http.StatusOK).JSON(models.ResponseHTTP{
79
		Success: true,
80
		Message: "Success get user by ID.",
81
		Data:    *user,
82
		Count:   1,
83
	})
84
}
85
86
// SetTodayConfig method to set a config
87
// @Description Set the today config for a deck
88
// @Summary sets the today config for a deck
89
// @Tags User
90
// @Produce json
91
// @Accept json
92
// @Param deckId path int true "Deck ID"
93
// @Param config body models.DeckConfig true "Deck Config"
94
// @Success 200
95
// @Router /v1/users/settings/{deckId}/today [post]
96
func SetTodayConfig(c *fiber.Ctx) error {
97
	db := database.DBConn // DB Conn
98
99
	// Params
100
	deckID := c.Params("deckID")
101
	deckidInt, _ := strconv.ParseUint(deckID, 10, 32)
102
103
	deckConfig := new(models.DeckConfig)
104
105
	auth := CheckAuth(c, models.PermUser) // Check auth
106
	if !auth.Success {
107
		return queries.AuthError(c, &auth)
108
	}
109
110
	if err := c.BodyParser(&deckConfig); err != nil {
111
		log := models.CreateLog(fmt.Sprintf("Error on SetTodayConfig: %s from %s", err.Error(), auth.User.Email), models.LogBodyParserError).SetType(models.LogTypeError).AttachIDs(auth.User.ID, uint(deckidInt), 0)
112
		_ = log.SendLog()
113
		return queries.RequestError(c, http.StatusBadRequest, err.Error())
114
	}
115
116
	access := new(models.Access)
117
	if err := db.Joins("User").Joins("Deck").Where("accesses.user_id = ? AND accesses.deck_id = ?", auth.User.ID, deckID).Find(&access).Error; err != nil {
118
		log := models.CreateLog(fmt.Sprintf("Forbidden from %s on deck %d - SetTodayConfig", auth.User.Email, deckidInt), models.LogDeckCardLimit).SetType(models.LogTypeWarning).AttachIDs(auth.User.ID, uint(deckidInt), 0)
119
		_ = log.SendLog()
120
		return queries.RequestError(c, http.StatusBadRequest, utils.ErrorNotSub)
121
	}
122
123
	if access.Permission == 0 {
124
		return queries.RequestError(c, http.StatusForbidden, utils.ErrorNotSub)
125
	}
126
127
	access.ToggleToday = deckConfig.TodaySetting
128
129
	db.Save(access)
130
131
	return c.Status(http.StatusOK).JSON(models.ResponseHTTP{
132
		Success: true,
133
		Message: "Success updated deck config",
134
		Data:    nil,
135
		Count:   1,
136
	})
137
}
138
139
// ResetPassword method to request a password reset
140
// @Description Request a password reset
141
// @Summary gets a code to reset a password
142
// @Tags User
143
// @Produce json
144
// @Accept json
145
// @Param config body string true "Email"
146
// @Success 200
147
// @Router /v1/users/resetpassword [post]
148
func ResetPassword(c *fiber.Ctx) error {
149
	db := database.DBConn
150
151
	var body struct {
152
		Email string `json:"email"`
153
	}
154
155
	if err := c.BodyParser(&body); err != nil {
156
		return queries.RequestError(c, http.StatusBadRequest, err.Error())
157
	}
158
159
	email := strings.ToLower(body.Email)
160
	email = strings.TrimSpace(email)
161
162
	if err := db.Where("email = ?", email).First(&models.User{}).Error; err != nil {
163
		return queries.RequestError(c, http.StatusBadRequest, err.Error())
164
	}
165
166
	_, found := database.Cache.Get(email)
167
	if found {
168
		return queries.RequestError(c, http.StatusBadRequest, "A password reset has already been sent to this email address. Please check your email for the link.")
169
	}
170
171
	token := utils.GenerateSecretCode(3)
172
	database.Cache.Set(email, token, time.Minute*10)
173
174
	// Send email
175
	go func() {
176
		err := utils.SendEmail(email, "Password Reset", "Your password reset code is: "+token)
177
		if err != nil {
178
			log := models.CreateLog(fmt.Sprintf("Error on ResetPassword: %s", err.Error()), models.LogBodyParserError).SetType(models.LogTypeError).AttachIDs(0, 0, 0)
179
			_ = log.SendLog()
180
		}
181
	}()
182
183
	log := models.CreateLog(fmt.Sprintf("Password reset request for %s", email), models.LogUserPasswordReset).SetType(models.LogTypeInfo).AttachIDs(0, 0, 0)
184
	_ = log.SendLog()
185
186
	return c.Status(http.StatusOK).JSON(models.ResponseHTTP{
187
		Success: true,
188
		Message: "Success sent password reset email",
189
		Data:    nil,
190
		Count:   1,
191
	})
192
}
193
194
// ResetPasswordConfirm method to confirm a password reset
195
// @Description Confirm a password reset
196
// @Summary reset a password
197
// @Tags User
198
// @Produce json
199
// @Accept json
200
// @Param config body models.PasswordResetConfirm true "Password reset"
201
// @Success 200
202
// @Router /v1/users/confirmpassword [post]
203
func ResetPasswordConfirm(c *fiber.Ctx) error {
204
	db := database.DBConn
205
206
	var body models.PasswordResetConfirm
207
208
	if err := c.BodyParser(&body); err != nil {
209
		return queries.RequestError(c, http.StatusBadRequest, err.Error())
210
	}
211
212
	email := strings.ToLower(body.Email)
213
	email = strings.TrimSpace(email)
214
215
	token, found := database.Cache.Get(email)
216
	if !found {
217
		return queries.RequestError(c, http.StatusBadRequest, "Your password reset code has expired. Please request a new one.")
218
	}
219
220
	if token != body.Code {
221
		return queries.RequestError(c, http.StatusBadRequest, "Invalid password reset code.")
222
	}
223
224
	user := new(models.User)
225
	if err := db.Where("email = ?", email).First(&user).Error; err != nil {
226
		return queries.RequestError(c, http.StatusBadRequest, err.Error())
227
	}
228
229
	// Register checks
230
	if len(body.Pass) > utils.MaxPasswordLen {
231
		log := models.CreateLog(fmt.Sprintf("Error on reset password: %s - %s", user.Username, user.Email), models.LogBadRequest).SetType(models.LogTypeWarning).AttachIDs(user.ID, 0, 0)
232
		_ = log.SendLog()
233
		return queries.RequestError(c, http.StatusForbidden, utils.ErrorRequestFailed)
234
	}
235
236
	password, _ := bcrypt.GenerateFromPassword([]byte(body.Pass), 10) // Hash password
237
238
	user.Password = password
239
	db.Save(user)
240
241
	database.Cache.Delete(email)
242
243
	log := models.CreateLog(fmt.Sprintf("Password reset for %s", email), models.LogUserPasswordChanged).SetType(models.LogTypeInfo).AttachIDs(user.ID, 0, 0)
244
	_ = log.SendLog()
245
246
	return c.Status(http.StatusOK).JSON(models.ResponseHTTP{
247
		Success: true,
248
		Message: "Success updated password",
249
		Data:    nil,
250
		Count:   1,
251
	})
252
}
253
254
// PUT
255
256
// UpdateUserByID function
257
func UpdateUserByID(c *fiber.Ctx) error {
258
	db := database.DBConn // DB Conn
259
260
	// Params
261
	id := c.Params("id")
262
263
	auth := CheckAuth(c, models.PermAdmin) // Check auth
264
	if !auth.Success {
265
		return queries.AuthError(c, &auth)
266
	}
267
268
	user := new(models.User)
269
270
	if err := db.First(&user, id).Error; err != nil {
271
		return queries.RequestError(c, http.StatusInternalServerError, err.Error())
272
	}
273
274
	if res := UpdateUser(c, user); !res.Success {
275
		return queries.RequestError(c, http.StatusInternalServerError, res.Message)
276
	}
277
278
	return c.Status(http.StatusOK).JSON(models.ResponseHTTP{
279
		Success: true,
280
		Message: "Success update user by ID",
281
		Data:    *user,
282
		Count:   1,
283
	})
284
}
285
286
// UpdateUser function
287
func UpdateUser(c *fiber.Ctx, u *models.User) *models.ResponseHTTP {
288
	db := database.DBConn
289
290
	email, password, permissions := u.Email, u.Password, u.Permissions
291
292
	res := new(models.ResponseHTTP)
293
294
	if err := c.BodyParser(&u); err != nil {
295
		res.GenerateError(err.Error())
296
		return res
297
	}
298
299
	if u.Email != email || !bytes.Equal(u.Password, password) || u.Permissions != permissions {
300
		res.GenerateError(utils.ErrorBreak)
301
		return res
302
	}
303
304
	db.Save(u)
305
306
	res.GenerateSuccess("Success update user", nil, 0)
307
	return res
308
}
309