Initial commit
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gookit/validate"
|
||||
"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
|
||||
)
|
||||
|
||||
func ValidateApiLoginPost(c *fiber.Ctx) error {
|
||||
var login models.Login
|
||||
if err := c.BodyParser(&login); err != nil {
|
||||
return c.Status(401).JSON(fiber.Map{
|
||||
"error": true,
|
||||
"message": "Invalid Credentials",
|
||||
})
|
||||
}
|
||||
v := validate.Struct(login)
|
||||
if !v.Validate() {
|
||||
return c.Status(401).JSON(fiber.Map{
|
||||
"error": true,
|
||||
"message": "Invalid Credentials",
|
||||
})
|
||||
}
|
||||
user, err := login.CheckLogin() //nolint:wsl
|
||||
|
||||
if err != nil {
|
||||
return c.Status(401).JSON(fiber.Map{
|
||||
"error": true,
|
||||
"message": "Invalid Credentials",
|
||||
})
|
||||
}
|
||||
c.Locals("user", user)
|
||||
return c.Next()
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gookit/validate"
|
||||
"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
|
||||
)
|
||||
|
||||
func ValidateApiRegisterPost(c *fiber.Ctx) error {
|
||||
var register models.RegisterForm
|
||||
if err := c.BodyParser(®ister); err != nil {
|
||||
return c.Status(401).JSON(fiber.Map{
|
||||
"error": true,
|
||||
"message": err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
v := validate.Struct(register)
|
||||
if !v.Validate() {
|
||||
return c.Status(401).JSON(fiber.Map{
|
||||
"error": true,
|
||||
"message": v.Errors,
|
||||
})
|
||||
}
|
||||
c.Locals("register", register)
|
||||
return c.Next()
|
||||
}
|
||||
@@ -0,0 +1,272 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/oarkflow/log"
|
||||
|
||||
"github.com/sujit-baniya/fiber-boilerplate/app"
|
||||
config2 "github.com/sujit-baniya/fiber-boilerplate/config"
|
||||
"github.com/sujit-baniya/fiber-boilerplate/pkg/auth"
|
||||
|
||||
"github.com/form3tech-oss/jwt-go"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
// Config defines the config for BasicAuth middleware
|
||||
type AuthConfig struct {
|
||||
// Filter defines a function to skip middleware.
|
||||
// Optional. Default: nil
|
||||
Filter func(*fiber.Ctx) bool
|
||||
|
||||
// SuccessHandler defines a function which is executed for a valid token.
|
||||
// Optional. Default: nil
|
||||
SuccessHandler fiber.Handler
|
||||
|
||||
// ErrorHandler defines a function which is executed for an invalid token.
|
||||
// It may be used to define a custom JWT error.
|
||||
// Optional. Default: 401 Invalid or expired JWT
|
||||
ErrorHandler fiber.ErrorHandler
|
||||
|
||||
// Signing key to validate token. Used as fallback if SigningKeys has length 0.
|
||||
// Required. This or SigningKeys.
|
||||
SigningKey interface{}
|
||||
|
||||
// Map of signing keys to validate token with kid field usage.
|
||||
// Required. This or SigningKey.
|
||||
SigningKeys map[string]interface{}
|
||||
|
||||
// Signing method, used to check token signing method.
|
||||
// Optional. Default: "HS256".
|
||||
// Possible values: "HS256", "HS384", "HS512", "ES256", "ES384", "ES512", "RS256", "RS384", "RS512"
|
||||
SigningMethod string
|
||||
|
||||
// Context key to store user information from the token into context.
|
||||
// Optional. Default: "user".
|
||||
ContextKey string
|
||||
|
||||
// Claims are extendable claims data defining token content.
|
||||
// Optional. Default value jwt.MapClaims
|
||||
Claims jwt.Claims
|
||||
|
||||
// TokenLookup is a string in the form of "<source>:<name>" that is used
|
||||
// to extract token from the request.
|
||||
// Optional. Default value "header:Authorization".
|
||||
// Possible values:
|
||||
// - "header:<name>"
|
||||
// - "query:<name>"
|
||||
// - "param:<name>"
|
||||
// - "cookie:<name>"
|
||||
TokenLookup string
|
||||
|
||||
// AuthScheme to be used in the Authorization header.
|
||||
// Optional. Default: "Bearer".
|
||||
AuthScheme string
|
||||
|
||||
keyFunc jwt.Keyfunc
|
||||
}
|
||||
|
||||
// New ...
|
||||
func Authenticate(config ...AuthConfig) fiber.Handler {
|
||||
// Init config
|
||||
var cfg AuthConfig
|
||||
if len(config) > 0 {
|
||||
cfg = config[0]
|
||||
}
|
||||
if cfg.SuccessHandler == nil {
|
||||
cfg.SuccessHandler = func(c *fiber.Ctx) error {
|
||||
return c.Next()
|
||||
}
|
||||
}
|
||||
if cfg.ErrorHandler == nil {
|
||||
cfg.ErrorHandler = func(c *fiber.Ctx, err error) error {
|
||||
var er fiber.Error
|
||||
if err.Error() == "Missing or malformed JWT" {
|
||||
er.Code = fiber.StatusBadRequest
|
||||
} else {
|
||||
er.Code = fiber.StatusUnauthorized
|
||||
}
|
||||
er.Message = err.Error()
|
||||
return config2.CustomErrorHandler(c, &er)
|
||||
}
|
||||
}
|
||||
if cfg.SigningKey == nil && len(cfg.SigningKeys) == 0 {
|
||||
log.Error().Msg("Fiber: JWT middleware requires signing key")
|
||||
}
|
||||
if cfg.SigningMethod == "" {
|
||||
cfg.SigningMethod = "HS256"
|
||||
}
|
||||
if cfg.ContextKey == "" {
|
||||
cfg.ContextKey = "user"
|
||||
}
|
||||
if cfg.Claims == nil {
|
||||
cfg.Claims = jwt.MapClaims{}
|
||||
}
|
||||
if cfg.TokenLookup == "" {
|
||||
cfg.TokenLookup = "header:" + fiber.HeaderAuthorization
|
||||
}
|
||||
if cfg.AuthScheme == "" {
|
||||
cfg.AuthScheme = "Bearer"
|
||||
}
|
||||
cfg.keyFunc = func(t *jwt.Token) (interface{}, error) {
|
||||
// Check the signing method
|
||||
if t.Method.Alg() != cfg.SigningMethod {
|
||||
return nil, fmt.Errorf("Unexpected jwt signing method=%v", t.Header["alg"])
|
||||
}
|
||||
if len(cfg.SigningKeys) > 0 {
|
||||
if kid, ok := t.Header["kid"].(string); ok {
|
||||
if key, ok := cfg.SigningKeys[kid]; ok {
|
||||
return key, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("Unexpected jwt key id=%v", t.Header["kid"])
|
||||
}
|
||||
return cfg.SigningKey, nil
|
||||
}
|
||||
// Initialize
|
||||
extractors := make([]func(c *fiber.Ctx) (string, error), 0)
|
||||
rootParts := strings.Split(cfg.TokenLookup, ",")
|
||||
for _, rootPart := range rootParts {
|
||||
parts := strings.Split(strings.TrimSpace(rootPart), ":")
|
||||
|
||||
switch parts[0] {
|
||||
case "header":
|
||||
extractors = append(extractors, jwtFromHeader(parts[1], cfg.AuthScheme))
|
||||
case "query":
|
||||
extractors = append(extractors, jwtFromQuery(parts[1]))
|
||||
case "param":
|
||||
extractors = append(extractors, jwtFromParam(parts[1]))
|
||||
case "cookie":
|
||||
extractors = append(extractors, jwtFromCookie(parts[1]))
|
||||
}
|
||||
}
|
||||
// Return middleware handler
|
||||
return func(c *fiber.Ctx) error {
|
||||
// Filter request to skip middleware
|
||||
if cfg.Filter != nil && cfg.Filter(c) {
|
||||
return c.Next()
|
||||
}
|
||||
var auth string
|
||||
var err error
|
||||
|
||||
for _, extractor := range extractors {
|
||||
auth, err = extractor(c)
|
||||
if auth != "" && err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return cfg.ErrorHandler(c, err)
|
||||
}
|
||||
token := new(jwt.Token)
|
||||
if _, ok := cfg.Claims.(jwt.MapClaims); ok {
|
||||
token, err = jwt.Parse(auth, cfg.keyFunc)
|
||||
} else {
|
||||
t := reflect.ValueOf(cfg.Claims).Type().Elem()
|
||||
claims := reflect.New(t).Interface().(jwt.Claims)
|
||||
token, err = jwt.ParseWithClaims(auth, claims, cfg.keyFunc)
|
||||
}
|
||||
if err == nil && token.Valid {
|
||||
// Store user information from token into context.
|
||||
c.Locals(cfg.ContextKey, token)
|
||||
return cfg.SuccessHandler(c)
|
||||
}
|
||||
return cfg.ErrorHandler(c, err)
|
||||
}
|
||||
}
|
||||
|
||||
// jwtFromHeader returns a function that extracts token from the request header.
|
||||
func jwtFromHeader(header string, authScheme string) func(c *fiber.Ctx) (string, error) {
|
||||
return func(c *fiber.Ctx) (string, error) {
|
||||
auth := c.Get(header)
|
||||
l := len(authScheme)
|
||||
if len(auth) > l+1 && auth[:l] == authScheme {
|
||||
return auth[l+1:], nil
|
||||
}
|
||||
return "", errors.New("Missing or malformed JWT")
|
||||
}
|
||||
}
|
||||
|
||||
// jwtFromQuery returns a function that extracts token from the query string.
|
||||
func jwtFromQuery(param string) func(c *fiber.Ctx) (string, error) {
|
||||
return func(c *fiber.Ctx) (string, error) {
|
||||
token := c.Query(param)
|
||||
if token == "" {
|
||||
return "", errors.New("Missing or malformed JWT")
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
}
|
||||
|
||||
// jwtFromParam returns a function that extracts token from the url param string.
|
||||
func jwtFromParam(param string) func(c *fiber.Ctx) (string, error) {
|
||||
return func(c *fiber.Ctx) (string, error) {
|
||||
token := c.Params(param)
|
||||
if token == "" {
|
||||
return "", errors.New("Missing or malformed JWT")
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
}
|
||||
|
||||
// jwtFromCookie returns a function that extracts token from the named cookie.
|
||||
func jwtFromCookie(name string) func(c *fiber.Ctx) (string, error) {
|
||||
return func(c *fiber.Ctx) (string, error) {
|
||||
token := c.Cookies(name)
|
||||
if token == "" {
|
||||
return "", errors.New("Missing or malformed JWT")
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
}
|
||||
|
||||
func AuthWeb() func(*fiber.Ctx) error {
|
||||
// Definir la función que maneja la autenticación
|
||||
return func(c *fiber.Ctx) error {
|
||||
// Intentar obtener el usuario
|
||||
user, err := auth.User(c)
|
||||
|
||||
// Si no se puede obtener el usuario (es nil o hay error), hacer logout
|
||||
if err != nil || user == nil {
|
||||
// Ejecutar logout y destruir la sesión
|
||||
auth.Logout(c)
|
||||
|
||||
// Redirigir al login
|
||||
return c.Redirect("/login")
|
||||
}
|
||||
|
||||
// Si el usuario está presente, continuar con la autenticación normal
|
||||
return Authenticate(AuthConfig{
|
||||
SigningKey: []byte(app.Http.Token.AppJwtSecret),
|
||||
TokenLookup: "cookie:Verify-Rest-Token",
|
||||
ErrorHandler: func(ctx *fiber.Ctx, err error) error {
|
||||
// En caso de error de autenticación, hacer logout
|
||||
auth.Logout(ctx)
|
||||
return ctx.Redirect("/login")
|
||||
},
|
||||
})(c) // Llamar al siguiente middleware
|
||||
}
|
||||
}
|
||||
|
||||
func AuthAdmin(c *fiber.Ctx) error {
|
||||
if !auth.IsAdmin(c) {
|
||||
auth.Logout(c)
|
||||
return c.Redirect("/login")
|
||||
}
|
||||
return c.Next()
|
||||
}
|
||||
|
||||
func AuthApi() func(*fiber.Ctx) error {
|
||||
return Authenticate(AuthConfig{
|
||||
SigningKey: []byte(app.Http.Token.ApiJwtSecret),
|
||||
TokenLookup: "header:Verify-Rest-Token",
|
||||
ErrorHandler: func(ctx *fiber.Ctx, err error) error {
|
||||
auth.Logout(ctx)
|
||||
return ctx.Status(401).JSON("Invalid Attempt")
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/sujit-baniya/fiber-boilerplate/pkg/auth"
|
||||
)
|
||||
|
||||
func LoadUserMiddleware(c *fiber.Ctx) error {
|
||||
user, err := auth.User(c)
|
||||
if err == nil && user != nil {
|
||||
// Convertimos el usuario a un map[string]interface{} e incluimos los campos necesarios
|
||||
userData := map[string]interface{}{
|
||||
"ID": user.ID,
|
||||
"Name": user.Name,
|
||||
"Email": user.Email,
|
||||
"NombreUsuario": user.NombreUsuario,
|
||||
"IsAdmin": user.IsAdmin,
|
||||
"Role": map[string]interface{}{
|
||||
"ID": user.Role.ID,
|
||||
"Name": user.Role.Name,
|
||||
// Incluye otros campos relevantes del rol
|
||||
},
|
||||
}
|
||||
|
||||
// Guarda el map en el contexto
|
||||
c.Locals("user", userData)
|
||||
}
|
||||
// Si no está autenticado, continuamos sin agregar nada
|
||||
return c.Next()
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package middlewares
|
||||
|
||||
import "github.com/gofiber/fiber/v2"
|
||||
|
||||
func LoadCacheHeaders(c *fiber.Ctx) error {
|
||||
c.Set("X-DNS-Prefetch-Control", "off")
|
||||
c.Set("Pragma", "no-cache")
|
||||
c.Set("Expires", "Fri, 01 Jan 1990 00:00:00 GMT")
|
||||
c.Set("Cache-Control", "no-cache, must-revalidate, no-store, max-age=0, private")
|
||||
return c.Next()
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/sujit-baniya/fiber-boilerplate/pkg/auth"
|
||||
)
|
||||
|
||||
func LimitPhoneNumbersPerRequest(c *fiber.Ctx) error {
|
||||
if auth.IsLoggedIn(c) {
|
||||
return c.Redirect("/")
|
||||
}
|
||||
return c.Next()
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/oarkflow/log"
|
||||
)
|
||||
|
||||
func NewLog(logger *log.Logger, skip func(c *fiber.Ctx) bool) fiber.Handler {
|
||||
return func(c *fiber.Ctx) error {
|
||||
start := time.Now()
|
||||
next := c.Next()
|
||||
|
||||
if skip != nil && skip(c) {
|
||||
return nil
|
||||
}
|
||||
|
||||
end := time.Now()
|
||||
latency := end.Sub(start)
|
||||
|
||||
status := c.Response().StatusCode()
|
||||
msg := "Request"
|
||||
if next != nil {
|
||||
msg = next.Error()
|
||||
}
|
||||
|
||||
var e *log.Entry
|
||||
switch {
|
||||
case status >= 400 && status < 500:
|
||||
e = logger.Warn()
|
||||
case status >= 500:
|
||||
e = logger.Error()
|
||||
default:
|
||||
e = logger.Info()
|
||||
}
|
||||
e.Int("status", status).
|
||||
Str("method", c.Method()).
|
||||
Str("path", c.Path()).
|
||||
Str("ip", c.IP()).
|
||||
Dur("latency", latency).
|
||||
Str("user_agent", c.Get(fiber.HeaderUserAgent)).
|
||||
Msg(msg)
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gookit/validate"
|
||||
"github.com/sujit-baniya/fiber-boilerplate/pkg/auth"
|
||||
"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
|
||||
)
|
||||
|
||||
func RedirectToHomePageOnLogin(c *fiber.Ctx) error {
|
||||
if auth.IsLoggedIn(c) {
|
||||
return c.Redirect("/app/dashboard")
|
||||
}
|
||||
return c.Next()
|
||||
}
|
||||
|
||||
func ValidateLoginPost(c *fiber.Ctx) error {
|
||||
var login models.Login
|
||||
if err := c.BodyParser(&login); err != nil {
|
||||
return c.JSON(fiber.Map{
|
||||
"status": "error",
|
||||
"message": err.Error(),
|
||||
})
|
||||
}
|
||||
v := validate.Struct(login)
|
||||
if !v.Validate() {
|
||||
return c.JSON(fiber.Map{
|
||||
"status": "error",
|
||||
"message": v.Errors.One(),
|
||||
})
|
||||
}
|
||||
|
||||
user, err := login.CheckLogin()
|
||||
|
||||
if err != nil {
|
||||
return c.JSON(fiber.Map{
|
||||
"status": "error",
|
||||
"message": err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
c.Locals("user", user)
|
||||
return c.Next()
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/sujit-baniya/fiber-boilerplate/pkg/auth"
|
||||
"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
|
||||
)
|
||||
|
||||
func MenuMiddleware(c *fiber.Ctx) error {
|
||||
// Verificar si el usuario está logueado
|
||||
if !auth.IsLoggedIn(c) {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
|
||||
"error": true,
|
||||
"message": "Usuario no autenticado",
|
||||
})
|
||||
}
|
||||
|
||||
// Obtener los datos del usuario autenticado
|
||||
user, err := auth.User(c)
|
||||
if err != nil {
|
||||
// Si ocurre un error al obtener el usuario, finalizar la solicitud
|
||||
fmt.Printf("Error al obtener usuario: %v\n", err)
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
|
||||
"error": true,
|
||||
"message": "Usuario no válido o sesión expirada",
|
||||
})
|
||||
}
|
||||
|
||||
// Buscar los módulos y submódulos del usuario
|
||||
userModules, err := models.FindUserByID(user.ID)
|
||||
if err != nil {
|
||||
fmt.Printf("Error al buscar datos del usuario: %v\n", err)
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||
"error": true,
|
||||
"message": "Error al obtener datos del usuario",
|
||||
})
|
||||
}
|
||||
|
||||
// Organizar los módulos y submódulos
|
||||
moduleMap := make(map[string][]map[string]string)
|
||||
|
||||
for _, submodule := range userModules.Role.Submodules {
|
||||
// Recuperar el nombre del módulo
|
||||
moduleName := submodule.Module.Title
|
||||
// Crear un mapa para cada submódulo que contenga el título y la URL
|
||||
submoduleData := map[string]string{
|
||||
"title": submodule.Title,
|
||||
"url": submodule.Url, // Suponiendo que el submódulo tiene un campo `URL`
|
||||
}
|
||||
// Agregar el submódulo a la lista correspondiente del módulo
|
||||
moduleMap[moduleName] = append(moduleMap[moduleName], submoduleData)
|
||||
}
|
||||
|
||||
// Ordenar los módulos alfabéticamente
|
||||
var sortedModules []string
|
||||
for module := range moduleMap {
|
||||
sortedModules = append(sortedModules, module)
|
||||
}
|
||||
sort.Strings(sortedModules)
|
||||
|
||||
// Ordenar los submódulos de cada módulo alfabéticamente
|
||||
for _, moduleName := range sortedModules {
|
||||
// Ordenar los submódulos alfabéticamente por título
|
||||
sort.Slice(moduleMap[moduleName], func(i, j int) bool {
|
||||
return moduleMap[moduleName][i]["title"] < moduleMap[moduleName][j]["title"]
|
||||
})
|
||||
}
|
||||
c.Locals("userModules", moduleMap)
|
||||
|
||||
// Obtener un array solo con las URLs
|
||||
var urls []string
|
||||
for _, submodules := range moduleMap {
|
||||
for _, submoduleData := range submodules {
|
||||
urls = append(urls, submoduleData["url"])
|
||||
}
|
||||
}
|
||||
|
||||
// Opcional: adjuntar la lista de URLs al contexto
|
||||
c.Locals("urls", urls)
|
||||
|
||||
// Verificar si la URL de la solicitud está en la lista de URLs permitidas
|
||||
requestURL := c.Path()
|
||||
|
||||
// Obtener el índice del último '/' en la URL de la solicitud
|
||||
lastSlashIndex := strings.LastIndex(requestURL, "/")
|
||||
if lastSlashIndex != -1 {
|
||||
// Obtener solo la parte de la URL después del último '/'
|
||||
requestURL = requestURL[lastSlashIndex+1:]
|
||||
}
|
||||
|
||||
// Verificar si la URL modificada está en la lista de URLs permitidas
|
||||
urlAllowed := false
|
||||
for _, url := range urls {
|
||||
// Comparar solo la parte de la URL después del último '/'
|
||||
if requestURL == url[strings.LastIndex(url, "/")+1:] {
|
||||
urlAllowed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Si la URL no está permitida, devolver un error
|
||||
if !urlAllowed {
|
||||
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{
|
||||
"error": true,
|
||||
"message": "Acceso no permitido a esta ruta",
|
||||
})
|
||||
}
|
||||
|
||||
// Continuar con la siguiente función de middleware o manejador
|
||||
return c.Next()
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sujit-baniya/fiber-boilerplate/utils"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/sujit-baniya/fiber-boilerplate/app"
|
||||
)
|
||||
|
||||
func ValidatePasswordReset(c *fiber.Ctx) error {
|
||||
token := c.Query("t")
|
||||
err := _validatePasswordReset(c, token)
|
||||
if err != nil {
|
||||
return app.Http.Flash.WithError(c, fiber.Map{
|
||||
"message": err.Error(),
|
||||
}).Redirect("/token-vencido")
|
||||
}
|
||||
return c.Next()
|
||||
}
|
||||
|
||||
func ValidatePasswordResetPost(c *fiber.Ctx) error {
|
||||
token := c.Params("token")
|
||||
err := _validatePasswordReset(c, token)
|
||||
if err != nil {
|
||||
return app.Http.Flash.WithError(c, fiber.Map{
|
||||
"message": err.Error(),
|
||||
}).Redirect("/login")
|
||||
}
|
||||
return c.Next()
|
||||
}
|
||||
|
||||
func _validatePasswordReset(c *fiber.Ctx, t string) error {
|
||||
t = utils.Decrypt(t, app.Http.Server.Key)
|
||||
emailParts := strings.Split(t, "-reset-")
|
||||
if len(emailParts) != 2 {
|
||||
return errors.New("Invalid Password Reset Token")
|
||||
}
|
||||
|
||||
tokenTS, err := strconv.ParseInt(emailParts[1], 10, 64)
|
||||
if err != nil {
|
||||
return errors.New("Invalid Password Reset Token")
|
||||
}
|
||||
now := time.Now().Unix()
|
||||
diff := now - tokenTS
|
||||
if diff > (5 * 60) {
|
||||
return errors.New("Password Reset Token has expired!")
|
||||
} else if diff < 0 {
|
||||
return errors.New("Invalid Password Reset Token")
|
||||
}
|
||||
c.Locals("email", emailParts[0])
|
||||
c.Locals("token", t)
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/sujit-baniya/fiber-boilerplate/utils"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gookit/validate"
|
||||
"github.com/sujit-baniya/fiber-boilerplate/app"
|
||||
"github.com/sujit-baniya/fiber-boilerplate/pkg/auth"
|
||||
"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
|
||||
)
|
||||
|
||||
func ValidateRegisterPost(c *fiber.Ctx) error {
|
||||
// Declara el modelo RegisterForm
|
||||
var register models.RegisterForm
|
||||
|
||||
// Intenta parsear el cuerpo de la solicitud
|
||||
if err := c.BodyParser(®ister); err != nil {
|
||||
return app.Http.Flash.WithError(c, fiber.Map{
|
||||
"message": "Error al procesar los datos del formulario: " + err.Error(),
|
||||
}).Redirect("/register-gas")
|
||||
}
|
||||
|
||||
// Realiza la validación del formulario
|
||||
v := validate.Struct(register)
|
||||
if v == nil {
|
||||
return app.Http.Flash.WithError(c, fiber.Map{
|
||||
"message": "Error en la validación de los datos del formulario.",
|
||||
}).Redirect("/register-gas")
|
||||
}
|
||||
|
||||
// Verifica si hay errores de validación
|
||||
if !v.Validate() {
|
||||
errorMessage := "Error desconocido en los datos del formulario."
|
||||
if len(v.Errors) > 0 {
|
||||
errorMessage = v.Errors.One() // Obtiene el primer error si existe
|
||||
}
|
||||
return app.Http.Flash.WithError(c, fiber.Map{
|
||||
"message": errorMessage,
|
||||
}).Redirect("/register-gas")
|
||||
}
|
||||
|
||||
return c.Next()
|
||||
}
|
||||
|
||||
func ValidateConfirmToken(c *fiber.Ctx) error {
|
||||
t := utils.Decrypt(c.Query("t"), app.Http.Server.Key)
|
||||
fmt.Println(t)
|
||||
user, err := models.GetUserByUsuarioVerify(t)
|
||||
if err != nil {
|
||||
return app.Http.Flash.WithError(c, fiber.Map{
|
||||
"message": err.Error(),
|
||||
}).Redirect("/login")
|
||||
}
|
||||
|
||||
if user.EmailVerified {
|
||||
return app.Http.Flash.WithError(c, fiber.Map{
|
||||
"message": "Usuario was already validated",
|
||||
}).Redirect("/login")
|
||||
}
|
||||
user.EmailVerified = true
|
||||
app.Http.Database.DB.Save(&user)
|
||||
|
||||
auth.Login(c, user.ID, app.Http.Server.Key) //nolint:wsl
|
||||
c.Locals("user", user)
|
||||
return c.Next()
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/limiter"
|
||||
"time"
|
||||
)
|
||||
|
||||
func MaxBodySize(sizeInMB int) fiber.Handler {
|
||||
sizeInMB = sizeInMB * 1024 * 1024
|
||||
return func(c *fiber.Ctx) error {
|
||||
if len(c.Body()) >= sizeInMB {
|
||||
// custom response here
|
||||
return fiber.ErrRequestEntityTooLarge
|
||||
}
|
||||
return c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func Limit(maxRequest int, duration time.Duration) func(*fiber.Ctx) error {
|
||||
return limiter.New(limiter.Config{
|
||||
Max: maxRequest,
|
||||
Expiration: duration * time.Minute,
|
||||
LimitReached: func(c *fiber.Ctx) error {
|
||||
return c.Status(429).JSON(fiber.Map{
|
||||
"error": true,
|
||||
"message": "Too many requests",
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
// scrypt.go
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/scrypt"
|
||||
)
|
||||
|
||||
func Encrypt(key, data []byte) ([]byte, error) {
|
||||
key, salt, err := DeriveKey(key, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blockCipher, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gcm, err := cipher.NewGCM(blockCipher)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nonce := make([]byte, gcm.NonceSize())
|
||||
if _, err = rand.Read(nonce); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ciphertext := gcm.Seal(nonce, nonce, data, nil)
|
||||
ciphertext = append(ciphertext, salt...)
|
||||
return ciphertext, nil
|
||||
}
|
||||
func Decrypt(key, data []byte) ([]byte, error) {
|
||||
salt, data := data[len(data)-32:], data[:len(data)-32]
|
||||
key, _, err := DeriveKey(key, salt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blockCipher, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gcm, err := cipher.NewGCM(blockCipher)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nonce, ciphertext := data[:gcm.NonceSize()], data[gcm.NonceSize():]
|
||||
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return plaintext, nil
|
||||
}
|
||||
func DeriveKey(password, salt []byte) ([]byte, []byte, error) {
|
||||
if salt == nil {
|
||||
salt = make([]byte, 32)
|
||||
if _, err := rand.Read(salt); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
key, err := scrypt.Key(password, salt, 1048576, 8, 1, 32)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return key, salt, nil
|
||||
}
|
||||
func main() {
|
||||
var (
|
||||
password = []byte("mysecretpassword")
|
||||
data = []byte(fmt.Sprintf("%v", time.Now().Unix()))
|
||||
)
|
||||
ciphertext, err := Encrypt(password, data)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Printf("ciphertext: %s\n", hex.EncodeToString(ciphertext))
|
||||
plaintext, err := Decrypt(password, ciphertext)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Printf("plaintext: %s\n", plaintext)
|
||||
}
|
||||
Reference in New Issue
Block a user