add login method and api endpoint
This commit is contained in:
parent
d7c7140981
commit
224edb3ce5
8 changed files with 201 additions and 0 deletions
62
internal/api/users.go
Normal file
62
internal/api/users.go
Normal file
|
@ -0,0 +1,62 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"codeberg.org/lauralani/go-urlsh/internal/misc"
|
||||
"codeberg.org/lauralani/go-urlsh/models"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
func HandleUserPost(c *fiber.Ctx) error {
|
||||
var newuser models.LoginRequest
|
||||
|
||||
err := json.Unmarshal(c.Body(), &newuser)
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
return fiber.NewError(fiber.StatusBadRequest, "400 Bad Request")
|
||||
}
|
||||
|
||||
usercount, err := models.DB.NewSelect().Model((*models.User)(nil)).Count(context.Background())
|
||||
if err != nil {
|
||||
log.Printf("[POST /api/v1/users] Error querying database for users: %v\n", err.Error())
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
|
||||
}
|
||||
if usercount != 0 {
|
||||
log.Printf("[POST /api/v1/users] someone trying to create user but user already exists\n")
|
||||
return fiber.NewError(fiber.StatusUnauthorized, "401 Unauthorized")
|
||||
} else {
|
||||
salt := misc.RandomString(15)
|
||||
created := time.Now()
|
||||
hashbytes := sha256.Sum256([]byte(salt + newuser.Password))
|
||||
fmt.Printf("%x\n", hashbytes)
|
||||
|
||||
hash := hex.EncodeToString(hashbytes[:])
|
||||
|
||||
user := new(models.User)
|
||||
user.UserName = newuser.Username
|
||||
user.PasswordSalt = salt
|
||||
user.PasswordHash = hash
|
||||
user.Created = created
|
||||
|
||||
_, err = models.DB.NewInsert().Model(user).Exec(context.Background())
|
||||
if err != nil {
|
||||
log.Printf("[POST /api/v1/users] Error adding user %v to database : %v\n", newuser.Username, err.Error())
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
|
||||
}
|
||||
|
||||
userresponse := models.UserResponse{UserName: newuser.Username, Created: created}
|
||||
|
||||
c.Status(fiber.StatusCreated)
|
||||
err = c.JSON(userresponse)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ package app
|
|||
|
||||
import (
|
||||
"codeberg.org/lauralani/go-urlsh/internal/api"
|
||||
"codeberg.org/lauralani/go-urlsh/internal/web"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/compress"
|
||||
"github.com/gofiber/fiber/v2/middleware/cors"
|
||||
|
@ -47,6 +48,8 @@ func SetupFiber() error {
|
|||
|
||||
fiberapp.Static("/admin/", "./web")
|
||||
|
||||
fiberapp.Post("/admin/login", web.HandleLogin)
|
||||
|
||||
v1 := fiberapp.Group("/api/v1")
|
||||
v1.Use(cors.New(cors.Config{AllowOrigins: "*"}))
|
||||
|
||||
|
@ -60,6 +63,8 @@ func SetupFiber() error {
|
|||
v1.Post("/apikeys", api.HandleApiKeysPost)
|
||||
v1.Delete("/apikeys/:id<guid>", api.HandleApiKeysPost)
|
||||
|
||||
v1.Post("/users", api.HandleUserPost)
|
||||
|
||||
listenerr := fiberapp.Listen(ip + ":" + port)
|
||||
if listenerr != nil {
|
||||
return listenerr
|
||||
|
|
|
@ -19,5 +19,20 @@ func InitializeDB() error {
|
|||
if err != nil {
|
||||
return fmt.Errorf("couldn't create database: [%w]", err)
|
||||
}
|
||||
|
||||
_, err = models.DB.NewCreateTable().IfNotExists().Model((*models.User)(nil)).Exec(context.Background())
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't create database: [%w]", err)
|
||||
}
|
||||
|
||||
_, err = models.DB.NewCreateTable().IfNotExists().Model((*models.Login)(nil)).Exec(context.Background())
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't create database: [%w]", err)
|
||||
}
|
||||
|
||||
_, err = models.DB.NewCreateTable().IfNotExists().Model((*models.ApiKey)(nil)).Exec(context.Background())
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't create database: [%w]", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
67
internal/web/login.go
Normal file
67
internal/web/login.go
Normal file
|
@ -0,0 +1,67 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"codeberg.org/lauralani/go-urlsh/models"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/google/uuid"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
func HandleLogin(c *fiber.Ctx) error {
|
||||
var login models.LoginRequest
|
||||
var user models.User
|
||||
|
||||
if err := c.BodyParser(&login); err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "400 Bad Request")
|
||||
}
|
||||
|
||||
err := models.DB.NewSelect().Model(&user).Where("username = ?", login.Username).Scan(context.Background())
|
||||
if err != nil {
|
||||
log.Printf("Error authenticating User %v: User doesn't exist by IP %v\n", login.Username, c.IP())
|
||||
return fiber.NewError(fiber.StatusUnauthorized, "401 Unauthorized")
|
||||
}
|
||||
|
||||
passwordsumbytes := sha256.Sum256([]byte(user.PasswordSalt + login.Password))
|
||||
passwordsum := hex.EncodeToString(passwordsumbytes[:])
|
||||
|
||||
if passwordsum == user.PasswordHash {
|
||||
// Passwords match
|
||||
|
||||
expires := time.Now().Add(30 * 24 * time.Hour)
|
||||
key := uuid.New().String()
|
||||
login := new(models.Login)
|
||||
|
||||
cookie := new(fiber.Cookie)
|
||||
cookie.Name = "gourlsh_auth"
|
||||
cookie.Value = key
|
||||
cookie.Expires = expires
|
||||
|
||||
login.Expires = expires
|
||||
login.Cookie = key
|
||||
login.UserName = user.UserName
|
||||
|
||||
_, err = models.DB.NewInsert().Model(login).Exec(context.Background())
|
||||
if err != nil {
|
||||
log.Printf("DB Error inserting login cookie information for user %v: %v\n", login.UserName, err.Error())
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
|
||||
}
|
||||
|
||||
user.LastLogin = time.Now()
|
||||
_, err = models.DB.NewUpdate().Model(&user).WherePK().Exec(context.Background())
|
||||
if err != nil {
|
||||
log.Printf("DB Error updating last login information for user %v: %v\n", login.UserName, err.Error())
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
|
||||
}
|
||||
c.Cookie(cookie)
|
||||
c.Status(fiber.StatusSeeOther)
|
||||
c.Location("/admin/")
|
||||
return nil
|
||||
} else {
|
||||
log.Printf("Error authenticating User %v: password mismatch by IP %v\n", login.Username, c.IP())
|
||||
return fiber.NewError(fiber.StatusUnauthorized, "401 Unauthorized")
|
||||
}
|
||||
}
|
13
models/apikey.go
Normal file
13
models/apikey.go
Normal file
|
@ -0,0 +1,13 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"github.com/uptrace/bun"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ApiKey struct {
|
||||
bun.BaseModel `bun:"table:apikeys"`
|
||||
Key string `bun:"key,pk,type:uuid,default:gen_random_uuid()" json:"key,omitempty"`
|
||||
UserName string `bun:"username,notnull" json:"username"`
|
||||
Created time.Time `bun:"created,default:now()" json:"created"`
|
||||
}
|
19
models/login.go
Normal file
19
models/login.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"github.com/uptrace/bun"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Login struct {
|
||||
bun.BaseModel `bun:"table:logins"`
|
||||
ID int `bun:"id,pk,autoincrement"`
|
||||
UserName string `bun:"username,notnull"`
|
||||
Cookie string `bun:"cookie,notnull"`
|
||||
Expires time.Time `bun:"expires,notnull"`
|
||||
}
|
||||
|
||||
type LoginRequest struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
20
models/user.go
Normal file
20
models/user.go
Normal file
|
@ -0,0 +1,20 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"github.com/uptrace/bun"
|
||||
"time"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
bun.BaseModel `bun:"table:users"`
|
||||
UserName string `bun:"username,pk" json:"username"`
|
||||
Created time.Time `bun:"created,notnull,default:now()" json:"created"`
|
||||
LastLogin time.Time `bun:"lastlogin" json:"last_login"`
|
||||
PasswordSalt string `bun:"salt" json:"password_salt"`
|
||||
PasswordHash string `bun:"password" json:"password_hash"`
|
||||
}
|
||||
|
||||
type UserResponse struct {
|
||||
UserName string `json:"username"`
|
||||
Created time.Time `json:"created"`
|
||||
}
|
Loading…
Reference in a new issue