|
13 | 13 | using Exceptionless.Core.Mail; |
14 | 14 | using Exceptionless.Core.Repositories; |
15 | 15 | using Exceptionless.Core.Models; |
| 16 | +using Exceptionless.DateTimeExtensions; |
16 | 17 | using FluentValidation; |
| 18 | +using Foundatio.Caching; |
17 | 19 | using Foundatio.Logging; |
18 | 20 |
|
19 | 21 | namespace Exceptionless.Api.Controllers { |
20 | 22 | [RoutePrefix(API_PREFIX + "/users")] |
21 | 23 | [Authorize(Roles = AuthorizationRoles.User)] |
22 | 24 | public class UserController : RepositoryApiController<IUserRepository, User, ViewUser, User, UpdateUser> { |
23 | 25 | private readonly IOrganizationRepository _organizationRepository; |
| 26 | + private readonly ICacheClient _cacheClient; |
24 | 27 | private readonly IMailer _mailer; |
25 | 28 |
|
26 | | - public UserController(IUserRepository userRepository, IOrganizationRepository organizationRepository, IMailer mailer) : base(userRepository) { |
| 29 | + public UserController(IUserRepository userRepository, IOrganizationRepository organizationRepository, ICacheClient cacheClient, IMailer mailer) : base(userRepository) { |
27 | 30 | _organizationRepository = organizationRepository; |
| 31 | + _cacheClient = new ScopedCacheClient(cacheClient, "user"); |
28 | 32 | _mailer = mailer; |
29 | 33 | } |
30 | 34 |
|
@@ -117,12 +121,18 @@ public async Task<IHttpActionResult> UpdateEmailAddressAsync(string id, string e |
117 | 121 | return NotFound(); |
118 | 122 |
|
119 | 123 | email = email.ToLower(); |
120 | | - if (!await IsEmailAddressAvailableInternalAsync(email)) |
121 | | - return BadRequest("A user with this email address already exists."); |
122 | | - |
123 | 124 | if (String.Equals(ExceptionlessUser.EmailAddress, email, StringComparison.OrdinalIgnoreCase)) |
124 | 125 | return Ok(new UpdateEmailAddressResult { IsVerified = user.IsEmailAddressVerified }); |
125 | 126 |
|
| 127 | + // Only allow 3 email address updates per hour period by a single user. |
| 128 | + string updateEmailAddressAttemptsCacheKey = $"{ExceptionlessUser.Id}:attempts"; |
| 129 | + long attempts = await _cacheClient.IncrementAsync(updateEmailAddressAttemptsCacheKey, 1, DateTime.UtcNow.Ceiling(TimeSpan.FromHours(1))); |
| 130 | + if (attempts > 3) |
| 131 | + return BadRequest("Update email address rate limit reached. Please try updating later."); |
| 132 | + |
| 133 | + if (!await IsEmailAddressAvailableInternalAsync(email)) |
| 134 | + return BadRequest("A user with this email address already exists."); |
| 135 | + |
126 | 136 | user.ResetPasswordResetToken(); |
127 | 137 | user.EmailAddress = email; |
128 | 138 | user.IsEmailAddressVerified = user.OAuthAccounts.Count(oa => String.Equals(oa.EmailAddress(), email, StringComparison.OrdinalIgnoreCase)) > 0; |
|
0 commit comments