From 0fd45ea2bdff40e67837ed150818fc784a6ef2f1 Mon Sep 17 00:00:00 2001 From: MaxJa4 <74194322+MaxJa4@users.noreply.github.com> Date: Tue, 16 Jan 2024 22:15:26 +0100 Subject: [PATCH] Add login and auth --- .idea/inspectionProfiles/Project_Default.xml | 3 +- go.mod | 2 + go.sum | 4 + main.go | 209 +++++++++++++++---- models/user.go | 7 + templates/components/login_error.html | 3 + templates/login.html | 24 +++ 7 files changed, 206 insertions(+), 46 deletions(-) create mode 100644 models/user.go create mode 100644 templates/components/login_error.html create mode 100644 templates/login.html diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index 157477b..0b86ae1 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -4,10 +4,11 @@ diff --git a/go.mod b/go.mod index eb55da2..818314e 100644 --- a/go.mod +++ b/go.mod @@ -17,6 +17,8 @@ require ( github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.14.0 // indirect github.com/goccy/go-json v0.10.2 // indirect + github.com/gorilla/securecookie v1.1.2 // indirect + github.com/gorilla/sessions v1.2.2 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/json-iterator/go v1.1.12 // indirect diff --git a/go.sum b/go.sum index 3d244f8..1452acb 100644 --- a/go.sum +++ b/go.sum @@ -27,6 +27,10 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= +github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= +github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY= +github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= diff --git a/main.go b/main.go index 2538605..207fd24 100644 --- a/main.go +++ b/main.go @@ -4,17 +4,27 @@ import ( "InfrantrySkillCalculator/controllers" "InfrantrySkillCalculator/models" "InfrantrySkillCalculator/utils" + "fmt" "github.com/gin-gonic/gin" + "github.com/gorilla/sessions" + _ "github.com/gorilla/sessions" + "golang.org/x/crypto/bcrypt" "html/template" "io" "log" + "net/http" "os" ) +var store = sessions.NewCookieStore([]byte("f0q0qew0!)§(ds9713lsda231")) + func main() { //gin.SetMode(gin.ReleaseMode) // uncomment upon release router := gin.New() + router.LoadHTMLGlob("templates/**/*") + protected := router.Group("/") + protected.Use(AuthRequired()) models.ConnectDatabase() @@ -27,55 +37,164 @@ func main() { router.Static("/static", "./static") - router.GET("/", func(c *gin.Context) { - files := []string{ - "./templates/index.html", - "./templates/components/home_clan_bar.html", - "./templates/components/opp_clan_bar.html", - "./templates/components/home_player_list.html", - "./templates/components/opp_player_list.html", - "./templates/components/bottom_controls.html", - "./templates/modals/delete_clan.html", - "./templates/modals/add_clan.html", - "./templates/modals/edit_clan.html", - "./templates/modals/add_player.html", - "./templates/modals/delete_player.html", - "./templates/modals/edit_player.html", - "./templates/components/header.html", - } - tmpl, err := template.ParseFiles(files...) + router.GET("/login", loginPage) + router.POST("/login", loginPost) + router.GET("/logout", logout) - var clans []models.Clan - models.DB.Find(&clans) + protected.GET("/", mainPage) - data := map[string]interface{}{ - "clans": clans, - } - if err != nil { - log.Fatal(err) - } + protected.GET("/clans", controllers.GetAllClans) + protected.GET("/clans_html", controllers.GetAllClansHTML) + protected.GET("/clan/:id", controllers.GetClanByID) + protected.POST("/clan", controllers.AddClan) + protected.PATCH("/clan/:id", controllers.UpdateClanByID) + protected.DELETE("/clan/:id", controllers.DeleteClanByID) - err = tmpl.Execute(c.Writer, data) - if err != nil { - log.Fatal(err) - } - }) - - router.GET("/clans", controllers.GetAllClans) - router.GET("/clans_html", controllers.GetAllClansHTML) - router.GET("/clan/:id", controllers.GetClanByID) - router.POST("/clan", controllers.AddClan) - router.PATCH("/clan/:id", controllers.UpdateClanByID) - router.DELETE("/clan/:id", controllers.DeleteClanByID) - - router.GET("/players", controllers.GetAllPlayers) - router.GET("/players_html", controllers.GetPlayersByClanHTML) - router.GET("/player/:id", controllers.GetPlayerByID) - router.GET("/playerid/:name", controllers.GetPlayerIDByName) - router.POST("/player", controllers.AddPlayer) - router.PATCH("/player/:id", controllers.UpdatePlayerByID) - router.DELETE("/player/:id", controllers.DeletePlayerByID) + protected.GET("/players", controllers.GetAllPlayers) + protected.GET("/players_html", controllers.GetPlayersByClanHTML) + protected.GET("/player/:id", controllers.GetPlayerByID) + protected.GET("/playerid/:name", controllers.GetPlayerIDByName) + protected.POST("/player", controllers.AddPlayer) + protected.PATCH("/player/:id", controllers.UpdatePlayerByID) + protected.DELETE("/player/:id", controllers.DeletePlayerByID) log.Println("Running on 8000...") log.Fatal(router.Run(":8000")) } + +func mainPage(c *gin.Context) { + files := []string{ + "./templates/index.html", + "./templates/components/home_clan_bar.html", + "./templates/components/opp_clan_bar.html", + "./templates/components/home_player_list.html", + "./templates/components/opp_player_list.html", + "./templates/components/bottom_controls.html", + "./templates/modals/delete_clan.html", + "./templates/modals/add_clan.html", + "./templates/modals/edit_clan.html", + "./templates/modals/add_player.html", + "./templates/modals/delete_player.html", + "./templates/modals/edit_player.html", + "./templates/components/header.html", + } + tmpl, err := template.ParseFiles(files...) + if err != nil { + log.Fatal(err) + } + + var clans []models.Clan + models.DB.Find(&clans) + + data := map[string]interface{}{ + "clans": clans, + } + + err = tmpl.Execute(c.Writer, data) + if err != nil { + log.Fatal(err) + } +} + +func loginPage(c *gin.Context) { + session, _ := store.Get(c.Request, "session-name") + + if auth, ok := session.Values["authenticated"].(bool); ok && auth { + c.Redirect(http.StatusFound, "/") + return + } + + tmpl, err := template.ParseFiles([]string{"./templates/login.html", "./templates/components/header.html"}...) + if err != nil { + log.Fatal(err) + } + + err = tmpl.Execute(c.Writer, nil) + if err != nil { + log.Fatal(err) + } +} + +func loginPost(c *gin.Context) { + username := c.PostForm("username") + password := c.PostForm("password") + + if !checkUserCredentials(username, password) { + c.HTML(http.StatusOK, "login_error.html", gin.H{"message": "Ungültige Logindaten!"}) + return + } + + session, _ := store.Get(c.Request, "session-name") + session.Values["authenticated"] = true + session.Values["username"] = username + err := session.Save(c.Request, c.Writer) + if err != nil { + c.JSON(http.StatusInternalServerError, nil) + return + } + + c.Header("HX-Redirect", "/") + c.String(http.StatusOK, "") +} + +func checkUserCredentials(username, password string) bool { + var hashedPassword string + + hashedPassword, err := getUserPassword(username) + if err != nil { + return false + } + + err = bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password)) + return err == nil +} + +func getUserPassword(username string) (string, error) { + var user models.User + + if err := models.DB.Where("username = ?", username).First(&user).Error; err != nil { + log.Fatal(err) + return "", err + } + + var hashedPW string + hashedPW, err := hashPassword(user.Password) + if err != nil { + log.Fatal(err) + return "", err + } + + return hashedPW, nil +} + +func logout(c *gin.Context) { + session, _ := store.Get(c.Request, "session-name") + session.Values["authenticated"] = false + err := session.Save(c.Request, c.Writer) + if err != nil { + c.JSON(http.StatusInternalServerError, nil) + return + } + + c.Redirect(http.StatusFound, "/login") +} + +func hashPassword(password string) (string, error) { + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + if err != nil { + return "", fmt.Errorf("failed to hash password: %w", err) + } + return string(hashedPassword), nil +} + +func AuthRequired() gin.HandlerFunc { + return func(c *gin.Context) { + session, _ := store.Get(c.Request, "session-name") + if auth, ok := session.Values["authenticated"].(bool); !ok || !auth { + c.Redirect(http.StatusFound, "/login") + c.Abort() + return + } + c.Next() + } +} diff --git a/models/user.go b/models/user.go new file mode 100644 index 0000000..e5a8791 --- /dev/null +++ b/models/user.go @@ -0,0 +1,7 @@ +package models + +type User struct { + Username string `json:"username" gorm:"primary_key"` + Password string `json:"password"` + Enabled bool `json:"enabled" default:"1"` +} diff --git a/templates/components/login_error.html b/templates/components/login_error.html new file mode 100644 index 0000000..333c37a --- /dev/null +++ b/templates/components/login_error.html @@ -0,0 +1,3 @@ +
+ {{ .message }} +
diff --git a/templates/login.html b/templates/login.html new file mode 100644 index 0000000..944a87b --- /dev/null +++ b/templates/login.html @@ -0,0 +1,24 @@ + + + + {{ template "header" . }} + + +
+

Login

+
+
+
+ + +
+
+ + +
+ +
+
+
+ + \ No newline at end of file