user.go 2.82 KB
package main

import (
	"context"
	"net/http"
	"strings"
	"time"

	"github.com/dgrijalva/jwt-go"
	"github.com/go-sql-driver/mysql"
	"github.com/labstack/echo/v4"
	"golang.org/x/crypto/sha3"
)

type User struct {
	No        uint64    `json:"no"`
	ID        string    `json:"id"`
	Name      string    `json:"name"`
	CreatedAt time.Time `json:"created_at"`
	ExpiredAt time.Time `json:"expired_at"`
}

func (app *App) PostUsers(c echo.Context) error {
	body := struct {
		ID       string `json:"id"`
		Password string `json:"password"`
		Name     string `json:"name"`
	}{}
	if c.Bind(&body) != nil {
		return echo.NewHTTPError(http.StatusBadRequest, "Failed to parse request json")
	}

	hash := sha3.Sum256([]byte(body.Password))

	res, err := app.db.Exec("INSERT INTO users (`id`, `password`, `name`) VALUES (?, ?, ?)", body.ID, hash[:], body.Name)
	if err != nil {
		if merr, ok := err.(*mysql.MySQLError); ok {
			if merr.Number == 1062 {
				return echo.NewHTTPError(http.StatusConflict, "Already registered")
			}
		}

		return echo.NewHTTPError(http.StatusInternalServerError, "Failed to register")
	}

	no, _ := res.LastInsertId()
	return c.JSON(http.StatusOK, echo.Map{"user_no": no})
}

type AuthClaims struct {
	UserNo uint64 `json:"user_no"`
	jwt.StandardClaims
}

func (app *App) PostTokens(c echo.Context) error {
	body := struct {
		ID       string `json:"id"`
		Password string `json:"password"`
	}{}
	if c.Bind(&body) != nil {
		return echo.NewHTTPError(http.StatusBadRequest, "Failed to parse request json")
	}

	hash := sha3.Sum256([]byte(body.Password))
	rows, err := app.db.Query("SELECT `no` FROM users WHERE `id`=? AND `password`=?", body.ID, hash[:])
	if err != nil {
		return echo.NewHTTPError(http.StatusInternalServerError, "Failed to register")
	}

	if !rows.Next() {
		return echo.NewHTTPError(http.StatusUnauthorized, "Login failed")
	}

	no := uint64(0)
	rows.Scan(&no)

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, AuthClaims{UserNo: no})
	auth, err := token.SignedString([]byte(app.Config.TokenSecret))
	if err != nil {
		return echo.NewHTTPError(http.StatusInternalServerError, "Login failed")
	}

	return c.JSON(http.StatusOK, map[string]interface{}{"token": auth})
}

func (app *App) Authenticate(next func(http.ResponseWriter, *http.Request)) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		auth := r.Header.Get("Authorization")
		if len(auth) > 6 && strings.Index(auth, "Bearer ") == 0 {
			token, err := jwt.ParseWithClaims(auth[7:], &AuthClaims{}, func(token *jwt.Token) (interface{}, error) {
				return []byte(app.Config.TokenSecret), nil
			})

			if err == nil {
				claims := token.Claims.(*AuthClaims)
				ctx := context.WithValue(r.Context(), PropUserNo, claims.UserNo)
				next(w, r.WithContext(ctx))
				return
			}
		}

		WriteError(w, http.StatusUnauthorized, "Authorization failed")
	})
}