From dcd6ad3592317cfd37a2738d7be77df82876a330 Mon Sep 17 00:00:00 2001 From: Paul Schneider Date: Sun, 6 Sep 2026 19:47:38 +0100 Subject: [PATCH 1/2] handles duplicate email at register --- .../Accounting/AccountController.cs | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/src/Yavsc.Org/Controllers/Accounting/AccountController.cs b/src/Yavsc.Org/Controllers/Accounting/AccountController.cs index 77e0e32a4..44927d461 100644 --- a/src/Yavsc.Org/Controllers/Accounting/AccountController.cs +++ b/src/Yavsc.Org/Controllers/Accounting/AccountController.cs @@ -467,8 +467,25 @@ IHtmlLocalizerFactory htmlLocalizerFactory, if (ModelState.IsValid) { + var existingUser = await _userManager.FindByEmailAsync(model.Email); + if (existingUser is not null) + { + ModelState.AddModelError(nameof(model.Email), _localizer["DuplicateEmail"]); + return View(model); + } + var user = new ApplicationUser { UserName = model.UserName, Email = model.Email }; - var result = await _userManager.CreateAsync(user, model.Password); + IdentityResult result; + try + { + result = await _userManager.CreateAsync(user, model.Password); + } + catch (DbUpdateException ex) when (IsDuplicateEmailViolation(ex)) + { + _logger.LogWarning(ex, "Registration rejected: duplicate email '{Email}'.", model.Email); + ModelState.AddModelError(nameof(model.Email), _localizer["DuplicateEmail"]); + return View(model); + } if (result.Succeeded) { _logger.LogInformation(3, "User created a new account with password."); @@ -517,6 +534,21 @@ IHtmlLocalizerFactory htmlLocalizerFactory, return View(model); } + private static bool IsDuplicateEmailViolation(Exception exception) + { + for (var current = exception; current is not null; current = current.InnerException) + { + if (current is PostgresException pg + && pg.SqlState == PostgresErrorCodes.UniqueViolation + && string.Equals(pg.ConstraintName, "AK_AspNetUsers_Email", StringComparison.Ordinal)) + { + return true; + } + } + + return false; + } + [Authorize, HttpPost, ValidateAntiForgeryToken] public async Task SendConfirationEmail() { From c2574ac1d42cf6311feb673a31b6e6533d90fb0f Mon Sep 17 00:00:00 2001 From: Paul Schneider Date: Sun, 6 Sep 2026 19:48:51 +0100 Subject: [PATCH 2/2] correctif UTC sur le POST/PUT RDV --- .../RdvQueryApiControllerTests.cs | 31 +++++++++++++++++++ .../Business/RdvQueryApiController.cs | 13 +++++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/Yavsc.Api.Test/RdvQueryApiControllerTests.cs b/src/Yavsc.Api.Test/RdvQueryApiControllerTests.cs index fcd87f9a3..668814239 100644 --- a/src/Yavsc.Api.Test/RdvQueryApiControllerTests.cs +++ b/src/Yavsc.Api.Test/RdvQueryApiControllerTests.cs @@ -112,4 +112,35 @@ public sealed class RdvQueryApiControllerTests : IClassFixture(TestContext.Current.CancellationToken); + Assert.NotNull(created); + Assert.Equal(DateTimeKind.Utc, created!.EventDate.Kind); + } } diff --git a/src/Yavsc.Api/Controllers/Business/RdvQueryApiController.cs b/src/Yavsc.Api/Controllers/Business/RdvQueryApiController.cs index dd2982138..48e3be2e1 100644 --- a/src/Yavsc.Api/Controllers/Business/RdvQueryApiController.cs +++ b/src/Yavsc.Api/Controllers/Business/RdvQueryApiController.cs @@ -67,6 +67,7 @@ public class RdvQueryApiController : Controller var uid = User.GetUserId(); // Security: the caller always posts for themselves. query.ClientId = uid; + query.EventDate = EnsureUtc(query.EventDate); ModelState.Remove("Client"); ModelState.Remove("ClientId"); @@ -140,7 +141,7 @@ public class RdvQueryApiController : Controller existing.ActivityCode = query.ActivityCode; existing.PerformerId = query.PerformerId; existing.Consent = query.Consent; - existing.EventDate = query.EventDate; + existing.EventDate = EnsureUtc(query.EventDate); existing.LocationType = query.LocationType; existing.Reason = query.Reason; existing.Status = query.Status; @@ -206,4 +207,14 @@ public class RdvQueryApiController : Controller { return _context.RdvQueries.Any(e => e.Id == id); } + + private static DateTime EnsureUtc(DateTime value) + { + return value.Kind switch + { + DateTimeKind.Utc => value, + DateTimeKind.Local => value.ToUniversalTime(), + _ => DateTime.SpecifyKind(value, DateTimeKind.Utc) + }; + } }