generated from chaincode-labs/go-starter
/
post_forgot_password.go
106 lines (86 loc) · 3.13 KB
/
post_forgot_password.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package auth
import (
"database/sql"
"net/http"
"net/url"
"path"
"time"
"github.com/labstack/echo/v4"
"github.com/trino-network/pay-srv/internal/api"
"github.com/trino-network/pay-srv/internal/models"
"github.com/trino-network/pay-srv/internal/types"
"github.com/trino-network/pay-srv/internal/util"
"github.com/trino-network/pay-srv/internal/util/db"
"github.com/volatiletech/null/v8"
"github.com/volatiletech/sqlboiler/v4/boil"
)
func PostForgotPasswordRoute(s *api.Server) *echo.Route {
return s.Router.APIV1Auth.POST("/forgot-password", postForgotPasswordHandler(s))
}
func postForgotPasswordHandler(s *api.Server) echo.HandlerFunc {
return func(c echo.Context) error {
ctx := c.Request().Context()
var body types.PostForgotPasswordPayload
if err := util.BindAndValidateBody(c, &body); err != nil {
return err
}
log := util.LogFromContext(ctx).With().Str("username", body.Username.String()).Logger()
user, err := models.Users(models.UserWhere.Username.EQ(null.StringFrom(body.Username.String()))).One(ctx, s.DB)
if err != nil {
if err == sql.ErrNoRows {
log.Debug().Str("username", body.Username.String()).Err(err).Msg("User not found")
return c.NoContent(http.StatusNoContent)
}
log.Debug().Str("username", body.Username.String()).Err(err).Msg("Failed to load user")
return err
}
if !user.IsActive {
log.Debug().Msg("User is deactivated, rejecting password reset")
return c.NoContent(http.StatusNoContent)
}
if !user.Password.Valid {
log.Debug().Msg("User is missing password, forbidding password reset")
return c.NoContent(http.StatusNoContent)
}
if err := db.WithTransaction(ctx, s.DB, func(tx boil.ContextExecutor) error {
passwordResetToken, err := user.PasswordResetTokens(
models.PasswordResetTokenWhere.CreatedAt.GT(time.Now().Add(time.Minute*1)),
).One(ctx, tx)
if err != nil {
if err == sql.ErrNoRows {
log.Debug().Err(err).Msg("No valid password reset token exists, creating new one")
passwordResetToken = &models.PasswordResetToken{
UserID: user.ID,
ValidUntil: time.Now().Add(s.Config.Auth.PasswordResetTokenValidity),
}
if err := passwordResetToken.Insert(ctx, tx, boil.Infer()); err != nil {
log.Debug().Err(err).Msg("Failed to insert password reset token")
return err
}
} else {
log.Debug().Err(err).Msg("Failed to check for existing valid password reset token")
return err
}
}
u, err := url.Parse(s.Config.Frontend.BaseURL)
if err != nil {
log.Error().Err(err).Msg("Failed to parse frontend base URL")
return err
}
u.Path = path.Join(u.Path, s.Config.Frontend.PasswordResetEndpoint)
q := u.Query()
q.Set("token", passwordResetToken.Token)
u.RawQuery = q.Encode()
if err := s.Mailer.SendPasswordReset(ctx, user.Username.String, u.String()); err != nil {
log.Debug().Err(err).Msg("Failed to send password reset email")
return err
}
return nil
}); err != nil {
log.Debug().Err(err).Msg("Failed to initiate password reset")
return err
}
log.Debug().Msg("Successfully initiated password reset")
return c.NoContent(http.StatusNoContent)
}
}