@@ -4,12 +4,14 @@ import (
4
4
"bytes"
5
5
"context"
6
6
"encoding/json"
7
- "github.com/netlify/gotrue/security"
8
- "github.com/sirupsen/logrus"
9
7
"io"
10
8
"io/ioutil"
11
9
"net/http"
12
10
"strings"
11
+ "time"
12
+
13
+ "github.com/netlify/gotrue/security"
14
+ "github.com/sirupsen/logrus"
13
15
14
16
"github.com/didip/tollbooth/v5"
15
17
"github.com/didip/tollbooth/v5/limiter"
@@ -154,6 +156,44 @@ func (a *API) limitHandler(lmt *limiter.Limiter) middlewareHandler {
154
156
}
155
157
}
156
158
159
+ func (a * API ) limitEmailSentHandler () middlewareHandler {
160
+ // limit per hour
161
+ freq := a .config .RateLimitEmailSent / (60 * 60 )
162
+ lmt := tollbooth .NewLimiter (freq , & limiter.ExpirableOptions {
163
+ DefaultExpirationTTL : time .Hour ,
164
+ }).SetBurst (int (a .config .RateLimitEmailSent )).SetMethods ([]string {"PUT" , "POST" })
165
+ return func (w http.ResponseWriter , req * http.Request ) (context.Context , error ) {
166
+ c := req .Context ()
167
+ config := a .getConfig (c )
168
+ if config .External .Email .Enabled && ! config .Mailer .Autoconfirm {
169
+ if req .Method == "PUT" || req .Method == "POST" {
170
+ res := make (map [string ]interface {})
171
+ bodyBytes , err := ioutil .ReadAll (req .Body )
172
+ if err != nil {
173
+ return c , internalServerError ("Error invalid request body" ).WithInternalError (err )
174
+ }
175
+ req .Body .Close ()
176
+ req .Body = ioutil .NopCloser (bytes .NewBuffer (bodyBytes ))
177
+
178
+ jsonDecoder := json .NewDecoder (bytes .NewBuffer (bodyBytes ))
179
+ if err := jsonDecoder .Decode (& res ); err != nil {
180
+ return c , badRequestError ("Error invalid request body" ).WithInternalError (err )
181
+ }
182
+
183
+ if _ , ok := res ["email" ]; ! ok {
184
+ // email not in POST body
185
+ return c , nil
186
+ }
187
+
188
+ if err := tollbooth .LimitByRequest (lmt , w , req ); err != nil {
189
+ return c , httpError (http .StatusTooManyRequests , "Rate limit exceeded" )
190
+ }
191
+ }
192
+ }
193
+ return c , nil
194
+ }
195
+ }
196
+
157
197
func (a * API ) requireAdminCredentials (w http.ResponseWriter , req * http.Request ) (context.Context , error ) {
158
198
ctx := req .Context ()
159
199
t , err := a .extractBearerToken (w , req )
0 commit comments