diff --git a/controllers/cartItemController.go b/controllers/cartItemController.go index f21632a..8b333ba 100644 --- a/controllers/cartItemController.go +++ b/controllers/cartItemController.go @@ -1,8 +1,6 @@ package controllers import ( - "crypto/rand" - "encoding/hex" "errors" "fmt" "net/http" @@ -15,6 +13,7 @@ import ( "git.dynamicdiscord.de/kalipso/zineshop/models" //"git.dynamicdiscord.de/kalipso/zineshop/services" "git.dynamicdiscord.de/kalipso/zineshop/repositories" + "git.dynamicdiscord.de/kalipso/zineshop/utils" ) type CartItemController interface { @@ -53,15 +52,6 @@ func getSetCookieValue(c *gin.Context, cookieName string) string { return "" // Return empty string if cookie is not found } -func generateSessionId(length int) string { - bytes := make([]byte, length) // 16 bytes = 128 bits - _, err := rand.Read(bytes) - if err != nil { - panic("failed to generate session ID") - } - return hex.EncodeToString(bytes) -} - func GetSessionId(ctx *gin.Context) string { sessionId, err := ctx.Cookie("session_id") @@ -73,17 +63,13 @@ func GetSessionId(ctx *gin.Context) string { return responseCookie } - sessionId = generateSessionId(16) + sessionId = utils.GenerateSessionId(16) ctx.SetCookie("session_id", sessionId, 3600, "/", "", false, true) } return sessionId } -func GenerateToken() string { - return generateSessionId(16) -} - func (rc *cartItemController) NewCartItemFromForm(ctx *gin.Context) (models.CartItem, error) { sessionId := GetSessionId(ctx) shopItemIdStr := ctx.PostForm("ShopItemId") @@ -162,7 +148,7 @@ func (rc *cartItemController) NewAddressFromForm(ctx *gin.Context) (models.Addre func (rc *cartItemController) NewOrderFromForm(ctx *gin.Context) (models.Order, error) { sessionId := GetSessionId(ctx) status := models.OrderStatus("AwaitingConfirmation") - token := GenerateToken() + token := utils.GenerateToken() email := ctx.PostForm("email") comment := ctx.PostForm("comment") firstName := ctx.PostForm("firstName") diff --git a/controllers/userController.go b/controllers/userController.go index ed4b1b7..fc07462 100644 --- a/controllers/userController.go +++ b/controllers/userController.go @@ -1,11 +1,12 @@ package controllers import ( + "errors" "fmt" - "math/rand" "net/http" "github.com/gin-gonic/gin" + "gorm.io/gorm" "git.dynamicdiscord.de/kalipso/zineshop/models" "git.dynamicdiscord.de/kalipso/zineshop/repositories" @@ -36,7 +37,7 @@ func (uc *UserController) Register(c *gin.Context) { return } - _, err = services.Users.Register(body.Name, body.Email, body.Password) + _, err = services.Users.Register(body.Name, body.Email, body.Password, false) if err != nil { fmt.Println("Error: ", err) @@ -140,10 +141,12 @@ func (rc *UserController) LoginHandler(c *gin.Context) { } func CreateSessionData(c *gin.Context, extra any) gin.H { - _, exists := c.Get("user") + user, exists := c.Get("user") + userImpl, _ := user.(models.User) return gin.H{ "loggedIn": exists, + "isAdmin": userImpl.IsAdmin, "data": extra, } } @@ -153,11 +156,45 @@ func (rc *UserController) RegisterHandler(c *gin.Context) { email := c.PostForm("email") password := c.PostForm("password") - _, err := services.Users.Register(name, email, password) + //first registered user is admin + isEmpty, _ := repositories.Users.IsEmpty() + if isEmpty { + _, err := services.Users.Register(name, email, password, true) + if err != nil { + data := gin.H{ + "error": "Registering Failed.", + "success": "", + } + c.HTML(http.StatusOK, "register.html", data) + return + } - if err != nil { data := gin.H{ - "error": "Registering Failed.", + "error": "", + "success": "You successfully registered as Admin. Try logging in.", + } + + c.HTML(http.StatusOK, "register.html", data) + return + } + + //for any other user token is required + token := c.PostForm("token") + + if token == "" { + data := gin.H{ + "error": "No token. No register.", + "success": "", + } + + c.HTML(http.StatusOK, "register.html", data) + } + + tokenExists, err := repositories.Tokens.Exists(token) + + if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + data := gin.H{ + "error": err, "success": "", } @@ -165,6 +202,31 @@ func (rc *UserController) RegisterHandler(c *gin.Context) { return } + if !tokenExists { + data := gin.H{ + "error": "Invalid Token.", + "success": "", + } + c.HTML(http.StatusOK, "register.html", data) + return + } + + _, err = services.Users.Register(name, email, password, false) + if err != nil { + data := gin.H{ + "error": "Registering Failed.", + "success": "", + } + c.HTML(http.StatusOK, "register.html", data) + return + } + + err = repositories.Tokens.Delete(token) + + if err != nil { + fmt.Println("Could not delete RegisterToken: ", err) + } + data := gin.H{ "error": "", "success": "You successfully registered. Try logging in.", @@ -174,6 +236,39 @@ func (rc *UserController) RegisterHandler(c *gin.Context) { } func (rc *UserController) RegisterView(c *gin.Context) { + data := gin.H{ + "error": "", + "success": "", + "token": c.Param("token"), + } + + c.HTML(http.StatusOK, "registertoken.html", data) +} + +func (rc *UserController) InitAdmin(c *gin.Context) { + isEmpty, err := repositories.Users.IsEmpty() + + if err != nil { + data := gin.H{ + "error": err, + "success": "", + } + + c.HTML(http.StatusInternalServerError, "error.html", data) + return + } + + if !isEmpty { + data := gin.H{ + "error": "Registration is closed", + "success": "", + } + + c.HTML(http.StatusInternalServerError, "error.html", data) + return + + } + data := gin.H{ "error": "", "success": "", @@ -204,6 +299,38 @@ func (rc *UserController) ResetHandler(c *gin.Context) { c.HTML(http.StatusOK, "passwordreset.html", data) } +func (rc *UserController) InviteView(c *gin.Context) { + tokens, _ := repositories.Tokens.GetAll() + fmt.Println(tokens) + + data := gin.H{ + "tokens": tokens, + } + + c.HTML(http.StatusOK, "invites.html", data) +} + +func (rc *UserController) InviteHandler(c *gin.Context) { + action := c.PostForm("action") + + if action == "create" { + _, err := repositories.Tokens.Create() + + if err != nil { + fmt.Println(err) + c.HTML(http.StatusBadRequest, "error.html", gin.H{"error": err}) + return + } + } + + if action == "delete" { + token := c.PostForm("token") + repositories.Tokens.Delete(token) + } + + rc.InviteView(c) +} + func (rc *UserController) MainView(c *gin.Context) { shopItems, _ := repositories.ShopItems.GetAll() fmt.Println(len(shopItems)) @@ -227,68 +354,6 @@ func (rc *UserController) TagView(c *gin.Context) { c.HTML(http.StatusOK, "index.html", data) } -type booking struct { - Booked bool -} - -type calendarbooking struct { - Time string - Bookings []booking -} - -func (rc *UserController) CalendarView(c *gin.Context) { - shopItems, _ := repositories.ShopItems.GetAll() - fmt.Println(len(shopItems)) - - generateBookings := func(amountShopItems int) []calendarbooking { - var result []calendarbooking - time := 6 - for _ = range 18 { - book := calendarbooking{ - Time: fmt.Sprintf("%d:00", time), - Bookings: []booking{}, - } - for _ = range amountShopItems { - book.Bookings = append(book.Bookings, booking{Booked: rand.Float32() < 0.5}) - } - - time += 1 - result = append(result, book) - } - - return result - } - - bookings := gin.H{ - "head": []string{ - "malobeo", - "hole of fame", - "BK", - "AZ Conni", - }, - "bookings": generateBookings(4), - //"bookings": []calendarbooking{ - // { - // Time: "10:00", - // Bookings: []booking{ - // { Booked: true }, - // { Booked: false }, - // }, - // }, - //}, - } - - data := CreateSessionData(c, gin.H{ - "title": "shopItem Page", - "bookings": bookings, - "shopItemcount": len(bookings["head"].([]string)) + 1, - }) - - fmt.Println(data) - - c.HTML(http.StatusOK, "calendar.html", data) -} - func (rc *UserController) Logout(c *gin.Context) { c.SetCookie("Authorization", "", -1, "", "", false, true) c.HTML(http.StatusOK, "index.html", gin.H{}) diff --git a/main.go b/main.go index 13680ba..d603f48 100644 --- a/main.go +++ b/main.go @@ -55,66 +55,50 @@ func main() { server.Static("/static", os.Getenv("STATIC")) server.LoadHTMLGlob(fmt.Sprintf("%s/*.html", os.Getenv("VIEWS"))) - apiRoutes := server.Group("/api") - //apiRoutes.Use(middlewares.BasicAuth()) - { - apiRoutes.POST("/tags", authValidator.RequireAuth, shopItemController.CreateTag) - apiRoutes.GET("/tags", authValidator.OptionalAuth, shopItemController.GetAllTags) - apiRoutes.POST("/shopitems", authValidator.RequireAuth, shopItemController.Create) - apiRoutes.GET("/shopitems", authValidator.OptionalAuth, shopItemController.GetAll) - apiRoutes.GET("/shopitems/:id", authValidator.OptionalAuth, shopItemController.GetById) - apiRoutes.PUT("/shopitems/:id", authValidator.RequireAuth, shopItemController.Update) - apiRoutes.DELETE("/shopitems/:id", authValidator.RequireAuth, shopItemController.Delete) - - //apiRoutes.GET("/rooms/:id/users", authValidator.RequireAuth, authValidator.RequireRoomAdmin, shopItemController.GetUsers) - //apiRoutes.POST("/rooms/:id/users", authValidator.RequireAuth, shopItemController.AddUser) - - apiRoutes.POST("/users/register", userController.Register) - apiRoutes.POST("/users/login", userController.Login) - apiRoutes.GET("/users/validate", authValidator.OptionalAuth, userController.Validate) - } - viewRoutes := server.Group("/", authValidator.OptionalAuth) { viewRoutes.GET("/", userController.MainView) viewRoutes.GET("/shopitems/:id", shopItemController.ShopItemView) - viewRoutes.GET("/shopitems/:id/edit", authValidator.RequireAuth, shopItemController.EditItemView) - viewRoutes.POST("/shopitems/:id/edit", authValidator.RequireAuth, shopItemController.EditItemHandler) - viewRoutes.GET("/shopitems/:id/delete", authValidator.RequireAuth, shopItemController.DeleteItemView) - viewRoutes.POST("/shopitems/:id/delete", authValidator.RequireAuth, shopItemController.DeleteItemHandler) - viewRoutes.GET("/variant/:id/print", authValidator.RequireAuth, printController.PrintVariantView) - viewRoutes.GET("/cart/print", authValidator.RequireAuth, printController.PrintCartView) - viewRoutes.POST("/print", authValidator.RequireAuth, printController.PrintHandler) + viewRoutes.GET("/shopitems/:id/edit", authValidator.RequireAdmin, shopItemController.EditItemView) + viewRoutes.POST("/shopitems/:id/edit", authValidator.RequireAdmin, shopItemController.EditItemHandler) + viewRoutes.GET("/shopitems/:id/delete", authValidator.RequireAdmin, shopItemController.DeleteItemView) + viewRoutes.POST("/shopitems/:id/delete", authValidator.RequireAdmin, shopItemController.DeleteItemHandler) + viewRoutes.GET("/variant/:id/print", authValidator.RequireAdmin, printController.PrintVariantView) + viewRoutes.GET("/cart/print", authValidator.RequireAdmin, printController.PrintCartView) + viewRoutes.POST("/print", authValidator.RequireAdmin, printController.PrintHandler) - viewRoutes.GET("/tags", authValidator.RequireAuth, shopItemController.TagView) - viewRoutes.POST("/tags/:id", authValidator.RequireAuth, shopItemController.TagHandler) + viewRoutes.GET("/tags", authValidator.RequireAdmin, shopItemController.TagView) + viewRoutes.POST("/tags/:id", authValidator.RequireAdmin, shopItemController.TagHandler) viewRoutes.GET("/tags/:id", userController.TagView) - viewRoutes.POST("/tags", authValidator.RequireAuth, shopItemController.AddTagHandler) - viewRoutes.GET("/cart", cartItemController.CartItemView) - viewRoutes.POST("/cart", cartItemController.AddItemHandler) - viewRoutes.POST("/cart/delete", cartItemController.DeleteItemHandler) - viewRoutes.POST("/cart/edit", cartItemController.EditItemHandler) - viewRoutes.GET("/checkout", cartItemController.CheckoutView) - viewRoutes.POST("/checkout", cartItemController.CheckoutHandler) - viewRoutes.POST("/order", cartItemController.OrderHandler) - viewRoutes.GET("/order/:token", cartItemController.OrderView) + viewRoutes.POST("/tags", authValidator.RequireAdmin, shopItemController.AddTagHandler) + viewRoutes.GET("/cart", authValidator.RequireAuth, cartItemController.CartItemView) + viewRoutes.POST("/cart", authValidator.RequireAuth, cartItemController.AddItemHandler) + viewRoutes.POST("/cart/delete", authValidator.RequireAuth, cartItemController.DeleteItemHandler) + viewRoutes.POST("/cart/edit", authValidator.RequireAuth, cartItemController.EditItemHandler) + viewRoutes.GET("/checkout", authValidator.RequireAuth, cartItemController.CheckoutView) + viewRoutes.POST("/checkout", authValidator.RequireAuth, cartItemController.CheckoutHandler) + viewRoutes.POST("/order", authValidator.RequireAuth, cartItemController.OrderHandler) + viewRoutes.GET("/order/:token", authValidator.RequireAuth, cartItemController.OrderView) viewRoutes.GET("/order/:token/print", authValidator.RequireAuth, printController.PrintOrderView) - viewRoutes.GET("/orders", authValidator.RequireAuth, cartItemController.OrdersView) - viewRoutes.POST("/order/:token/edit", authValidator.RequireAuth, cartItemController.OrdersHandler) + viewRoutes.GET("/orders", authValidator.RequireAdmin, cartItemController.OrdersView) + viewRoutes.POST("/order/:token/edit", authValidator.RequireAdmin, cartItemController.OrdersHandler) //write middleware that redirects to homescreen on register/login/reset for logged in users viewRoutes.GET("/login", userController.LoginView) viewRoutes.GET("/logout", userController.Logout) - viewRoutes.GET("/register", userController.RegisterView) - viewRoutes.GET("/passwordreset", userController.ResetView) - viewRoutes.GET("/additem", authValidator.RequireAuth, shopItemController.AddItemView) - viewRoutes.GET("/batchupload", authValidator.RequireAuth, shopItemController.AddItemsView) + viewRoutes.GET("/register", userController.InitAdmin) + viewRoutes.GET("/register/:token", userController.RegisterView) + viewRoutes.GET("/invites", authValidator.RequireAdmin, userController.InviteView) + viewRoutes.POST("/invites", authValidator.RequireAdmin, userController.InviteHandler) + viewRoutes.GET("/passwordreset", authValidator.RequireAuth, userController.ResetView) + viewRoutes.GET("/additem", authValidator.RequireAdmin, shopItemController.AddItemView) + viewRoutes.GET("/batchupload", authValidator.RequireAdmin, shopItemController.AddItemsView) viewRoutes.POST("/login", userController.LoginHandler) viewRoutes.POST("/register", userController.RegisterHandler) - viewRoutes.POST("/additem", authValidator.RequireAuth, shopItemController.AddItemHandler) - viewRoutes.POST("/batchupload", authValidator.RequireAuth, shopItemController.AddItemsHandler) - viewRoutes.POST("/passwordreset", userController.ResetHandler) + viewRoutes.POST("/additem", authValidator.RequireAdmin, shopItemController.AddItemHandler) + viewRoutes.POST("/batchupload", authValidator.RequireAdmin, shopItemController.AddItemsHandler) + viewRoutes.POST("/passwordreset", authValidator.RequireAuth, userController.ResetHandler) } server.Run(":" + os.Getenv("PORT")) diff --git a/middlewares/requireAuth.go b/middlewares/requireAuth.go index 43e9bcc..7c5cbc1 100644 --- a/middlewares/requireAuth.go +++ b/middlewares/requireAuth.go @@ -1,13 +1,13 @@ package middlewares -import( - "os" +import ( "fmt" + "os" "time" //"strconv" - "net/http" "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt/v5" + "net/http" //"git.dynamicdiscord.de/kalipso/zineshop/models" "git.dynamicdiscord.de/kalipso/zineshop/repositories" @@ -70,7 +70,7 @@ func (av *AuthValidator) RequireAuth(c *gin.Context) { c.AbortWithStatus(http.StatusUnauthorized) return } - + if claims, ok := token.Claims.(jwt.MapClaims); ok { //Check Expiration if float64(time.Now().Unix()) > claims["exp"].(float64) { @@ -78,7 +78,7 @@ func (av *AuthValidator) RequireAuth(c *gin.Context) { c.AbortWithStatus(http.StatusUnauthorized) return } - + //Find user user, err := repositories.Users.GetById(claims["sub"]) @@ -86,15 +86,72 @@ func (av *AuthValidator) RequireAuth(c *gin.Context) { c.AbortWithStatus(http.StatusUnauthorized) return } - + //Attach to req c.Set("user", user) - + // Coninue c.Next() return } - + + c.AbortWithStatus(http.StatusUnauthorized) +} + +func (av *AuthValidator) RequireAdmin(c *gin.Context) { + // Get Cookie + tokenString, err := c.Cookie("Authorization") + + if err != nil { + c.AbortWithStatus(http.StatusUnauthorized) + return + } + + //Validate + token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { + // Don't forget to validate the alg is what you expect: + if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"]) + } + + // hmacSampleSecret is a []byte containing your secret, e.g. []byte("my_secret_key") + return []byte(os.Getenv("SECRET")), nil + }) + + if err != nil { + c.AbortWithStatus(http.StatusUnauthorized) + return + } + + if claims, ok := token.Claims.(jwt.MapClaims); ok { + //Check Expiration + if float64(time.Now().Unix()) > claims["exp"].(float64) { + //expired + c.AbortWithStatus(http.StatusUnauthorized) + return + } + + //Find user + user, err := repositories.Users.GetById(claims["sub"]) + + if err != nil { + c.AbortWithStatus(http.StatusUnauthorized) + return + } + + if !user.IsAdmin { + c.AbortWithStatus(http.StatusUnauthorized) + return + } + + //Attach to req + c.Set("user", user) + + // Coninue + c.Next() + return + } + c.AbortWithStatus(http.StatusUnauthorized) } @@ -119,19 +176,19 @@ func (av *AuthValidator) OptionalAuth(c *gin.Context) { if err != nil { return } - + if claims, ok := token.Claims.(jwt.MapClaims); ok { if float64(time.Now().Unix()) > claims["exp"].(float64) { return } - + //Find user user, err := repositories.Users.GetById(claims["sub"]) if err != nil { return } - + //Attach to req c.Set("user", user) } diff --git a/models/user.go b/models/user.go index 4b16094..b646fdc 100644 --- a/models/user.go +++ b/models/user.go @@ -4,9 +4,15 @@ import ( "gorm.io/gorm" ) +type RegisterToken struct { + gorm.Model + Token string `json:"token" binding:"required" gorm:"unique;not null"` +} + type User struct { gorm.Model - Name string `json:"name" binding:"required" gorm:"unique;not null"` + Name string `json:"name" binding:"required" gorm:"unique;not null"` Password string `json:"password" binding:"required" gorm:"not null"` - Email string `json:"email" binding:"required,email" gorm:"unique;not null"` + Email string `json:"email" binding:"required,email" gorm:"unique;not null"` + IsAdmin bool `json:"isAdmin" gorm:"default:false;not null"` } diff --git a/repositories/registerTokenRepository.go b/repositories/registerTokenRepository.go new file mode 100644 index 0000000..99391a2 --- /dev/null +++ b/repositories/registerTokenRepository.go @@ -0,0 +1,86 @@ +package repositories + +import ( + "errors" + "fmt" + + "gorm.io/gorm" + + "git.dynamicdiscord.de/kalipso/zineshop/models" + "git.dynamicdiscord.de/kalipso/zineshop/utils" +) + +type RegisterTokenRepository interface { + Create() (models.RegisterToken, error) + GetAll() ([]models.RegisterToken, error) + Exists(string) (bool, error) + Delete(string) error +} + +type GORMRegisterTokenRepository struct { + DB *gorm.DB +} + +func NewGORMRegisterTokenRepository(db *gorm.DB) RegisterTokenRepository { + return &GORMRegisterTokenRepository{ + DB: db, + } +} + +func (t *GORMRegisterTokenRepository) Create() (models.RegisterToken, error) { + token := utils.GenerateToken() + + exists, err := t.Exists(token) + if err != nil { + return models.RegisterToken{}, err + } + + if exists { + return t.Create() + } + + newToken := models.RegisterToken{ + Token: token, + } + result := t.DB.Create(&newToken) + + if result.Error != nil { + return models.RegisterToken{}, result.Error + } + + return newToken, nil +} + +func (t *GORMRegisterTokenRepository) GetAll() ([]models.RegisterToken, error) { + var tokens []models.RegisterToken + result := t.DB.Find(&tokens) + + return tokens, result.Error +} + +func (t *GORMRegisterTokenRepository) Exists(tokenString string) (bool, error) { + var token models.RegisterToken + result := t.DB.First(&token, "token = ?", tokenString) + + if result.Error != nil { + if errors.Is(result.Error, gorm.ErrRecordNotFound) { + return false, nil + } + + return false, result.Error + } + + return true, nil +} + +func (t *GORMRegisterTokenRepository) Delete(token string) error { + result := t.DB.Where("token = ?", token).Delete(&models.RegisterToken{}) + + if result.Error != nil { + return result.Error + } else if result.RowsAffected == 0 { + return fmt.Errorf("Token not found, could not be deleted") + } + + return nil +} diff --git a/repositories/repository.go b/repositories/repository.go index 33f60ae..e3e81ff 100644 --- a/repositories/repository.go +++ b/repositories/repository.go @@ -14,6 +14,7 @@ var ( Tags TagRepository CartItems CartItemRepository Orders OrderRepository + Tokens RegisterTokenRepository ) func InitRepositories() { @@ -27,7 +28,8 @@ func InitRepositories() { &models.User{}, &models.Tag{}, &models.CartItem{}, - &models.Order{}) + &models.Order{}, + &models.RegisterToken{}) if err != nil { panic("failed to migrate database") @@ -38,4 +40,5 @@ func InitRepositories() { Tags = NewGORMTagRepository(db) CartItems = NewGORMCartItemRepository(db) Orders = NewGORMOrderRepository(db) + Tokens = NewGORMRegisterTokenRepository(db) } diff --git a/repositories/userRepository.go b/repositories/userRepository.go index ae47eeb..d322edb 100644 --- a/repositories/userRepository.go +++ b/repositories/userRepository.go @@ -1,15 +1,16 @@ package repositories -import( +import ( "gorm.io/gorm" "git.dynamicdiscord.de/kalipso/zineshop/models" -) +) type UserRepository interface { - Create(models.User) (models.User, error) + Create(models.User) (models.User, error) GetByEmail(string) (models.User, error) GetById(interface{}) (models.User, error) + IsEmpty() (bool, error) } type GORMUserRepository struct { @@ -22,7 +23,7 @@ func NewGORMUserRepository(db *gorm.DB) UserRepository { } } -func (u *GORMUserRepository) Create(user models.User) (models.User, error) { +func (u *GORMUserRepository) Create(user models.User) (models.User, error) { result := u.DB.Create(&user) if result.Error != nil { @@ -53,3 +54,18 @@ func (u *GORMUserRepository) GetById(id interface{}) (models.User, error) { return user, nil } + +func (u *GORMUserRepository) IsEmpty() (bool, error) { + var user models.User + result := u.DB.First(&user) + + if result.Error != nil { + if result.Error == gorm.ErrRecordNotFound { + return true, nil + } else { + return false, result.Error + } + } + + return false, nil +} diff --git a/services/userService.go b/services/userService.go index 597c5f6..3f4e551 100644 --- a/services/userService.go +++ b/services/userService.go @@ -1,9 +1,9 @@ package services -import( +import ( + "golang.org/x/crypto/bcrypt" "os" "time" - "golang.org/x/crypto/bcrypt" "github.com/golang-jwt/jwt/v5" @@ -11,13 +11,13 @@ import( "git.dynamicdiscord.de/kalipso/zineshop/repositories" ) -var( +var ( Users UserService = UserService{} ) -type UserService struct {} +type UserService struct{} -func (u *UserService) Register(name string, email string, password string) (models.User, error) { +func (u *UserService) Register(name string, email string, password string, isAdmin bool) (models.User, error) { //hash pw hash, err := bcrypt.GenerateFromPassword([]byte(password), 10) @@ -25,7 +25,7 @@ func (u *UserService) Register(name string, email string, password string) (mode return models.User{}, err } - user := models.User{Name: name, Email: email, Password: string(hash)} + user := models.User{Name: name, Email: email, Password: string(hash), IsAdmin: isAdmin} _, err = repositories.Users.Create(user) if err != nil { @@ -35,7 +35,7 @@ func (u *UserService) Register(name string, email string, password string) (mode return user, nil } -//return jwt tokenstring on success +// return jwt tokenstring on success func (u *UserService) Login(email string, password string) (string, error) { //lookup requested user user, err := repositories.Users.GetByEmail(email) diff --git a/static/output.css b/static/output.css index 94f7a80..b0d07ea 100644 --- a/static/output.css +++ b/static/output.css @@ -650,10 +650,6 @@ video { margin-right: 1rem; } -.ms-2 { - margin-inline-start: 0.5rem; -} - .mt-1 { margin-top: 0.25rem; } @@ -814,10 +810,6 @@ video { flex-direction: column; } -.items-start { - align-items: flex-start; -} - .items-center { align-items: center; } @@ -929,11 +921,6 @@ video { border-bottom-left-radius: 0.375rem; } -.rounded-l { - border-top-left-radius: 0.25rem; - border-bottom-left-radius: 0.25rem; -} - .border { border-width: 1px; } @@ -1069,9 +1056,9 @@ video { background-color: rgb(224 231 255 / var(--tw-bg-opacity, 1)); } -.bg-indigo-300 { +.bg-indigo-500 { --tw-bg-opacity: 1; - background-color: rgb(165 180 252 / var(--tw-bg-opacity, 1)); + background-color: rgb(99 102 241 / var(--tw-bg-opacity, 1)); } .bg-indigo-600 { @@ -1234,46 +1221,6 @@ video { background-color: rgb(24 24 27 / var(--tw-bg-opacity, 1)); } -.bg-indigo-400 { - --tw-bg-opacity: 1; - background-color: rgb(129 140 248 / var(--tw-bg-opacity, 1)); -} - -.bg-indigo-500 { - --tw-bg-opacity: 1; - background-color: rgb(99 102 241 / var(--tw-bg-opacity, 1)); -} - -.bg-pink-200 { - --tw-bg-opacity: 1; - background-color: rgb(251 207 232 / var(--tw-bg-opacity, 1)); -} - -.bg-pink-300 { - --tw-bg-opacity: 1; - background-color: rgb(249 168 212 / var(--tw-bg-opacity, 1)); -} - -.bg-pink-700 { - --tw-bg-opacity: 1; - background-color: rgb(190 24 93 / var(--tw-bg-opacity, 1)); -} - -.bg-pink-500 { - --tw-bg-opacity: 1; - background-color: rgb(236 72 153 / var(--tw-bg-opacity, 1)); -} - -.bg-fuchsia-500 { - --tw-bg-opacity: 1; - background-color: rgb(217 70 239 / var(--tw-bg-opacity, 1)); -} - -.bg-blue-700 { - --tw-bg-opacity: 1; - background-color: rgb(29 78 216 / var(--tw-bg-opacity, 1)); -} - .fill-red-50 { fill: #fef2f2; } @@ -1299,10 +1246,6 @@ video { padding: 1rem; } -.p-8 { - padding: 2rem; -} - .px-2 { padding-left: 0.5rem; padding-right: 0.5rem; @@ -1402,10 +1345,6 @@ video { padding-top: 1.25rem; } -.pt-8 { - padding-top: 2rem; -} - .text-left { text-align: left; } @@ -1770,10 +1709,6 @@ video { color: rgb(39 39 42 / var(--tw-text-opacity, 1)); } -.underline { - text-decoration-line: underline; -} - .antialiased { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; @@ -1908,10 +1843,6 @@ video { text-decoration-line: underline; } -.hover\:no-underline:hover { - text-decoration-line: none; -} - .focus\:z-10:focus { z-index: 10; } @@ -1948,12 +1879,6 @@ video { box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); } -.focus\:ring-2:focus { - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); -} - .focus\:ring-4:focus { --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color); @@ -2065,10 +1990,6 @@ video { max-width: 24rem; } - .sm\:grid-cols-2 { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - .sm\:items-center { align-items: center; } @@ -2156,26 +2077,10 @@ video { max-width: 80rem; } - .lg\:max-w-4xl { - max-width: 56rem; - } - - .lg\:max-w-6xl { - max-width: 72rem; - } - .lg\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } - .lg\:grid-cols-4 { - grid-template-columns: repeat(4, minmax(0, 1fr)); - } - - .lg\:grid-cols-2 { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - .lg\:p-8 { padding: 2rem; } @@ -2199,26 +2104,9 @@ video { .xl\:grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); } - - .xl\:grid-cols-8 { - grid-template-columns: repeat(8, minmax(0, 1fr)); - } - - .xl\:gap-x-8 { - -moz-column-gap: 2rem; - column-gap: 2rem; - } } @media (min-width: 1536px) { - .\32xl\:grid-cols-8 { - grid-template-columns: repeat(8, minmax(0, 1fr)); - } - - .\32xl\:grid-cols-6 { - grid-template-columns: repeat(6, minmax(0, 1fr)); - } - .\32xl\:px-0 { padding-left: 0px; padding-right: 0px; @@ -2305,10 +2193,6 @@ video { color: rgb(156 163 175 / var(--tw-placeholder-opacity, 1)); } - .dark\:ring-offset-gray-800 { - --tw-ring-offset-color: #1f2937; - } - .dark\:hover\:bg-gray-600:hover { --tw-bg-opacity: 1; background-color: rgb(75 85 99 / var(--tw-bg-opacity, 1)); diff --git a/utils/utils.go b/utils/utils.go new file mode 100644 index 0000000..66c4d20 --- /dev/null +++ b/utils/utils.go @@ -0,0 +1,19 @@ +package utils + +import ( + "crypto/rand" + "encoding/hex" +) + +func GenerateSessionId(length int) string { + bytes := make([]byte, length) // 16 bytes = 128 bits + _, err := rand.Read(bytes) + if err != nil { + panic("failed to generate session ID") + } + return hex.EncodeToString(bytes) +} + +func GenerateToken() string { + return GenerateSessionId(16) +} diff --git a/views/deleteitem.html b/views/deleteitem.html index b014fc0..0cde7c9 100644 --- a/views/deleteitem.html +++ b/views/deleteitem.html @@ -5,7 +5,7 @@
Price: {{ .data.shopItem.BasePrice }}
- {{ if .loggedIn }} + {{ if .isAdmin }}Do you really want to delete this item?? diff --git a/views/header.html b/views/header.html index 7094e9f..9cd3f9f 100644 --- a/views/header.html +++ b/views/header.html @@ -25,7 +25,7 @@ {{ end }} --> - {{ if .loggedIn }} + {{ if .isAdmin }}
- {{ else }} + {{ end }} + {{ if .loggedIn }} diff --git a/views/invites.html b/views/invites.html new file mode 100644 index 0000000..0d2e303 --- /dev/null +++ b/views/invites.html @@ -0,0 +1,28 @@ +{{ template "header.html" . }} + + +
+ {{ .BasePrice }}€
+ {{ if $.loggedIn }} +{{ .BasePrice }}€
+ {{ end }}