package server import ( "context" "fmt" "net/http" "git.javil.eu/jacob1123/budgeteer" "git.javil.eu/jacob1123/budgeteer/postgres" "github.com/google/uuid" "github.com/labstack/echo/v4" ) const ( HeaderName = "Authorization" Bearer = "Bearer " ParamName = "token" ) func MustGetToken(c echo.Context) budgeteer.Token { //nolint:ireturn token := c.Get(ParamName) if token, ok := token.(budgeteer.Token); ok { return token } panic("Token is not a valid Token") } func (h *Handler) verifyLogin(c echo.Context) (budgeteer.Token, error) { //nolint:ireturn tokenString := c.Request().Header.Get(HeaderName) if len(tokenString) <= len(Bearer) { return nil, echo.NewHTTPError(http.StatusUnauthorized, "no authorization header supplied") } tokenString = tokenString[7:] token, err := h.TokenVerifier.VerifyToken(tokenString) if err != nil { return nil, fmt.Errorf("verify token '%s': %s", tokenString, err) } return token, nil } func (h *Handler) verifyLoginWithForbidden(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { token, err := h.verifyLogin(c) if err != nil { // c.Header("WWW-Authenticate", "Bearer") return echo.NewHTTPError(http.StatusForbidden, err) } c.Set(ParamName, token) return next(c) } } type loginInformation struct { Password string `json:"password"` User string `json:"user"` } func (h *Handler) loginPost(c echo.Context) error { var login loginInformation err := c.Bind(&login) if err != nil { return err } user, err := h.Service.GetUserByUsername(c.Request().Context(), login.User) if err != nil { return err } if err = h.CredentialsVerifier.Verify(login.Password, user.Password); err != nil { return err } token, err := h.TokenVerifier.CreateToken(&user) if err != nil { return err } go h.UpdateLastLogin(user.ID) budgets, err := h.Service.GetBudgetsForUser(c.Request().Context(), user.ID) if err != nil { return err } return c.JSON(http.StatusOK, LoginResponse{token, user, budgets}) } type LoginResponse struct { Token string User postgres.User Budgets []postgres.Budget } type registerInformation struct { Password string `json:"password"` Email string `json:"email"` Name string `json:"name"` } func (h *Handler) registerPost(c echo.Context) error { var register registerInformation err := c.Bind(®ister) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, "error parsing body") } if register.Email == "" || register.Password == "" || register.Name == "" { return echo.NewHTTPError(http.StatusBadRequest, "e-mail, password and name are required") } _, err = h.Service.GetUserByUsername(c.Request().Context(), register.Email) if err == nil { return echo.NewHTTPError(http.StatusBadRequest, "email is already taken") } hash, err := h.CredentialsVerifier.Hash(register.Password) if err != nil { return err } createUser := postgres.CreateUserParams{ Name: register.Name, Password: hash, Email: register.Email, } user, err := h.Service.CreateUser(c.Request().Context(), createUser) if err != nil { return err } token, err := h.TokenVerifier.CreateToken(&user) if err != nil { return err } go h.UpdateLastLogin(user.ID) budgets, err := h.Service.GetBudgetsForUser(c.Request().Context(), user.ID) if err != nil { return err } return c.JSON(http.StatusOK, LoginResponse{token, user, budgets}) } func (h *Handler) UpdateLastLogin(userID uuid.UUID) { _, err := h.Service.UpdateLastLogin(context.Background(), userID) if err != nil { fmt.Printf("Error updating last login: %s", err) } }