Basic JWT is now working #12
9
.vscode/settings.json
vendored
9
.vscode/settings.json
vendored
@ -1,9 +1,18 @@
|
|||||||
{
|
{
|
||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
|
"echojwt",
|
||||||
"glebarez",
|
"glebarez",
|
||||||
"gocook",
|
"gocook",
|
||||||
"huandu",
|
"huandu",
|
||||||
"labstack",
|
"labstack",
|
||||||
"sqlbuilder"
|
"sqlbuilder"
|
||||||
|
],
|
||||||
|
"sqltools.connections": [
|
||||||
|
{
|
||||||
|
"previewLimit": 50,
|
||||||
|
"driver": "SQLite",
|
||||||
|
"database": "${workspaceFolder:go-cook}/gocook.db",
|
||||||
|
"name": "gocook"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
128
api/handlers/v1/auth.go
Normal file
128
api/handlers/v1/auth.go
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"go-cook/api/models"
|
||||||
|
"go-cook/api/repositories"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/golang-jwt/jwt/v5"
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ErrJwtMissing = "auth token is missing"
|
||||||
|
ErrJwtClaimsMissing = "claims missing on token"
|
||||||
|
ErrJwtExpired = "auth token has expired"
|
||||||
|
)
|
||||||
|
|
||||||
|
type JwtToken struct {
|
||||||
|
Exp time.Time `json:"exp"`
|
||||||
|
Authorized bool `json:"authorized"`
|
||||||
|
UserName string `json:"username"`
|
||||||
|
Token string `json:"token"`
|
||||||
|
jwt.RegisteredClaims
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateJwt(username string) (string, error) {
|
||||||
|
//TODO use env here
|
||||||
|
secret := []byte("ThisIsABadSecretDontReallyUseThis")
|
||||||
|
|
||||||
|
token := jwt.New(jwt.SigningMethodHS256)
|
||||||
|
claims := token.Claims.(jwt.MapClaims)
|
||||||
|
claims["exp"] = time.Now().Add(10 * time.Minute)
|
||||||
|
claims["authorized"] = true
|
||||||
|
claims["username"] = username
|
||||||
|
|
||||||
|
tokenString, err := token.SignedString(secret)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tokenString, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) AuthRegister(c echo.Context) error {
|
||||||
|
username := c.QueryParam("username")
|
||||||
|
_, err := h.userRepo.GetByName(username)
|
||||||
|
if err != nil {
|
||||||
|
// if we have an err, validate that if its not user not found.
|
||||||
|
// if the user is not found, we can use that name
|
||||||
|
if err.Error() != repositories.ErrUserNotFound {
|
||||||
|
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||||
|
HttpCode: http.StatusInternalServerError,
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
password := c.QueryParam("password")
|
||||||
|
err = h.UserService.CheckPasswordForRequirements(password)
|
||||||
|
if err != nil {
|
||||||
|
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||||
|
HttpCode: http.StatusInternalServerError,
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = h.userRepo.Create(username, password)
|
||||||
|
if err != nil {
|
||||||
|
return c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
||||||
|
HttpCode: http.StatusInternalServerError,
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) AuthLogin(c echo.Context) error {
|
||||||
|
username := c.QueryParam("username")
|
||||||
|
password := c.QueryParam("password")
|
||||||
|
|
||||||
|
// check if the user exists
|
||||||
|
err := h.UserService.DoesUserExist(username)
|
||||||
|
if err != nil {
|
||||||
|
return c.JSON(http.StatusInternalServerError, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure the hash matches
|
||||||
|
err = h.UserService.DoesPasswordMatchHash(username, password)
|
||||||
|
if err != nil {
|
||||||
|
return c.JSON(http.StatusInternalServerError, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := generateJwt(username)
|
||||||
|
if err != nil {
|
||||||
|
return c.JSON(http.StatusInternalServerError, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, token)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) RefreshJwtToken(c echo.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) getJwtToken(c echo.Context) (JwtToken, error) {
|
||||||
|
// Make sure that the request came with a jwtToken
|
||||||
|
token, ok := c.Get("user").(*jwt.Token)
|
||||||
|
if !ok {
|
||||||
|
return JwtToken{}, errors.New(ErrJwtMissing)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the claims from the token
|
||||||
|
claims, ok := token.Claims.(*JwtToken)
|
||||||
|
if !ok {
|
||||||
|
return JwtToken{}, errors.New(ErrJwtClaimsMissing)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check to see if the token has expired
|
||||||
|
hasExpired := claims.Exp.Compare(time.Now())
|
||||||
|
if hasExpired == -1 {
|
||||||
|
return JwtToken{}, errors.New(ErrJwtExpired)
|
||||||
|
}
|
||||||
|
|
||||||
|
return *claims, nil
|
||||||
|
}
|
@ -2,6 +2,7 @@ package v1
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"go-cook/api/models"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
@ -47,3 +48,15 @@ func (h *Handler) HelloBody(c echo.Context) error {
|
|||||||
Message: fmt.Sprintf("Hello, %s", request.Name),
|
Message: fmt.Sprintf("Hello, %s", request.Name),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Handler) ProtectedRoute(c echo.Context) error {
|
||||||
|
token, err := h.getJwtToken(c)
|
||||||
|
if err != nil {
|
||||||
|
return c.JSON(http.StatusForbidden, models.ErrorResponse{
|
||||||
|
HttpCode: http.StatusForbidden,
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, token)
|
||||||
|
}
|
||||||
|
@ -3,26 +3,53 @@ package v1
|
|||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"go-cook/api/repositories"
|
"go-cook/api/repositories"
|
||||||
|
"go-cook/api/services"
|
||||||
|
|
||||||
|
"github.com/golang-jwt/jwt/v5"
|
||||||
|
echojwt "github.com/labstack/echo-jwt/v4"
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Handler struct {
|
type Handler struct {
|
||||||
userRepo repositories.UserRepository
|
UserService services.UserService
|
||||||
|
userRepo repositories.IUserTable
|
||||||
|
recipeRepo repositories.IRecipeTable
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHandler(conn *sql.DB) *Handler {
|
func NewHandler(conn *sql.DB) *Handler {
|
||||||
return &Handler{
|
return &Handler{
|
||||||
|
UserService: services.NewUserService(conn),
|
||||||
userRepo: repositories.NewUserRepository(conn),
|
userRepo: repositories.NewUserRepository(conn),
|
||||||
|
recipeRepo: repositories.NewRecipeRepository(conn),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) Register(v1 *echo.Group) {
|
func (h *Handler) Register(v1 *echo.Group) {
|
||||||
|
jwtConfig := echojwt.Config{
|
||||||
|
NewClaimsFunc: func(c echo.Context) jwt.Claims {
|
||||||
|
return new(JwtToken)
|
||||||
|
},
|
||||||
|
SigningKey: []byte("ThisIsABadSecretDontReallyUseThis"),
|
||||||
|
}
|
||||||
|
|
||||||
|
v1.POST("/login", h.AuthLogin)
|
||||||
|
v1.POST("/register", h.AuthRegister)
|
||||||
demo := v1.Group("/demo")
|
demo := v1.Group("/demo")
|
||||||
|
|
||||||
demo.GET("/hello", h.DemoHello)
|
demo.GET("/hello", h.DemoHello)
|
||||||
demo.GET("/hello/:who", h.HelloWho)
|
demo.GET("/hello/:who", h.HelloWho)
|
||||||
|
|
||||||
|
demo.Use(echojwt.WithConfig(jwtConfig))
|
||||||
demo.GET("/hello/body", h.HelloBody)
|
demo.GET("/hello/body", h.HelloBody)
|
||||||
|
|
||||||
users := v1.Group("/users")
|
protected := v1.Group("/demo/protected")
|
||||||
users.POST("/new", h.NewUser)
|
protected.Use(echojwt.WithConfig(jwtConfig))
|
||||||
|
protected.GET("", h.ProtectedRoute)
|
||||||
|
|
||||||
|
//recipes := v1.Group("/recipe")
|
||||||
|
|
||||||
|
//users := v1.Group("/users")
|
||||||
|
//users.POST("/register", h.RegisterUser)
|
||||||
|
//users.POST("/login", h.LoginUser)
|
||||||
|
//users.POST("/update/password", h.UpdatePassword)
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,10 @@ import (
|
|||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (h *Handler) NewUser(c echo.Context) error {
|
type newUserResponse struct {
|
||||||
return c.JSON(http.StatusOK, "not implemented yet")
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) RegisterUser(c echo.Context) error {
|
||||||
|
return c.JSON(http.StatusOK, newUserResponse{})
|
||||||
}
|
}
|
||||||
|
12
api/models/recipe.go
Normal file
12
api/models/recipe.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type RecipeModel struct {
|
||||||
|
Id int32
|
||||||
|
Title string
|
||||||
|
Thumbnail string
|
||||||
|
Content string
|
||||||
|
CreatedAt time.Time
|
||||||
|
LastUpdated time.Time
|
||||||
|
}
|
6
api/models/std.go
Normal file
6
api/models/std.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
type ErrorResponse struct {
|
||||||
|
HttpCode int `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
@ -9,3 +9,7 @@ type UserModel struct {
|
|||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
LastUpdated time.Time
|
LastUpdated time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UserDto struct {
|
||||||
|
|
||||||
|
}
|
||||||
|
45
api/repositories/recipe.go
Normal file
45
api/repositories/recipe.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package repositories
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"go-cook/api/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type IRecipeTable interface {
|
||||||
|
Create(models.RecipeModel) error
|
||||||
|
List() ([]models.RecipeModel, error)
|
||||||
|
Get(id int) (models.RecipeModel, error)
|
||||||
|
Update(id int, entity models.RecipeModel) error
|
||||||
|
Delete(id int) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type RecipeRepository struct {
|
||||||
|
client *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRecipeRepository(client *sql.DB) RecipeRepository {
|
||||||
|
return RecipeRepository{
|
||||||
|
client: client,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr RecipeRepository) Create(models.RecipeModel) error {
|
||||||
|
return errors.New("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr RecipeRepository) List() ([]models.RecipeModel, error) {
|
||||||
|
return []models.RecipeModel{}, errors.New("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr RecipeRepository) Get(id int) (models.RecipeModel, error) {
|
||||||
|
return models.RecipeModel{}, errors.New("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr RecipeRepository) Update(id int, entity models.RecipeModel) error {
|
||||||
|
return errors.New("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr RecipeRepository) Delete(id int) error {
|
||||||
|
return errors.New("not implemented")
|
||||||
|
}
|
@ -2,6 +2,7 @@ package repositories
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go-cook/api/models"
|
"go-cook/api/models"
|
||||||
"time"
|
"time"
|
||||||
@ -12,8 +13,17 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
TableName string = "users"
|
TableName string = "users"
|
||||||
|
ErrUserNotFound string = "requested user was not found"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type IUserTable interface {
|
||||||
|
GetByName(name string) (models.UserModel, error)
|
||||||
|
Create(name, password string) (int64, error)
|
||||||
|
Update(id int, entity models.UserModel) error
|
||||||
|
UpdatePassword(name, password string) error
|
||||||
|
CheckUserHash(name, password string) error
|
||||||
|
}
|
||||||
|
|
||||||
// Creates a new instance of UserRepository with the bound sql
|
// Creates a new instance of UserRepository with the bound sql
|
||||||
func NewUserRepository(conn *sql.DB) UserRepository {
|
func NewUserRepository(conn *sql.DB) UserRepository {
|
||||||
return UserRepository{
|
return UserRepository{
|
||||||
@ -37,10 +47,15 @@ func (ur UserRepository) GetByName(name string) (models.UserModel, error) {
|
|||||||
return models.UserModel{}, err
|
return models.UserModel{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return ur.processRows(rows)[0], nil
|
data := ur.processRows(rows)
|
||||||
|
if (len(data) == 0) {
|
||||||
|
return models.UserModel{}, errors.New(ErrUserNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ur UserRepository) NewUser(name, password string) (int64, error) {
|
return data[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ur UserRepository) Create(name, password string) (int64, error) {
|
||||||
passwordBytes := []byte(password)
|
passwordBytes := []byte(password)
|
||||||
hash, err := bcrypt.GenerateFromPassword(passwordBytes, bcrypt.DefaultCost)
|
hash, err := bcrypt.GenerateFromPassword(passwordBytes, bcrypt.DefaultCost)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -62,6 +77,10 @@ func (ur UserRepository) NewUser(name, password string) (int64, error) {
|
|||||||
return 1, nil
|
return 1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ur UserRepository) Update(id int, entity models.UserModel) error {
|
||||||
|
return errors.New("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
func (ur UserRepository) UpdatePassword(name, password string) error {
|
func (ur UserRepository) UpdatePassword(name, password string) error {
|
||||||
_, err := ur.GetByName(name)
|
_, err := ur.GetByName(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -20,7 +20,7 @@ func TestCanCreateNewUser(t *testing.T) {
|
|||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
repo := repositories.NewUserRepository(db)
|
repo := repositories.NewUserRepository(db)
|
||||||
updated, err := repo.NewUser("testing", "NotSecure")
|
updated, err := repo.Create("testing", "NotSecure")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
|
106
api/services/userService.go
Normal file
106
api/services/userService.go
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"go-cook/api/models"
|
||||||
|
"go-cook/api/repositories"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ErrPasswordNotLongEnough = "password needs to be 8 character or longer"
|
||||||
|
ErrPasswordMissingSpecialCharacter = "password needs to contain one of the following: !, @, #"
|
||||||
|
ErrInvalidPassword = "invalid password"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This will handle operations that are user related, but one layer higher then the repository
|
||||||
|
type UserService struct {
|
||||||
|
repo repositories.IUserTable
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUserService(conn *sql.DB) UserService {
|
||||||
|
return UserService{
|
||||||
|
repo: repositories.NewUserRepository(conn),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (us UserService) DoesUserExist(username string) error {
|
||||||
|
_, err := us.repo.GetByName(username)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (us UserService) DoesPasswordMatchHash(username, password string) error {
|
||||||
|
//passwordBytes := []byte(password)
|
||||||
|
//hash, err := bcrypt.GenerateFromPassword(passwordBytes, bcrypt.DefaultCost)
|
||||||
|
//if err != nil {
|
||||||
|
// return err
|
||||||
|
//}
|
||||||
|
|
||||||
|
model, err := us.GetUser(username)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = bcrypt.CompareHashAndPassword([]byte(model.Hash), []byte(password))
|
||||||
|
if err != nil {
|
||||||
|
return errors.New(ErrInvalidPassword)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (us UserService) GetUser(username string) (models.UserModel, error) {
|
||||||
|
return us.repo.GetByName(username)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (us UserService) CreateNewUser(name, password string) (models.UserModel, error) {
|
||||||
|
err := us.CheckPasswordForRequirements(password)
|
||||||
|
if err != nil {
|
||||||
|
return models.UserModel{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
us.repo.Create(name, password)
|
||||||
|
return models.UserModel{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (us UserService) CheckPasswordForRequirements(password string) error {
|
||||||
|
err := us.checkPasswordLength(password)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = us.checkPasswordForSpecialCharacters(password)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (us UserService) checkPasswordLength(password string) error {
|
||||||
|
if len(password) < 8 {
|
||||||
|
return errors.New(ErrPasswordNotLongEnough)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (us UserService) checkPasswordForSpecialCharacters(password string) error {
|
||||||
|
var chars []string
|
||||||
|
chars = append(chars, "!")
|
||||||
|
chars = append(chars, "@")
|
||||||
|
chars = append(chars, "#")
|
||||||
|
|
||||||
|
for _, char := range chars {
|
||||||
|
if strings.Contains(password, char) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.New(ErrPasswordMissingSpecialCharacter)
|
||||||
|
}
|
@ -1,43 +0,0 @@
|
|||||||
package services
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"errors"
|
|
||||||
"go-cook/api/models"
|
|
||||||
"go-cook/api/repositories"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
ErrPasswordNotLongEnough = "password needs to be 8 character or longer"
|
|
||||||
)
|
|
||||||
|
|
||||||
// This will handle operations that are user related, but one layer higher then the repository
|
|
||||||
type UserService struct {
|
|
||||||
repo repositories.UserRepository
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewUserService(conn *sql.DB) UserService {
|
|
||||||
return UserService{
|
|
||||||
repo: repositories.NewUserRepository(conn),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (us UserService) DoesUserExist(username string) error {
|
|
||||||
_, err := us.repo.GetByName(username)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (us UserService) CreateNewUser(name, password string) (models.UserModel, error) {
|
|
||||||
us.repo.NewUser(name, password)
|
|
||||||
return models.UserModel{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (us UserService) CheckPasswordForRequirements(password string) error {
|
|
||||||
if len(password) <= 8 {
|
|
||||||
return errors.New(ErrPasswordNotLongEnough)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
2
go.mod
2
go.mod
@ -21,10 +21,12 @@ require (
|
|||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-playground/validator v9.31.0+incompatible // indirect
|
github.com/go-playground/validator v9.31.0+incompatible // indirect
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
|
||||||
github.com/google/uuid v1.5.0 // indirect
|
github.com/google/uuid v1.5.0 // indirect
|
||||||
github.com/huandu/go-sqlbuilder v1.25.0 // indirect
|
github.com/huandu/go-sqlbuilder v1.25.0 // indirect
|
||||||
github.com/huandu/xstrings v1.3.2 // indirect
|
github.com/huandu/xstrings v1.3.2 // indirect
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
|
github.com/labstack/echo-jwt/v4 v4.2.0 // indirect
|
||||||
github.com/labstack/echo/v4 v4.11.4 // indirect
|
github.com/labstack/echo/v4 v4.11.4 // indirect
|
||||||
github.com/labstack/gommon v0.4.2 // indirect
|
github.com/labstack/gommon v0.4.2 // indirect
|
||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
|
4
go.sum
4
go.sum
@ -34,6 +34,8 @@ github.com/go-playground/validator v9.31.0+incompatible h1:UA72EPEogEnq76ehGdEDp
|
|||||||
github.com/go-playground/validator v9.31.0+incompatible/go.mod h1:yrEkQXlcI+PugkyDjY2bRrL/UBU4f3rvrgkN3V8JEig=
|
github.com/go-playground/validator v9.31.0+incompatible/go.mod h1:yrEkQXlcI+PugkyDjY2bRrL/UBU4f3rvrgkN3V8JEig=
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||||
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
|
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
|
||||||
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/huandu/go-assert v1.1.5/go.mod h1:yOLvuqZwmcHIC5rIzrBhT7D3Q9c3GFnd0JrPVhn/06U=
|
github.com/huandu/go-assert v1.1.5/go.mod h1:yOLvuqZwmcHIC5rIzrBhT7D3Q9c3GFnd0JrPVhn/06U=
|
||||||
@ -50,6 +52,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
|||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=
|
github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=
|
||||||
github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
|
github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
|
||||||
|
github.com/labstack/echo-jwt/v4 v4.2.0 h1:odSISV9JgcSCuhgQSV/6Io3i7nUmfM/QkBeR5GVJj5c=
|
||||||
|
github.com/labstack/echo-jwt/v4 v4.2.0/go.mod h1:MA2RqdXdEn4/uEglx0HcUOgQSyBaTh5JcaHIan3biwU=
|
||||||
github.com/labstack/echo/v4 v4.11.4 h1:vDZmA+qNeh1pd/cCkEicDMrjtrnMGQ1QFI9gWN1zGq8=
|
github.com/labstack/echo/v4 v4.11.4 h1:vDZmA+qNeh1pd/cCkEicDMrjtrnMGQ1QFI9gWN1zGq8=
|
||||||
github.com/labstack/echo/v4 v4.11.4/go.mod h1:noh7EvLwqDsmh/X/HWKPUl1AjzJrhyptRyEbQJfxen8=
|
github.com/labstack/echo/v4 v4.11.4/go.mod h1:noh7EvLwqDsmh/X/HWKPUl1AjzJrhyptRyEbQJfxen8=
|
||||||
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
||||||
|
1
main.go
1
main.go
@ -27,6 +27,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
e.Pre(middleware.RemoveTrailingSlash())
|
e.Pre(middleware.RemoveTrailingSlash())
|
||||||
e.Pre(middleware.Logger())
|
e.Pre(middleware.Logger())
|
||||||
|
e.Pre(middleware.Recover())
|
||||||
|
|
||||||
v1Group := e.Group("/api/v1")
|
v1Group := e.Group("/api/v1")
|
||||||
v1Api := v1.NewHandler(db)
|
v1Api := v1.NewHandler(db)
|
||||||
|
15
rest.http
15
rest.http
@ -1,3 +1,8 @@
|
|||||||
|
###
|
||||||
|
POST http://localhost:1323/api/v1/register?username=test&password=test1234!
|
||||||
|
###
|
||||||
|
POST http://localhost:1323/api/v1/login?username=test&password=test1234!
|
||||||
|
###
|
||||||
GET http://localhost:1323/api/v1/demo/hello
|
GET http://localhost:1323/api/v1/demo/hello
|
||||||
###
|
###
|
||||||
GET http://localhost:1323/api/v1/demo/hello/james
|
GET http://localhost:1323/api/v1/demo/hello/james
|
||||||
@ -5,5 +10,13 @@ GET http://localhost:1323/api/v1/demo/hello/james
|
|||||||
GET http://localhost:1323/api/v1/demo/hello/body
|
GET http://localhost:1323/api/v1/demo/hello/body
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
name = "body"
|
{
|
||||||
|
"name": "body"
|
||||||
|
}
|
||||||
###
|
###
|
||||||
|
|
||||||
|
POST http://localhost:1323/api/v1/login?username=test
|
||||||
|
|
||||||
|
###
|
||||||
|
GET http://localhost:1323/api/v1/demo/protected
|
||||||
|
Authorization: Bearer
|
Loading…
Reference in New Issue
Block a user