finish with create user and start with user login
parent
71fe2a5e46
commit
e0266d9788
|
@ -1,7 +1,6 @@
|
|||
[server]
|
||||
debug = true
|
||||
host = "127.0.0.1:3000"
|
||||
secret = "KAWXQHtKaz8BmWksXsQFPrdE78ptBuwBsaUNt8XcKGZt44QbUp"
|
||||
|
||||
[database]
|
||||
host = "127.0.0.1:3306"
|
||||
|
|
34
example.http
34
example.http
|
@ -1,17 +1,27 @@
|
|||
###
|
||||
#### user login
|
||||
|
||||
GET http://localhost:3000/api/v1/users
|
||||
Content-Type: application/xml
|
||||
# Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MjEyNTAxODcsInVzZXJfaGFzaHRhZyI6IjRIUVQ3NSIsInVzZXJfaWQiOiJhZDAyYzY2NzRmYjI0OWY0YWI4NWU1MjYzZDUyMzAwZiIsInVzZXJuYW1lIjoicnVpc3BpcGUxIn0.wJJO_2maGG_1h2hingzZm3VmNJnmQnpzknU4dnw-8IE
|
||||
Cookie: session_id=b81aedb75b084d01945ee45134ddbc39;
|
||||
|
||||
###
|
||||
|
||||
POST http://localhost:3000/api/v1/users
|
||||
POST http://localhost:3000/api/v1/user/login
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"username": "183",
|
||||
"email": "183@roese.dev",
|
||||
"password": "teksmkamsdkasd- a , ' . + * maskdmaskdm"
|
||||
"username": "107",
|
||||
"password": "my-passworda"
|
||||
}
|
||||
|
||||
### get users
|
||||
|
||||
POST http://localhost:3000/api/v1/users
|
||||
Content-Type: application/xml
|
||||
Cookie: session_id=5CLPfNbit0SCNoyRy2AWslJSWTascm3q
|
||||
|
||||
#### create user
|
||||
|
||||
POST http://localhost:3000/api/v1/users
|
||||
Content-Type: application/json
|
||||
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36
|
||||
|
||||
{
|
||||
"username": "107",
|
||||
"email": "107@roese.dev",
|
||||
"password": "my-password"
|
||||
}
|
1
go.mod
1
go.mod
|
@ -12,6 +12,7 @@ require (
|
|||
github.com/google/uuid v1.2.0
|
||||
github.com/klauspost/compress v1.12.2 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/mileusna/useragent v1.0.2 // indirect
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||
github.com/satori/go.uuid v1.2.0 // indirect
|
||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||
|
|
2
go.sum
2
go.sum
|
@ -28,6 +28,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mileusna/useragent v1.0.2 h1:DgVKtiPnjxlb73z9bCwgdUvU2nQNQ97uhgfO8l9uz/w=
|
||||
github.com/mileusna/useragent v1.0.2/go.mod h1:3d8TOmwL/5I8pJjyVDteHtgDGcefrFUX4ccGOMKNYYc=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
|
|
|
@ -13,9 +13,8 @@ type Config struct {
|
|||
}
|
||||
|
||||
type server struct {
|
||||
Debug bool
|
||||
Host string
|
||||
Secret string
|
||||
Debug bool
|
||||
Host string
|
||||
}
|
||||
|
||||
type database struct {
|
||||
|
|
|
@ -2,7 +2,6 @@ package database
|
|||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.umbach.dev/app-idea/rest-api/modules/config"
|
||||
|
@ -20,7 +19,7 @@ func GetDatabase() (*sql.DB, error) {
|
|||
|
||||
if err := db.Ping(); err != nil {
|
||||
log.Warnln("DB Connection error:", err.Error())
|
||||
return nil, errors.New(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return db, nil
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
package user
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"database/sql"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -12,14 +14,11 @@ import (
|
|||
"git.umbach.dev/app-idea/rest-api/modules/database"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/google/uuid"
|
||||
ua "github.com/mileusna/useragent"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/zhengxiaowai/shortuuid"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
//err = bcrypt.CompareHashAndPassword(hashedPassword, []byte("hello wolrd"))
|
||||
//fmt.Println(err)
|
||||
|
||||
type LoginInput struct {
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
|
@ -92,7 +91,12 @@ func NewUser(c *fiber.Ctx) error {
|
|||
}
|
||||
|
||||
if input.Hashtag == "" {
|
||||
input.Hashtag = RandomHashtag(db, 6)
|
||||
input.Hashtag, err = generateRandomHashtag(db, 6)
|
||||
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
return c.SendStatus(fiber.StatusInternalServerError)
|
||||
}
|
||||
} else if !isHashtagValid(db, input.Hashtag) {
|
||||
return c.SendStatus(fiber.StatusUnprocessableEntity)
|
||||
}
|
||||
|
@ -101,49 +105,67 @@ func NewUser(c *fiber.Ctx) error {
|
|||
hashedPassword, err := bcrypt.GenerateFromPassword(password, bcrypt.DefaultCost)
|
||||
|
||||
if err != nil {
|
||||
log.Warnln("bcrypt password error", err)
|
||||
log.Warnln("Failed to bcrypt password", err)
|
||||
return c.SendStatus(fiber.StatusInternalServerError)
|
||||
}
|
||||
|
||||
user_id := strings.Replace(uuid.New().String(), "-", "", -1)
|
||||
userId := strings.Replace(uuid.New().String(), "-", "", -1)
|
||||
created := time.Now().Format("2006-01-02 15:04:05") // YYYY-MM-DD hh:mm:ss
|
||||
|
||||
stmt, err := db.Prepare("INSERT INTO users (user_id, user_hashtag, username, email, password, created) VALUES (?, ?, ?, ?, ?, ?);")
|
||||
stmt.Exec(user_id, input.Hashtag, input.Username, input.Email, hashedPassword, created)
|
||||
stmt.Exec(userId, input.Hashtag, input.Username, input.Email, hashedPassword, created)
|
||||
|
||||
stmt.Close()
|
||||
|
||||
expires := time.Now().Add(time.Hour * 72)
|
||||
session_id := strings.Replace(uuid.New().String(), "-", "", -1)
|
||||
if err != nil {
|
||||
log.Warnln("Failed to insert user to db", err.Error())
|
||||
return c.SendStatus(fiber.StatusInternalServerError)
|
||||
}
|
||||
|
||||
//h := sha256.New()
|
||||
//h.Write([]byte(config.GetConfig().Server.Secret))
|
||||
//b := h.Sum([]byte(session_id))
|
||||
sessionId, err := createUserSession(db, userId, c.IP(), string(c.Context().UserAgent()))
|
||||
|
||||
//log.Infoln("sha256", h, base64.StdEncoding.EncodeToString(b))
|
||||
if err != nil {
|
||||
log.Warnln(err)
|
||||
return c.SendStatus(fiber.StatusInternalServerError)
|
||||
}
|
||||
|
||||
stmt2, err := db.Prepare("INSERT INTO sessions (user_id, session_id, expires) VALUES (?, ?, ?);")
|
||||
stmt2.Exec(user_id, session_id, expires)
|
||||
expires := getExpiresTime()
|
||||
|
||||
stmt2.Close()
|
||||
|
||||
log.Debugln("user created", user_id, input.Hashtag, input.Username, input.Email)
|
||||
|
||||
c.Cookie(&fiber.Cookie{Name: "session_id", Value: base64.StdEncoding.EncodeToString(b), Secure: true, HTTPOnly: true, Expires: expires})
|
||||
c.Cookie(&fiber.Cookie{Name: "session_id", Value: sessionId, Secure: true, HTTPOnly: true, Expires: expires})
|
||||
c.Cookie(&fiber.Cookie{Name: "username", Value: input.Username, Secure: true, Expires: expires})
|
||||
c.Cookie(&fiber.Cookie{Name: "user_hashtag", Value: input.Hashtag, Secure: true, Expires: expires})
|
||||
|
||||
log.Debugln("user created", userId, input.Hashtag, input.Username, input.Email)
|
||||
|
||||
return c.SendStatus(fiber.StatusCreated)
|
||||
}
|
||||
|
||||
func RandomHashtag(db *sql.DB, n int) string {
|
||||
func generateRandomString(n int) (string, error) {
|
||||
const letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||
r := make([]byte, n)
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
num, err := rand.Int(rand.Reader, big.NewInt(int64(len(letters))))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
r[i] = letters[num.Int64()]
|
||||
}
|
||||
|
||||
return string(r), nil
|
||||
}
|
||||
|
||||
func generateRandomHashtag(db *sql.DB, n int) (string, error) {
|
||||
c := make(chan bool)
|
||||
var s string
|
||||
var err error
|
||||
|
||||
for {
|
||||
su := shortuuid.NewShortUUID()
|
||||
su.SetAlphabet("ABCDEFGHJKLMNPQRSTUVWXYZ0123456789")
|
||||
s = su.Random(n)
|
||||
s, err = generateRandomString(6)
|
||||
|
||||
if err != nil {
|
||||
return "", errors.New("Error generating Hashtag " + err.Error())
|
||||
}
|
||||
|
||||
go func() {
|
||||
err := db.QueryRow("SELECT user_hashtag FROM users WHERE user_hashtag = ?", s).Scan(&s)
|
||||
|
@ -160,7 +182,7 @@ func RandomHashtag(db *sql.DB, n int) string {
|
|||
}
|
||||
}
|
||||
|
||||
return s
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func isHashtagValid(db *sql.DB, h string) bool {
|
||||
|
@ -170,8 +192,6 @@ func isHashtagValid(db *sql.DB, h string) bool {
|
|||
|
||||
err := db.QueryRow("SELECT user_hashtag FROM users WHERE user_hashtag = ?", h).Scan(&h)
|
||||
|
||||
fmt.Println("isHashtagValid", err == sql.ErrNoRows)
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
return true
|
||||
}
|
||||
|
@ -213,9 +233,84 @@ func isEmailAvailable(db *sql.DB, e string) bool {
|
|||
}
|
||||
|
||||
func SessionIdCheck(c *fiber.Ctx) error {
|
||||
session_id := c.Cookies("session_id")
|
||||
valid, err := isSessionIdValid(c.Cookies("session_id"))
|
||||
|
||||
log.Infoln("session_id", session_id)
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
return fiber.ErrInternalServerError
|
||||
}
|
||||
|
||||
if valid {
|
||||
return c.Next()
|
||||
}
|
||||
|
||||
return fiber.ErrUnauthorized
|
||||
}
|
||||
|
||||
func isSessionIdValid(sessionId string) (bool, error) {
|
||||
db, err := database.GetDatabase()
|
||||
|
||||
if db == nil || err != nil {
|
||||
return false, errors.New("DB error " + err.Error())
|
||||
}
|
||||
|
||||
defer db.Close()
|
||||
|
||||
err = db.QueryRow("SELECT session_id FROM sessions WHERE session_id = ?", sessionId).Scan(&sessionId)
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func createUserSession(db *sql.DB, userId string, ip string, userAgent string) (string, error) {
|
||||
sessionId, err := generateRandomString(32)
|
||||
|
||||
if err != nil {
|
||||
return "", errors.New("Failed to generate user session " + err.Error())
|
||||
}
|
||||
|
||||
log.Info(time.Now().String())
|
||||
log.Info(getExpiresTime())
|
||||
|
||||
stmt, err := db.Prepare("INSERT INTO sessions (user_id, session_id, ip, user_agent, last_login, expires) VALUES (?, ?, ?, ?, ?, ?);")
|
||||
|
||||
if err != nil {
|
||||
return "", errors.New("Failed to insert user into db " + err.Error())
|
||||
}
|
||||
|
||||
ua := ua.Parse(userAgent)
|
||||
|
||||
stmt.Exec(userId, sessionId, ip, ua.OS+" "+ua.Name, time.Now(), getExpiresTime())
|
||||
stmt.Close()
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func getExpiresTime() time.Time {
|
||||
// TODO: db default
|
||||
return time.Now().Add(time.Hour * 72)
|
||||
}
|
||||
|
||||
func Login(c *fiber.Ctx) error {
|
||||
var input LoginInput
|
||||
|
||||
if err := c.BodyParser(&input); err != nil {
|
||||
return c.SendStatus(fiber.StatusBadRequest)
|
||||
}
|
||||
|
||||
log.Println(input)
|
||||
|
||||
if input.Username != "" && !isValid(input.Username, 3, 30) {
|
||||
log.Info("for1")
|
||||
return c.SendStatus(fiber.StatusBadRequest)
|
||||
}
|
||||
|
||||
if input.Email != "" && !isEmailValid(input.Email) {
|
||||
log.Info("for2")
|
||||
return c.SendStatus(fiber.StatusForbidden)
|
||||
}
|
||||
|
||||
db, err := database.GetDatabase()
|
||||
|
||||
|
@ -223,9 +318,36 @@ func SessionIdCheck(c *fiber.Ctx) error {
|
|||
return c.SendStatus(fiber.StatusInternalServerError)
|
||||
}
|
||||
|
||||
var hashedPassword string
|
||||
|
||||
if input.Username != "" {
|
||||
err = db.QueryRow("SELECT password FROM users WHERE username = ?", input.Username).Scan(&hashedPassword)
|
||||
|
||||
log.Info(hashedPassword)
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Infoln("una", err)
|
||||
return c.SendStatus(fiber.StatusUnauthorized)
|
||||
}
|
||||
|
||||
err = bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(input.Password))
|
||||
|
||||
if err != nil {
|
||||
log.Warnln("Failed to comapare bcrypt password", err)
|
||||
return c.SendStatus(fiber.StatusUnauthorized)
|
||||
}
|
||||
|
||||
log.Info("password correct")
|
||||
|
||||
//err = bcrypt.CompareHashAndPassword(hashedPassword, []byte("hello wolrd"))
|
||||
//fmt.Println(err)
|
||||
|
||||
defer db.Close()
|
||||
|
||||
return fiber.ErrUnauthorized
|
||||
return c.JSON(fiber.Map{"test": hashedPassword})
|
||||
}
|
||||
|
||||
func GetUser(c *fiber.Ctx) error {
|
||||
|
|
|
@ -3,38 +3,15 @@ package router
|
|||
import (
|
||||
"git.umbach.dev/app-idea/rest-api/routers/api/v1/user"
|
||||
|
||||
jwt "github.com/form3tech-oss/jwt-go"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
/*
|
||||
func Protected() fiber.Handler {
|
||||
return jwtware.New(jwtware.Config{
|
||||
SigningKey: []byte(config.GetConfig().Server.Secret),
|
||||
ErrorHandler: jwtError,
|
||||
})
|
||||
}
|
||||
|
||||
func jwtError(c *fiber.Ctx, err error) error {
|
||||
if err.Error() == "Missing or malformed JWT" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"message": "Missing or malformed JWT"})
|
||||
}
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"message": "Invalid or expired JWT"})
|
||||
} */
|
||||
|
||||
func Test(c *fiber.Ctx) error {
|
||||
usert := c.Locals("user").(*jwt.Token)
|
||||
claims := usert.Claims.(jwt.MapClaims)
|
||||
name := claims["username"].(string)
|
||||
return c.SendString("Welcome" + name)
|
||||
}
|
||||
|
||||
func SetupRoutes(app *fiber.App) {
|
||||
api := app.Group("/api/v1")
|
||||
|
||||
/* Unauthenticated routes */
|
||||
|
||||
app.Get("/user", user.GetUser)
|
||||
u := api.Group("/user")
|
||||
u.Get("/user", user.GetUser)
|
||||
u.Post("/login", user.Login)
|
||||
|
||||
users := api.Group("/users")
|
||||
users.Post("/", user.NewUser)
|
||||
|
|
Loading…
Reference in New Issue