started "real work"
This commit is contained in:
@@ -6,10 +6,14 @@ require github.com/go-sql-driver/mysql v1.8.1
|
||||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/jfarleyx/go-simple-cache v1.1.0 // indirect
|
||||
github.com/jmoiron/sqlx v1.4.0 // indirect
|
||||
github.com/klauspost/compress v1.17.9 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
@@ -17,11 +21,14 @@ require (
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.51.0 // indirect
|
||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
||||
golang.org/x/net v0.21.0 // indirect
|
||||
golang.org/x/sys v0.29.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/go-playground/validator/v10 v10.23.0
|
||||
github.com/gofiber/fiber/v2 v2.52.6
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1
|
||||
github.com/rs/zerolog v1.33.0
|
||||
|
||||
@@ -3,6 +3,14 @@ filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4
|
||||
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o=
|
||||
github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
@@ -18,6 +26,8 @@ github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
|
||||
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
|
||||
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
||||
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
@@ -42,11 +52,15 @@ github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVS
|
||||
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
||||
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||
|
||||
@@ -4,18 +4,18 @@ import (
|
||||
"github.com/johannesbuehl/golunteer/backend/pkg/db"
|
||||
)
|
||||
|
||||
type assignments map[string]string
|
||||
type assignments map[string]*string
|
||||
|
||||
type assignemntDB struct {
|
||||
TaskName string `db:"taskName"`
|
||||
UserName string `db:"userName"`
|
||||
TaskName string `db:"taskName"`
|
||||
UserName *string `db:"userName"`
|
||||
}
|
||||
|
||||
func Event(eventID int) (assignments, error) {
|
||||
// get the assignments from the database
|
||||
var assignmentRows []assignemntDB
|
||||
|
||||
if err := db.DB.Select(&assignmentRows, "SELECT USERS.name AS userName, TASKS.text AS taskName FROM USER_ASSIGNMENTS JOIN USERS ON USER_ASSIGNMENTS.userName = USERS.name LEFT JOIN TASKS ON USER_ASSIGNMENTS.taskID = TASKS.id WHERE USER_ASSIGNMENTS.eventID = ?", eventID); err != nil {
|
||||
if err := db.DB.Select(&assignmentRows, "SELECT USERS.name AS userName, TASKS.text AS taskName FROM USER_ASSIGNMENTS LEFT JOIN USERS ON USER_ASSIGNMENTS.userName = USERS.name LEFT JOIN TASKS ON USER_ASSIGNMENTS.taskID = TASKS.id WHERE USER_ASSIGNMENTS.eventID = ?", eventID); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
// transform the rows into the returned map
|
||||
|
||||
@@ -3,46 +3,70 @@ package events
|
||||
import (
|
||||
"github.com/johannesbuehl/golunteer/backend/pkg/db"
|
||||
"github.com/johannesbuehl/golunteer/backend/pkg/db/assignments"
|
||||
"github.com/johannesbuehl/golunteer/backend/pkg/logger"
|
||||
)
|
||||
|
||||
type Event struct {
|
||||
type EventWithAssignment struct {
|
||||
eventDataDB
|
||||
Tasks []string
|
||||
Assignments map[string]string
|
||||
Tasks map[string]*string `json:"tasks"`
|
||||
}
|
||||
|
||||
type eventDataDB struct {
|
||||
Id int `db:"id"`
|
||||
Date string `db:"date"`
|
||||
Description string `db:"description"`
|
||||
Id int `db:"id" json:"id"`
|
||||
Date string `db:"date" json:"date"`
|
||||
Description string `db:"description" json:"description"`
|
||||
}
|
||||
|
||||
// transform the database-entry to an Event
|
||||
func (e *eventDataDB) Event() (Event, error) {
|
||||
func (e *eventDataDB) Event() (EventWithAssignment, error) {
|
||||
// get the availabilites associated with the event
|
||||
if assignemnts, err := assignments.Event(e.Id); err != nil {
|
||||
return Event{}, err
|
||||
return EventWithAssignment{}, err
|
||||
} else {
|
||||
return Event{
|
||||
return EventWithAssignment{
|
||||
eventDataDB: *e,
|
||||
Assignments: assignemnts,
|
||||
Tasks: assignemnts,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// get all the event ids
|
||||
func All() (map[int]eventDataDB, error) {
|
||||
func All() ([]eventDataDB, error) {
|
||||
var dbRows []eventDataDB
|
||||
|
||||
if err := db.DB.Select(&dbRows, "SELECT * FROM EVENTS"); err != nil {
|
||||
if err := db.DB.Select(&dbRows, "SELECT *, DATE_FORMAT(date, '%Y-%m-%dT%H:%i:%s') as date FROM EVENTS"); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
eventsMap := map[int]eventDataDB{}
|
||||
|
||||
for _, idRow := range dbRows {
|
||||
eventsMap[idRow.Id] = idRow
|
||||
}
|
||||
|
||||
return eventsMap, nil
|
||||
return dbRows, nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithAssignments() ([]EventWithAssignment, error) {
|
||||
// get all events
|
||||
if eventsDB, err := All(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
events := make([]EventWithAssignment, len(eventsDB))
|
||||
|
||||
for ii, e := range eventsDB {
|
||||
if ev, err := e.Event(); err != nil {
|
||||
logger.Logger.Error().Msgf("can't get assignments for event with id = %d: %v", e.Id, err)
|
||||
} else {
|
||||
events[ii] = ev
|
||||
}
|
||||
}
|
||||
|
||||
return events, nil
|
||||
}
|
||||
}
|
||||
|
||||
func UserPending(userName string) (int, error) {
|
||||
var result struct {
|
||||
Count int `db:"count(*)"`
|
||||
}
|
||||
|
||||
if err := db.DB.QueryRowx("SELECT count(*) FROM USERS WHERE name = ? AND name NOT IN (SELECT userName FROM USER_AVAILABILITIES)", userName).StructScan(&result); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
return result.Count, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,28 +5,29 @@ import (
|
||||
"github.com/johannesbuehl/golunteer/backend/pkg/db/events"
|
||||
)
|
||||
|
||||
func getEvents(c *fiber.Ctx) responseMessage {
|
||||
func getEventsAssignments(args HandlerArgs) responseMessage {
|
||||
response := responseMessage{}
|
||||
|
||||
// get all eventRows
|
||||
if eventRows, err := events.All(); err != nil {
|
||||
if events, err := events.WithAssignments(); err != nil {
|
||||
response.Status = fiber.StatusInternalServerError
|
||||
|
||||
logger.Error().Msgf("events retrieving failed: %v", err)
|
||||
logger.Error().Msgf("can't retrieve events with assignments: %v", err)
|
||||
} else {
|
||||
// get the data for all the allEvents
|
||||
allEvents := []events.Event{}
|
||||
|
||||
for _, eventRow := range eventRows {
|
||||
if e, err := eventRow.Event(); err != nil {
|
||||
logger.Error().Msgf("error while populating event with id = %d: %v", eventRow.Id, err)
|
||||
} else {
|
||||
allEvents = append(allEvents, e)
|
||||
}
|
||||
|
||||
// response.Data = struct{ Events []events.Event }{Events: allEvents}
|
||||
response.Data = allEvents
|
||||
}
|
||||
response.Data = events
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
func getEventsUserPending(args HandlerArgs) responseMessage {
|
||||
response := responseMessage{}
|
||||
|
||||
if count, err := events.UserPending(args.User.UserName); err != nil {
|
||||
response.Status = fiber.StatusInternalServerError
|
||||
|
||||
logger.Warn().Msgf("can't query database for users %q pending events: %v", args.User.UserName, err)
|
||||
} else {
|
||||
response.Data = count
|
||||
}
|
||||
|
||||
return response
|
||||
|
||||
@@ -1,67 +1,98 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/johannesbuehl/golunteer/backend/pkg/db"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
type UserLogin struct {
|
||||
UserName string `json:"userName"`
|
||||
LoggedIn bool `json:"loggedIn"`
|
||||
}
|
||||
|
||||
// handle welcome-messages from clients
|
||||
func handleWelcome(c *fiber.Ctx) error {
|
||||
logger.Debug().Msgf("HTTP %s request: %q", c.Method(), c.OriginalURL())
|
||||
|
||||
response := responseMessage{}
|
||||
response.Data = UserLogin{
|
||||
LoggedIn: false,
|
||||
response.Data = UserChecked{
|
||||
Admin: false,
|
||||
}
|
||||
|
||||
if ok, err := checkUser(c); err != nil {
|
||||
if user, err := checkUser(c); err != nil {
|
||||
response.Status = fiber.StatusInternalServerError
|
||||
|
||||
logger.Warn().Msgf("can't check user: %v", err)
|
||||
} else if !ok {
|
||||
} else if user == nil {
|
||||
response.Status = fiber.StatusNoContent
|
||||
|
||||
logger.Debug().Msgf("user not authorized")
|
||||
} else {
|
||||
if uid, _, err := extractJWT(c); err != nil {
|
||||
response.Status = fiber.StatusBadRequest
|
||||
|
||||
logger.Error().Msgf("can't extract JWT: %v", err)
|
||||
} else {
|
||||
if users, err := db.SelectOld[UserDB]("users", "uid = ? LIMIT 1", strconv.Itoa(uid)); err != nil {
|
||||
response.Status = fiber.StatusInternalServerError
|
||||
|
||||
logger.Error().Msgf("can't get users from database: %v", err)
|
||||
} else {
|
||||
if len(users) != 1 {
|
||||
response.Status = fiber.StatusForbidden
|
||||
response.Message = "unknown user"
|
||||
|
||||
removeSessionCookie(c)
|
||||
} else {
|
||||
user := users[0]
|
||||
|
||||
response.Data = UserLogin{
|
||||
UserName: user.UserName,
|
||||
LoggedIn: true,
|
||||
}
|
||||
}
|
||||
|
||||
logger.Debug().Msgf("welcomed user with uid = %v", uid)
|
||||
}
|
||||
response.Data = UserChecked{
|
||||
UserName: user.UserName,
|
||||
Admin: user.Admin,
|
||||
}
|
||||
|
||||
logger.Debug().Msgf("welcomed user %q", user.UserName)
|
||||
}
|
||||
|
||||
return response.send(c)
|
||||
}
|
||||
|
||||
const messageWrongLogin = "Unkown user or wrong password"
|
||||
|
||||
func handleLogin(c *fiber.Ctx) error {
|
||||
panic("not implemented yet")
|
||||
logger.Debug().Msgf("HTTP %s request: %q", c.Method(), c.OriginalURL())
|
||||
|
||||
// extract username and password from the request
|
||||
requestBody := struct {
|
||||
Username string `json:"userName" validate:"required"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
}{}
|
||||
|
||||
var response responseMessage
|
||||
|
||||
if err := c.BodyParser(&requestBody); err != nil {
|
||||
logger.Debug().Msgf("can't parse login-body: %v", err)
|
||||
|
||||
response.Status = fiber.StatusBadRequest
|
||||
|
||||
// validate the body
|
||||
} else if err := validate.Struct(requestBody); err != nil {
|
||||
logger.Warn().Msgf("can't parse login-body: %v", err)
|
||||
} else {
|
||||
// query the database for the user
|
||||
var result userDB
|
||||
if err := db.DB.QueryRowx("SELECT password, admin, tokenID FROM USERS WHERE name = ?", requestBody.Username).StructScan(&result); err != nil {
|
||||
response.Status = fiber.StatusForbidden
|
||||
response.Message = messageWrongLogin
|
||||
|
||||
logger.Info().Msgf("can't get user with name = %q from database", requestBody.Username)
|
||||
} else {
|
||||
// hash the password
|
||||
if bcrypt.CompareHashAndPassword(result.Password, []byte(requestBody.Password)) != nil {
|
||||
response.Status = fiber.StatusForbidden
|
||||
|
||||
logger.Info().Msgf("login denied: wrong password for user with name = %q", requestBody.Username)
|
||||
} else {
|
||||
// password is correct -> generate the JWT
|
||||
if jwt, err := config.SignJWT(JWTPayload{
|
||||
UserID: requestBody.Username,
|
||||
TokenID: result.TokenID,
|
||||
}); err != nil {
|
||||
response.Status = fiber.StatusInternalServerError
|
||||
logger.Error().Msgf("can't create JWT: %v", err)
|
||||
} else {
|
||||
setSessionCookie(c, &jwt)
|
||||
|
||||
response.Data = UserChecked{
|
||||
UserName: requestBody.Username,
|
||||
Admin: true,
|
||||
}
|
||||
|
||||
logger.Debug().Msgf("user %q logged in", requestBody.Username)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return response.send(c)
|
||||
}
|
||||
|
||||
// handles logout-requests
|
||||
@@ -70,9 +101,5 @@ func handleLogout(c *fiber.Ctx) error {
|
||||
|
||||
removeSessionCookie(c)
|
||||
|
||||
return responseMessage{
|
||||
Data: UserLogin{
|
||||
LoggedIn: false,
|
||||
},
|
||||
}.send(c)
|
||||
return responseMessage{}.send(c)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
_config "github.com/johannesbuehl/golunteer/backend/pkg/config"
|
||||
@@ -11,6 +12,8 @@ import (
|
||||
_logger "github.com/johannesbuehl/golunteer/backend/pkg/logger"
|
||||
)
|
||||
|
||||
var validate *validator.Validate
|
||||
|
||||
var logger = _logger.Logger
|
||||
var config = _config.Config
|
||||
|
||||
@@ -47,7 +50,14 @@ func (result responseMessage) send(c *fiber.Ctx) error {
|
||||
}
|
||||
}
|
||||
|
||||
type HandlerArgs struct {
|
||||
C *fiber.Ctx
|
||||
User UserChecked
|
||||
}
|
||||
|
||||
func init() {
|
||||
validate = validator.New()
|
||||
|
||||
// setup fiber
|
||||
app = fiber.New(fiber.Config{
|
||||
AppName: "johannes-pv",
|
||||
@@ -63,8 +73,8 @@ func init() {
|
||||
}
|
||||
|
||||
// map with the individual registered endpoints
|
||||
endpoints := map[string]map[string]func(*fiber.Ctx) responseMessage{
|
||||
"GET": {"events": getEvents},
|
||||
endpoints := map[string]map[string]func(HandlerArgs) responseMessage{
|
||||
"GET": {"events/assignments": getEventsAssignments, "events/user/pending": getEventsUserPending},
|
||||
"POST": {},
|
||||
"PATCH": {},
|
||||
"DELETE": {},
|
||||
@@ -81,7 +91,28 @@ func init() {
|
||||
handleMethods[method]("/api/"+address, func(c *fiber.Ctx) error {
|
||||
logger.Debug().Msgf("HTTP %s request: %q", c.Method(), c.OriginalURL())
|
||||
|
||||
return handler(c).send(c)
|
||||
var response responseMessage
|
||||
|
||||
if user, err := checkUser(c); err != nil {
|
||||
response = responseMessage{
|
||||
Status: fiber.StatusBadRequest,
|
||||
}
|
||||
|
||||
logger.Error().Msgf("can't check user: %v", err)
|
||||
} else if user == nil {
|
||||
response = responseMessage{
|
||||
Status: fiber.StatusNoContent,
|
||||
}
|
||||
|
||||
logger.Log().Msgf("user not authorized")
|
||||
} else {
|
||||
response = handler(HandlerArgs{
|
||||
C: c,
|
||||
User: *user,
|
||||
})
|
||||
}
|
||||
|
||||
return response.send(c)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -125,7 +156,7 @@ func removeSessionCookie(c *fiber.Ctx) {
|
||||
|
||||
// payload of the JSON webtoken
|
||||
type JWTPayload struct {
|
||||
UserID int `json:"userID"`
|
||||
UserID string `json:"userID"`
|
||||
TokenID string `json:"tokenID"`
|
||||
}
|
||||
|
||||
@@ -138,7 +169,7 @@ type JWT struct {
|
||||
// extracts the json webtoken from the request
|
||||
//
|
||||
// @returns (userID, tokenID, error)
|
||||
func extractJWT(c *fiber.Ctx) (int, string, error) {
|
||||
func extractJWT(c *fiber.Ctx) (string, string, error) {
|
||||
// get the session-cookie
|
||||
cookie := c.Cookies("session")
|
||||
|
||||
@@ -151,70 +182,57 @@ func extractJWT(c *fiber.Ctx) (int, string, error) {
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return -1, "", err
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// extract the claims from the JWT
|
||||
if claims, ok := token.Claims.(*JWT); ok && token.Valid {
|
||||
return claims.CustomClaims.UserID, claims.CustomClaims.TokenID, nil
|
||||
} else {
|
||||
return -1, "", fmt.Errorf("invalid JWT")
|
||||
return "", "", fmt.Errorf("invalid JWT")
|
||||
}
|
||||
}
|
||||
|
||||
// user-entry in the database
|
||||
type UserDB struct {
|
||||
UserName string `json:"userName"`
|
||||
Password []byte `json:"password"`
|
||||
Admin bool `json:"admin"`
|
||||
TokenID string `json:"tokenID"`
|
||||
type userDB struct {
|
||||
UserName string `db:"userName"`
|
||||
Password []byte `db:"password"`
|
||||
Admin bool `db:"admin"`
|
||||
TokenID string `db:"tokenID"`
|
||||
}
|
||||
|
||||
type UserChecked struct {
|
||||
UserName string `json:"userName" db:"userName"`
|
||||
Admin bool `json:"admin" db:"admin"`
|
||||
}
|
||||
|
||||
// checks wether the request is from a valid user
|
||||
func checkUser(c *fiber.Ctx) (bool, error) {
|
||||
uid, tid, err := extractJWT(c)
|
||||
func checkUser(c *fiber.Ctx) (*UserChecked, error) {
|
||||
userName, tokenID, err := extractJWT(c)
|
||||
|
||||
if err != nil {
|
||||
return false, nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var dbResult struct {
|
||||
TokenID string `db:"tokenID"`
|
||||
Admin bool `db:"admin"`
|
||||
}
|
||||
|
||||
// retrieve the user from the database
|
||||
response, err := db.SelectOld[UserDB]("users", "uid = ? LIMIT 1", uid)
|
||||
if err := db.DB.QueryRowx("SELECT tokenID, admin FROM USERS WHERE name = ?", userName).StructScan(&dbResult); err != nil {
|
||||
return nil, err
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// if exactly one user came back and the tID is valid, the user is authorized
|
||||
if len(response) == 1 && response[0].TokenID == tid {
|
||||
// if the tokenID is valid, the user is authorized
|
||||
} else if dbResult.TokenID != tokenID {
|
||||
return nil, err
|
||||
} else {
|
||||
// reset the expiration of the cookie
|
||||
setSessionCookie(c, nil)
|
||||
|
||||
return true, err
|
||||
} else {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
// checks wether the request is from the admin
|
||||
func checkAdmin(c *fiber.Ctx) (bool, error) {
|
||||
uid, tokenID, err := extractJWT(c)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// retrieve the user from the database
|
||||
response, err := db.SelectOld[UserDB]("users", "uid = ? LIMIT 1", uid)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// if exactly one user came back and its name is "admin", the user is the admin
|
||||
if len(response) != 1 {
|
||||
return false, fmt.Errorf("user doesn't exist")
|
||||
} else {
|
||||
return response[0].UserName == "admin" && response[0].TokenID == tokenID, err
|
||||
return &UserChecked{
|
||||
UserName: userName,
|
||||
Admin: dbResult.Admin,
|
||||
}, err
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user