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

controllers.Logout   A

Complexity

Conditions 2

Size

Total Lines 17
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 13
nop 1
dl 0
loc 17
rs 9.75
c 0
b 0
f 0
1
package controllers
2
3
import (
4
	"fmt"
5
	"github.com/memnix/memnixrest/app/models"
6
	"github.com/memnix/memnixrest/app/queries"
7
	"github.com/memnix/memnixrest/pkg/database"
8
	"github.com/memnix/memnixrest/pkg/utils"
9
	"net/http"
10
	"os"
11
	"strconv"
12
	"strings"
13
	"time"
14
15
	"github.com/gofiber/fiber/v2"
16
	"github.com/golang-jwt/jwt"
17
	"golang.org/x/crypto/bcrypt"
18
)
19
20
var SecretKey = os.Getenv("SECRET")
21
22
// Register method to create a new user
23
// @Description Register a new user
24
// @Summary registers a new user
25
// @Tags Auth
26
// @Produce json
27
// @Param email body string true "Email"
28
// @Param password body string true "Password"
29
// @Param username body string true "Username"
30
// @Success 200 {object} models.User
31
// @Failure 404 "Error"
32
// @Failure 403 "Forbidden"
33
// @Router /register [post]
34
func Register(c *fiber.Ctx) error {
35
	var data map[string]string
36
	db := database.DBConn // DB Conn
37
38
	if err := c.BodyParser(&data); err != nil {
39
		return err
40
	}
41
42
	if len(data["password"]) > utils.MaxPasswordLen || len(data["username"]) > utils.MaxUsernameLen || len(data["email"]) > utils.MaxEmailLen {
43
		log := models.CreateLog(fmt.Sprintf("Error on register: %s - %s", data["username"], data["email"]), models.LogBadRequest).SetType(models.LogTypeWarning).AttachIDs(0, 0, 0)
44
		_ = log.SendLog()
45
		return queries.RequestError(c, http.StatusForbidden, utils.ErrorRequestFailed)
46
	}
47
48
	password, _ := bcrypt.GenerateFromPassword([]byte(data["password"]), 14)
49
	user := models.User{
50
		Username: data["username"],
51
		Email:    strings.ToLower(data["email"]),
52
		Password: password,
53
	}
54
55
	if err := db.Create(&user).Error; err != nil {
56
		log := models.CreateLog(fmt.Sprintf("Error on register: %s - %s", data["username"], data["email"]), models.LogAlreadyUsedEmail).SetType(models.LogTypeWarning).AttachIDs(user.ID, 0, 0)
57
		_ = log.SendLog()
58
		return queries.RequestError(c, http.StatusForbidden, utils.ErrorAlreadyUsedEmail)
59
	}
60
61
	log := models.CreateLog(fmt.Sprintf("Register: %s - %s", user.Username, user.Email), models.LogUserRegister).SetType(models.LogTypeInfo).AttachIDs(user.ID, 0, 0)
62
	_ = log.SendLog()
63
64
	return c.JSON(user)
65
}
66
67
// Login method to login user and return access with fresh token
68
// @Description Login user and return access with fresh token
69
// @Summary logins user and return access with fresh token
70
// @Tags Auth
71
// @Produce json
72
// @Param email body string true "Email"
73
// @Param password body string true "Password"
74
// @Success 200 {object} models.User
75
// @Failure 404 "Error"
76
// @Failure 400 "Incorrect password"
77
// @Failure 500 "Internal error"
78
// @Router /login [post]
79
func Login(c *fiber.Ctx) error {
80
	var data map[string]string
81
	db := database.DBConn // DB Conn
82
83
	if err := c.BodyParser(&data); err != nil {
84
		return err
85
	}
86
87
	var user models.User
88
89
	db.Where("email = ?", strings.ToLower(data["email"])).First(&user)
90
91
	// handle error
92
	if user.ID == 0 { //default Id when return nil
93
		log := models.CreateLog(fmt.Sprintf("Error on login: %s", data["email"]), models.LogIncorrectEmail).SetType(models.LogTypeWarning).AttachIDs(user.ID, 0, 0)
94
		_ = log.SendLog()
95
		c.Status(fiber.StatusBadRequest)
96
		return c.JSON(fiber.Map{
97
			"message": "Incorrect email or password !",
98
		})
99
	}
100
101
	// match password
102
	if err := bcrypt.CompareHashAndPassword(user.Password, []byte(data["password"])); err != nil {
103
		c.Status(fiber.StatusBadRequest)
104
		log := models.CreateLog(fmt.Sprintf("Error on login: %s", data["email"]), models.LogIncorrectPassword).SetType(models.LogTypeWarning).AttachIDs(user.ID, 0, 0)
105
		_ = log.SendLog()
106
		return c.JSON(fiber.Map{
107
			"message": "Incorrect email or password !",
108
		})
109
	}
110
111
	claims := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.StandardClaims{
112
		Issuer:    strconv.Itoa(int(user.ID)),
113
		ExpiresAt: time.Now().Add(time.Hour * 336).Unix(), //14 day
114
	}) // expires after 2 weeks
115
116
	token, err := claims.SignedString([]byte(SecretKey))
117
	if err != nil {
118
		log := models.CreateLog(fmt.Sprintf("Error on login: %s", err.Error()), models.LogLoginError).SetType(models.LogTypeError).AttachIDs(user.ID, 0, 0)
119
		_ = log.SendLog()
120
		c.Status(fiber.StatusInternalServerError)
121
		return c.JSON(fiber.Map{
122
			"message": "error when logging in !",
123
		})
124
	}
125
126
	log := models.CreateLog(fmt.Sprintf("Login: %s - %s", user.Username, user.Email), models.LogUserLogin).SetType(models.LogTypeInfo).AttachIDs(user.ID, 0, 0)
127
	if err = log.SendLog(); err != nil {
128
		fmt.Println(err)
129
	}
130
131
	return c.JSON(fiber.Map{
132
		"message": "Login Succeeded",
133
		"token":   token,
134
	})
135
}
136
137
func isConnected(c *fiber.Ctx) (int, models.ResponseAuth) {
138
	db := database.DBConn // DB Conn
139
	tokenString := extractToken(c)
140
	var user models.User
141
142
	token, err := jwt.Parse(tokenString, jwtKeyFunc)
143
	if err != nil {
144
		return fiber.StatusForbidden, models.ResponseAuth{
145
			Success: false,
146
			Message: "Failed to get the user. Try to logout/login. Otherwise, contact the support",
147
			User:    user,
148
		}
149
	}
150
151
	claims := token.Claims.(jwt.MapClaims)
152
153
	if res := db.Where("id = ?", claims["iss"]).First(&user); res.Error != nil {
154
		log := models.CreateLog(fmt.Sprintf("Error on check auth: %s", res.Error), models.LogLoginError).SetType(models.LogTypeError).AttachIDs(user.ID, 0, 0)
155
		_ = log.SendLog()
156
		c.Status(fiber.StatusInternalServerError)
157
		return fiber.StatusInternalServerError, models.ResponseAuth{
158
			Success: false,
159
			Message: "Failed to get the user. Try to logout/login. Otherwise, contact the support",
160
			User:    user,
161
		}
162
	}
163
164
	return fiber.StatusOK, models.ResponseAuth{
165
		Success: true,
166
		Message: "User is connected",
167
		User:    user,
168
	}
169
}
170
171
// User method to get connected user
172
// @Description To get connected user
173
// @Summary  gets connected user
174
// @Tags Auth
175
// @Produce json
176
// @Success 200 {object} models.User
177
// @Failure 404 "Error"
178
// @Failure 401 "Forbidden"
179
// @Security ApiKeyAuth
180
// @Router /user [get]
181
func User(c *fiber.Ctx) error {
182
183
	statusCode, response := isConnected(c)
184
185
	return c.Status(statusCode).JSON(response)
186
}
187
188
func AuthDebugMode(c *fiber.Ctx) models.ResponseAuth {
189
	db := database.DBConn // DB Conn
190
	var user models.User
191
192
	if res := db.Where("id = ?", 6).First(&user); res.Error != nil {
193
		c.Status(fiber.StatusInternalServerError)
194
		return models.ResponseAuth{
195
			Success: false,
196
			Message: "Failed to get the user. Try to logout/login. Otherwise, contact the support",
197
		}
198
	}
199
200
	return models.ResponseAuth{
201
		Success: true,
202
		Message: "Authenticated",
203
		User:    user,
204
	}
205
}
206
207
func CheckAuth(c *fiber.Ctx, p models.Permission) models.ResponseAuth {
208
	statusCode, response := isConnected(c)
209
210
	if statusCode != fiber.StatusOK {
211
		c.Status(statusCode)
212
		return response
213
	}
214
215
	user := response.User
216
217
	if user.Permissions < p {
218
		log := models.CreateLog(fmt.Sprintf("Permission error: %s | had %s but tried %s", user.Email, user.Permissions.ToString(), p.ToString()), models.LogPermissionForbidden).SetType(models.LogTypeWarning).AttachIDs(user.ID, 0, 0)
219
		_ = log.SendLog()
220
		c.Status(fiber.StatusUnauthorized)
221
		return models.ResponseAuth{
222
			Success: false,
223
			Message: "You don't have the right permissions to perform this request.",
224
		}
225
	}
226
227
	return models.ResponseAuth{
228
		Success: true,
229
		Message: "Authenticated",
230
		User:    user,
231
	}
232
}
233
234
// Logout method to de-auth connected user and delete token
235
// @Description Logout to de-auth connected user and delete token
236
// @Summary logouts and de-auth connected user and delete token
237
// @Tags Auth
238
// @Produce json
239
// @Success 200 "Logout"
240
// @Failure 404 "Error"
241
// @Failure 401 "Forbidden"
242
// @Security ApiKeyAuth[user]
243
// @Router /logout [post]
244
func Logout(c *fiber.Ctx) error {
245
	auth := CheckAuth(c, models.PermUser) // Check auth
246
	if !auth.Success {
247
		return c.Status(http.StatusUnauthorized).JSON(models.ResponseHTTP{
248
			Success: false,
249
			Message: auth.Message,
250
			Data:    nil,
251
			Count:   0,
252
		})
253
	}
254
255
	log := models.CreateLog(fmt.Sprintf("Logout: %s - %s", auth.User.Username, auth.User.Email), models.LogUserLogout).SetType(models.LogTypeInfo).AttachIDs(auth.User.ID, 0, 0)
256
	_ = log.SendLog()
257
258
	return c.JSON(fiber.Map{
259
		"message": "successfully logged out !",
260
		"token":   "",
261
	})
262
}
263
264
func extractToken(c *fiber.Ctx) string {
265
	token := c.Get("Authorization")
266
	// Normally Authorization HTTP header.
267
	onlyToken := strings.Split(token, " ")
268
	if len(onlyToken) == 2 {
269
		return onlyToken[1]
270
	}
271
272
	return ""
273
}
274
275
func jwtKeyFunc(token *jwt.Token) (interface{}, error) {
276
	return []byte(SecretKey), nil
277
}
278