78 lines
2.9 KiB
Go
78 lines
2.9 KiB
Go
package routers
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/tech/sendico/pkg/api/http/response"
|
|
"github.com/tech/sendico/pkg/merrors"
|
|
"github.com/tech/sendico/pkg/model"
|
|
"github.com/tech/sendico/pkg/mservice"
|
|
"github.com/tech/sendico/server/interface/api/srequest"
|
|
"github.com/tech/sendico/server/interface/api/sresponse"
|
|
"github.com/tech/sendico/server/internal/server/confirmationimp"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
const pendingLoginTTLMinutes = 10
|
|
|
|
func (pr *PublicRouter) logUserIn(ctx context.Context, _ *http.Request, req *srequest.Login) http.HandlerFunc {
|
|
// Get the account database entry
|
|
trimmedLogin := strings.TrimSpace(req.Login)
|
|
account, err := pr.db.GetByEmail(ctx, strings.ToLower(trimmedLogin))
|
|
if errors.Is(err, merrors.ErrNoData) || (account == nil) {
|
|
pr.logger.Debug("User not found while logging in", zap.Error(err), zap.String("login", req.Login))
|
|
return response.Unauthorized(pr.logger, pr.service, "user not found")
|
|
}
|
|
if err != nil {
|
|
pr.logger.Warn("Failed to query user with email", zap.Error(err), zap.String("login", req.Login))
|
|
return response.Internal(pr.logger, pr.service, err)
|
|
}
|
|
|
|
if account.VerifyToken != "" {
|
|
return response.Forbidden(pr.logger, pr.service, "account_not_verified", "Account verification required")
|
|
}
|
|
|
|
if !account.MatchPassword(req.Password) {
|
|
return response.Unauthorized(pr.logger, pr.service, "password does not match")
|
|
}
|
|
|
|
pendingToken, err := pr.imp.CreatePendingToken(account, pendingLoginTTLMinutes)
|
|
if err != nil {
|
|
pr.logger.Warn("Failed to generate pending token", zap.Error(err))
|
|
return response.Internal(pr.logger, pr.service, err)
|
|
}
|
|
|
|
cfg := confirmationimp.DefaultConfig()
|
|
_, rec, err := pr.cstore.Create(ctx, account.ID, account.Login, model.ConfirmationTargetLogin, cfg, pr.generateCode)
|
|
if err != nil {
|
|
pr.logger.Warn("Failed to create login confirmation code", zap.Error(err))
|
|
return response.Internal(pr.logger, pr.service, err)
|
|
}
|
|
pr.logger.Info("Login confirmation code issued", zap.String("destination", pr.maskEmail(account.Login)))
|
|
|
|
return sresponse.LoginPending(pr.logger, account, &pendingToken, pr.maskEmail(account.Login), int(time.Until(rec.ExpiresAt).Seconds()))
|
|
}
|
|
|
|
func (a *PublicRouter) login(r *http.Request) http.HandlerFunc {
|
|
// TODO: add rate check
|
|
var req srequest.Login
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
a.logger.Info("Failed to decode login request", zap.Error(err))
|
|
return response.BadPayload(a.logger, mservice.Accounts, err)
|
|
}
|
|
req.Login = strings.TrimSpace(req.Login)
|
|
req.Password = strings.TrimSpace(req.Password)
|
|
if req.Login == "" {
|
|
return response.BadRequest(a.logger, mservice.Accounts, "email_missing", "login request has no user name")
|
|
}
|
|
if req.Password == "" {
|
|
return response.BadRequest(a.logger, mservice.Accounts, "password_missing", "login request has no password")
|
|
}
|
|
return a.logUserIn(r.Context(), r, &req)
|
|
}
|