From 9d05f7398cdea9d0f2ab88730085595603497b17 Mon Sep 17 00:00:00 2001 From: Irinel Nistor Date: Wed, 27 May 2020 17:10:50 +0300 Subject: [PATCH 01/13] Add delete user identity server endpoint and delete profile and forms api endpoint --- .../Controllers/ProfileController.cs | 19 +++- .../StamAcasa.Common/Services/IUserService.cs | 1 + .../StamAcasa.Common/Services/UserService.cs | 34 ++++++ .../Account/DeleteAccountController.cs | 105 ++++++++++++++++++ .../Quickstart/Account/DeleteAccountModel.cs | 14 +++ .../src/StamAcasa.IdentityServer/Startup.cs | 3 + .../StamAcasa.IdentityServer/appsettings.json | 1 + 7 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 backend/src/StamAcasa.IdentityServer/Quickstart/Account/DeleteAccountController.cs create mode 100644 backend/src/StamAcasa.IdentityServer/Quickstart/Account/DeleteAccountModel.cs diff --git a/backend/src/StamAcasa.Api/Controllers/ProfileController.cs b/backend/src/StamAcasa.Api/Controllers/ProfileController.cs index a5721e9c..dca1b2de 100644 --- a/backend/src/StamAcasa.Api/Controllers/ProfileController.cs +++ b/backend/src/StamAcasa.Api/Controllers/ProfileController.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; @@ -165,5 +166,21 @@ public async Task UpdateFamilyProfile(int id, [FromBody] UserProf return new OkObjectResult(result != null); } + + /// + /// Delete user profile and related data. + /// + /// + [HttpDelete] + public async Task DeleteProfile() + { + var sub = User.Claims.FirstOrDefault(c => c.Type == "sub")?.Value; + if (sub == null) + return new UnauthorizedResult(); + + await _userService.DeleteUserAndDependentData(sub); + + return Ok(); + } } } \ No newline at end of file diff --git a/backend/src/StamAcasa.Common/Services/IUserService.cs b/backend/src/StamAcasa.Common/Services/IUserService.cs index e4623b2b..c2e6334c 100644 --- a/backend/src/StamAcasa.Common/Services/IUserService.cs +++ b/backend/src/StamAcasa.Common/Services/IUserService.cs @@ -12,5 +12,6 @@ public interface IUserService Task> GetDependentInfo(string sub); Task> GetAll(); Task> GetAllParents(); + Task DeleteUserAndDependentData(string sub); } } diff --git a/backend/src/StamAcasa.Common/Services/UserService.cs b/backend/src/StamAcasa.Common/Services/UserService.cs index cf4754ad..232c383b 100644 --- a/backend/src/StamAcasa.Common/Services/UserService.cs +++ b/backend/src/StamAcasa.Common/Services/UserService.cs @@ -107,5 +107,39 @@ await _context.Users.ForEachAsync(u => var result = parents.Select(_mapper.Map); return result; } + + public async Task DeleteUserAndDependentData(string sub) + { + var user = await _context.Users + .Include(u => u.DependentUsers) + .FirstOrDefaultAsync(u => u.Sub == sub); + if (user == null) + { + return; + } + + var strategy = _context.Database.CreateExecutionStrategy(); + await strategy.ExecuteAsync(async () => + { + using (var transaction = _context.Database.BeginTransaction()) + { + await DeleteUserAndForms(user); + + foreach (var familyMember in user.DependentUsers) + { + await DeleteUserAndForms(familyMember); + } + + await _context.SaveChangesAsync(); + transaction.Commit(); + } + }); + } + + private async Task DeleteUserAndForms(User user) + { + await _context.Database.ExecuteSqlRawAsync("DELETE FROM \"Forms\" WHERE \"UserId\" = {0}", new object[] { user.Id }); + _context.Remove(user); + } } } diff --git a/backend/src/StamAcasa.IdentityServer/Quickstart/Account/DeleteAccountController.cs b/backend/src/StamAcasa.IdentityServer/Quickstart/Account/DeleteAccountController.cs new file mode 100644 index 00000000..f296a16e --- /dev/null +++ b/backend/src/StamAcasa.IdentityServer/Quickstart/Account/DeleteAccountController.cs @@ -0,0 +1,105 @@ +using System; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; +using IdentityServer.Data; +using IdentityServer4; +using IdentityServer4.Models; +using IdentityServer4.Services; +using IdentityServer4.Validation; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; + +namespace StamAcasa.IdentityServer.Quickstart.Account +{ + [Route("api/[controller]")] + [ApiController] + [AllowAnonymous] + public class DeleteAccountController : ControllerBase + { + private readonly UserManager _userManager; + private readonly DefaultTokenService _tokenService; + private readonly IStamAcasaIdentityConfiguration _identityConfiguration; + private readonly IHttpClientFactory _clientFactory; + private readonly string _apiUrl; + + public DeleteAccountController(UserManager userManager, DefaultTokenService tokenService, IStamAcasaIdentityConfiguration identityConfiguration, IHttpClientFactory clientFactory, IConfiguration configuration) + { + _userManager = userManager; + _tokenService = tokenService; + _identityConfiguration = identityConfiguration; + _clientFactory = clientFactory; + _apiUrl = configuration["StamAcasaApi"]; + } + + [HttpPost] + public async Task DeleteAccountAsync([FromBody] DeleteAccountModel model) + { + var user = await _userManager.FindByNameAsync(model.Username); + if (user == null) + { + return NotFound($"Could not find user with id {model.Username}"); + } + + if (!await _userManager.CheckPasswordAsync(user, model.Password)) + { + return new UnauthorizedResult(); + } + + string tokenValue = await CreateAccessToken(user); + var deleteResponse = await DeleteApiUserDataAsync(tokenValue); + if (!deleteResponse.IsSuccessStatusCode) + { + return StatusCode(StatusCodes.Status500InternalServerError, new { message = "Unexpected error occurred deleting user data" }); + } + + var response = await _userManager.DeleteAsync(user); + if (!response.Succeeded) + { + return StatusCode(StatusCodes.Status500InternalServerError, $"Unexpected error occurred deleting user with ID '{user.Id}'."); + } + + return Ok(); + } + + private async Task DeleteApiUserDataAsync(string tokenValue) + { + var client = _clientFactory.CreateClient(); + + var request = new HttpRequestMessage(HttpMethod.Delete, + $"{_apiUrl}/api/Profile"); + request.Headers.Add("Authorization", $"Bearer {tokenValue}"); + + return await client.SendAsync(request); + } + + private async Task CreateAccessToken(ApplicationUser user) + { + var IdentityUser = new IdentityServerUser(user.Id) + { + IdentityProvider = IdentityServerConstants.LocalIdentityProvider, + AuthenticationTime = DateTime.UtcNow, + }; + + var request = new TokenCreationRequest + { + Subject = IdentityUser.CreatePrincipal(), + IncludeAllIdentityClaims = true, + Resources = new Resources(_identityConfiguration.Ids, _identityConfiguration.Apis()) + }; + + request.ValidatedRequest = new ValidatedRequest + { + Subject = request.Subject + }; + request.ValidatedRequest.SetClient(_identityConfiguration.Clients.FirstOrDefault()); + + var accesToken = await _tokenService.CreateAccessTokenAsync(request); + var token = await _tokenService.CreateSecurityTokenAsync(accesToken); + return token; + } + } +} \ No newline at end of file diff --git a/backend/src/StamAcasa.IdentityServer/Quickstart/Account/DeleteAccountModel.cs b/backend/src/StamAcasa.IdentityServer/Quickstart/Account/DeleteAccountModel.cs new file mode 100644 index 00000000..f7febb7e --- /dev/null +++ b/backend/src/StamAcasa.IdentityServer/Quickstart/Account/DeleteAccountModel.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace StamAcasa.IdentityServer.Quickstart.Account +{ + public class DeleteAccountModel + { + public string Username { get; set; } + + public string Password { get; set; } + } +} diff --git a/backend/src/StamAcasa.IdentityServer/Startup.cs b/backend/src/StamAcasa.IdentityServer/Startup.cs index dfe1da48..b7ff4267 100644 --- a/backend/src/StamAcasa.IdentityServer/Startup.cs +++ b/backend/src/StamAcasa.IdentityServer/Startup.cs @@ -10,6 +10,7 @@ using StamAcasa.IdentityServer; using StamAcasa.Common.Queue; using EasyNetQ; +using IdentityServer4.Services; namespace IdentityServer { @@ -92,6 +93,8 @@ public void ConfigureServices(IServiceCollection services) Configuration.GetValue("RabbitMQ:User"), Configuration.GetValue("RabbitMQ:Password")) )); + services.AddScoped(); + services.AddSingleton(_identityConfiguration); services.AddSingleton(); services.AddSingleton(); } diff --git a/backend/src/StamAcasa.IdentityServer/appsettings.json b/backend/src/StamAcasa.IdentityServer/appsettings.json index 0826317c..3da3a36e 100644 --- a/backend/src/StamAcasa.IdentityServer/appsettings.json +++ b/backend/src/StamAcasa.IdentityServer/appsettings.json @@ -148,6 +148,7 @@ "Password": "password" }, "IdentityServerPublicOrigin": "", + "StamAcasaApi": "http://stamacasa.api", "Certificate": { "Base64Encoded": "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", "Password": "1234" From c93d3420dbb8f383878d40c6de89449af675b46e Mon Sep 17 00:00:00 2001 From: Irinel Nistor Date: Thu, 28 May 2020 13:28:10 +0300 Subject: [PATCH 02/13] Add adminApi policy to the delete endpoint --- .../Controllers/ProfileAdminController.cs | 38 +++++++++++++++++++ .../Controllers/ProfileController.cs | 16 -------- backend/src/StamAcasa.Api/appsettings.json | 8 +++- .../Account/DeleteAccountController.cs | 12 ++---- .../StamAcasa.IdentityServer/appsettings.json | 20 ++++++++++ 5 files changed, 68 insertions(+), 26 deletions(-) create mode 100644 backend/src/StamAcasa.Api/Controllers/ProfileAdminController.cs diff --git a/backend/src/StamAcasa.Api/Controllers/ProfileAdminController.cs b/backend/src/StamAcasa.Api/Controllers/ProfileAdminController.cs new file mode 100644 index 00000000..0cd4d357 --- /dev/null +++ b/backend/src/StamAcasa.Api/Controllers/ProfileAdminController.cs @@ -0,0 +1,38 @@ +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using StamAcasa.Common.Services; + +namespace StamAcasa.Api.Controllers +{ + [Route("api/profile")] + [Authorize(AuthenticationSchemes = "adminApi")] + [ApiExplorerSettings(IgnoreApi = true)] + [ApiController] + public class ProfileAdminController : ControllerBase + { + private readonly IUserService _userService; + + public ProfileAdminController(IUserService userService) + { + _userService = userService; + } + + /// + /// Delete user profile and related data. + /// + /// + [HttpDelete] + public async Task DeleteProfile() + { + var sub = User.Claims.FirstOrDefault(c => c.Type == "sub")?.Value; + if (sub == null) + return new UnauthorizedResult(); + + await _userService.DeleteUserAndDependentData(sub); + + return Ok(); + } + } +} \ No newline at end of file diff --git a/backend/src/StamAcasa.Api/Controllers/ProfileController.cs b/backend/src/StamAcasa.Api/Controllers/ProfileController.cs index dca1b2de..67d09dc3 100644 --- a/backend/src/StamAcasa.Api/Controllers/ProfileController.cs +++ b/backend/src/StamAcasa.Api/Controllers/ProfileController.cs @@ -166,21 +166,5 @@ public async Task UpdateFamilyProfile(int id, [FromBody] UserProf return new OkObjectResult(result != null); } - - /// - /// Delete user profile and related data. - /// - /// - [HttpDelete] - public async Task DeleteProfile() - { - var sub = User.Claims.FirstOrDefault(c => c.Type == "sub")?.Value; - if (sub == null) - return new UnauthorizedResult(); - - await _userService.DeleteUserAndDependentData(sub); - - return Ok(); - } } } \ No newline at end of file diff --git a/backend/src/StamAcasa.Api/appsettings.json b/backend/src/StamAcasa.Api/appsettings.json index 12967660..fd92998e 100644 --- a/backend/src/StamAcasa.Api/appsettings.json +++ b/backend/src/StamAcasa.Api/appsettings.json @@ -19,8 +19,12 @@ "ApiSecret": "svpqYnJSR8xzn8Rl" }, { - "ApiName": "usersApi", - "ApiSecret": "st4k!b7s$af201cv" + "ApiName": "usersApi", + "ApiSecret": "st4k!b7s$af201cv" + }, + { + "ApiName": "adminApi", + "ApiSecret": "3rXGcXpcPKxAIshJ" } ], "TargetFolder": "answers", diff --git a/backend/src/StamAcasa.IdentityServer/Quickstart/Account/DeleteAccountController.cs b/backend/src/StamAcasa.IdentityServer/Quickstart/Account/DeleteAccountController.cs index f296a16e..6871278c 100644 --- a/backend/src/StamAcasa.IdentityServer/Quickstart/Account/DeleteAccountController.cs +++ b/backend/src/StamAcasa.IdentityServer/Quickstart/Account/DeleteAccountController.cs @@ -25,6 +25,7 @@ public class DeleteAccountController : ControllerBase private readonly IStamAcasaIdentityConfiguration _identityConfiguration; private readonly IHttpClientFactory _clientFactory; private readonly string _apiUrl; + private const string IdsrvClientId = "idsrvClient"; public DeleteAccountController(UserManager userManager, DefaultTokenService tokenService, IStamAcasaIdentityConfiguration identityConfiguration, IHttpClientFactory clientFactory, IConfiguration configuration) { @@ -39,12 +40,7 @@ public DeleteAccountController(UserManager userManager, Default public async Task DeleteAccountAsync([FromBody] DeleteAccountModel model) { var user = await _userManager.FindByNameAsync(model.Username); - if (user == null) - { - return NotFound($"Could not find user with id {model.Username}"); - } - - if (!await _userManager.CheckPasswordAsync(user, model.Password)) + if (user == null || !await _userManager.CheckPasswordAsync(user, model.Password)) { return new UnauthorizedResult(); } @@ -84,7 +80,7 @@ private async Task CreateAccessToken(ApplicationUser user) AuthenticationTime = DateTime.UtcNow, }; - var request = new TokenCreationRequest + var request = new TokenCreationRequest { Subject = IdentityUser.CreatePrincipal(), IncludeAllIdentityClaims = true, @@ -95,7 +91,7 @@ private async Task CreateAccessToken(ApplicationUser user) { Subject = request.Subject }; - request.ValidatedRequest.SetClient(_identityConfiguration.Clients.FirstOrDefault()); + request.ValidatedRequest.SetClient(_identityConfiguration.Clients.FirstOrDefault(c => c.ClientId == IdsrvClientId)); var accesToken = await _tokenService.CreateAccessTokenAsync(request); var token = await _tokenService.CreateSecurityTokenAsync(accesToken); diff --git a/backend/src/StamAcasa.IdentityServer/appsettings.json b/backend/src/StamAcasa.IdentityServer/appsettings.json index 3da3a36e..393744d0 100644 --- a/backend/src/StamAcasa.IdentityServer/appsettings.json +++ b/backend/src/StamAcasa.IdentityServer/appsettings.json @@ -20,6 +20,11 @@ "Name": "usersApi", "Secret": "st4k!b7s$af201cv", "ClaimList": [ "openid", "email" ] + }, + { + "Name": "adminApi", + "Secret": "3rXGcXpcPKxAIshJ", + "ClaimList": [ "openid" ] } ], "ClientApplications": [ @@ -125,6 +130,21 @@ ], "AllowAccessTokensViaBrowser": true, "AccessTokenType": 1 + }, + { + "ClientId": "idsrvClient", + "ClientSecrets": [ + { + "Value": "HcBFXeMlI/5iUCtyLErjz4rJ66u2Bdk+ytqj0aELxhw=" + } + ], + "ClientName": "Identiy Server Client", + "AllowedGrantTypes": [ "client_credentials" ], + "AllowedScopes": [ + "openid", + "adminApi" + ], + "AccessTokenType": 1 } ], "EnableEmailConfirmation": true, From 3f4238a43e180f4178f47e5eeeaeb6a84c933cb7 Mon Sep 17 00:00:00 2001 From: Irinel Nistor Date: Wed, 3 Jun 2020 21:23:35 +0300 Subject: [PATCH 03/13] Delete only the IDP user --- .../Controllers/ProfileAdminController.cs | 38 ------------- .../Account/DeleteAccountController.cs | 55 +------------------ .../StamAcasa.IdentityServer/appsettings.json | 1 - 3 files changed, 1 insertion(+), 93 deletions(-) delete mode 100644 backend/src/StamAcasa.Api/Controllers/ProfileAdminController.cs diff --git a/backend/src/StamAcasa.Api/Controllers/ProfileAdminController.cs b/backend/src/StamAcasa.Api/Controllers/ProfileAdminController.cs deleted file mode 100644 index 0cd4d357..00000000 --- a/backend/src/StamAcasa.Api/Controllers/ProfileAdminController.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using StamAcasa.Common.Services; - -namespace StamAcasa.Api.Controllers -{ - [Route("api/profile")] - [Authorize(AuthenticationSchemes = "adminApi")] - [ApiExplorerSettings(IgnoreApi = true)] - [ApiController] - public class ProfileAdminController : ControllerBase - { - private readonly IUserService _userService; - - public ProfileAdminController(IUserService userService) - { - _userService = userService; - } - - /// - /// Delete user profile and related data. - /// - /// - [HttpDelete] - public async Task DeleteProfile() - { - var sub = User.Claims.FirstOrDefault(c => c.Type == "sub")?.Value; - if (sub == null) - return new UnauthorizedResult(); - - await _userService.DeleteUserAndDependentData(sub); - - return Ok(); - } - } -} \ No newline at end of file diff --git a/backend/src/StamAcasa.IdentityServer/Quickstart/Account/DeleteAccountController.cs b/backend/src/StamAcasa.IdentityServer/Quickstart/Account/DeleteAccountController.cs index 6871278c..38e48dab 100644 --- a/backend/src/StamAcasa.IdentityServer/Quickstart/Account/DeleteAccountController.cs +++ b/backend/src/StamAcasa.IdentityServer/Quickstart/Account/DeleteAccountController.cs @@ -21,19 +21,10 @@ namespace StamAcasa.IdentityServer.Quickstart.Account public class DeleteAccountController : ControllerBase { private readonly UserManager _userManager; - private readonly DefaultTokenService _tokenService; - private readonly IStamAcasaIdentityConfiguration _identityConfiguration; - private readonly IHttpClientFactory _clientFactory; - private readonly string _apiUrl; - private const string IdsrvClientId = "idsrvClient"; - public DeleteAccountController(UserManager userManager, DefaultTokenService tokenService, IStamAcasaIdentityConfiguration identityConfiguration, IHttpClientFactory clientFactory, IConfiguration configuration) + public DeleteAccountController(UserManager userManager) { _userManager = userManager; - _tokenService = tokenService; - _identityConfiguration = identityConfiguration; - _clientFactory = clientFactory; - _apiUrl = configuration["StamAcasaApi"]; } [HttpPost] @@ -45,13 +36,6 @@ public async Task DeleteAccountAsync([FromBody] DeleteAccountMode return new UnauthorizedResult(); } - string tokenValue = await CreateAccessToken(user); - var deleteResponse = await DeleteApiUserDataAsync(tokenValue); - if (!deleteResponse.IsSuccessStatusCode) - { - return StatusCode(StatusCodes.Status500InternalServerError, new { message = "Unexpected error occurred deleting user data" }); - } - var response = await _userManager.DeleteAsync(user); if (!response.Succeeded) { @@ -60,42 +44,5 @@ public async Task DeleteAccountAsync([FromBody] DeleteAccountMode return Ok(); } - - private async Task DeleteApiUserDataAsync(string tokenValue) - { - var client = _clientFactory.CreateClient(); - - var request = new HttpRequestMessage(HttpMethod.Delete, - $"{_apiUrl}/api/Profile"); - request.Headers.Add("Authorization", $"Bearer {tokenValue}"); - - return await client.SendAsync(request); - } - - private async Task CreateAccessToken(ApplicationUser user) - { - var IdentityUser = new IdentityServerUser(user.Id) - { - IdentityProvider = IdentityServerConstants.LocalIdentityProvider, - AuthenticationTime = DateTime.UtcNow, - }; - - var request = new TokenCreationRequest - { - Subject = IdentityUser.CreatePrincipal(), - IncludeAllIdentityClaims = true, - Resources = new Resources(_identityConfiguration.Ids, _identityConfiguration.Apis()) - }; - - request.ValidatedRequest = new ValidatedRequest - { - Subject = request.Subject - }; - request.ValidatedRequest.SetClient(_identityConfiguration.Clients.FirstOrDefault(c => c.ClientId == IdsrvClientId)); - - var accesToken = await _tokenService.CreateAccessTokenAsync(request); - var token = await _tokenService.CreateSecurityTokenAsync(accesToken); - return token; - } } } \ No newline at end of file diff --git a/backend/src/StamAcasa.IdentityServer/appsettings.json b/backend/src/StamAcasa.IdentityServer/appsettings.json index 393744d0..2f39fb92 100644 --- a/backend/src/StamAcasa.IdentityServer/appsettings.json +++ b/backend/src/StamAcasa.IdentityServer/appsettings.json @@ -168,7 +168,6 @@ "Password": "password" }, "IdentityServerPublicOrigin": "", - "StamAcasaApi": "http://stamacasa.api", "Certificate": { "Base64Encoded": "MIIJogIBAzCCCV4GCSqGSIb3DQEHAaCCCU8EgglLMIIJRzCCBZAGCSqGSIb3DQEHAaCCBYEEggV9MIIFeTCCBXUGCyqGSIb3DQEMCgECoIIE7jCCBOowHAYKKoZIhvcNAQwBAzAOBAhUCyLL72MyGAICB9AEggTIYfvlCFuecdAnt7GTjS17thKoRHXW7FDrFt+15GvuR0hNe9u5bEk7LljmQVSIgZgDFiotqUOSQzeDyyEGHvA8xyUkSRNvfnRYEMIGQ/Y5JOzqEKf5azXedptUbjAlsz+meT1CL679GqWU7eXGhpXen0eRJCjQEdDZrPmi/sYJfMfdn5Zeh5PVnBEFJIxhZaaTU1NNZna5HYq6TfNIsvnEiTdoLuLQhAHps3fYRRoLkCJjyhyOyyyOjj5zM4x26fVXm4jBcx/TCE9FxaKruzryXMEExXr1sCOcBpVqSf0Q45tdXm9i7m8ndPzmZXHUCsAv4LgJh5l9zns1j6gIEYiPDlMZ4sYO+rmaJrhaRTVeIN/wDTbVGBOth3A5zuMJcID05KhEKYO7kJQqUDG66QqqTvAU8g2YWNQ+Vl/bNVzJA6uUyYFqDBlrhMcVq5EHh+0r86RmdaTGOqPWu8LsmJUFY2Txf2FvcFMBw21cclTxE6gTizMaW56gB9uozB/sQ7BbJac+fhOs8tEbWL7xrwLkkEfe+6Ub/Xa54BjWC7+wqS2u4NpW2Wf6Hmyql076Azc7/ZxIvdoTpDhum2V0uWibl/TodlQ2z1UPxSy9hetWvogdEaNw4RQNjYnI4iM252GoJfPrmNo2G4vgqmIg1tQGcXvrgxUKC+V60x2uHs82w6zUgaSkK6hTACxN/yHhClMGdHOfqJ54khWhy63g2cif0CiqWjP5zfy4AFIZeQgAGiRhpg47p5Zdvltbp9JY3lfi+3N3l5WR911Re3NPG7UYqp/kpl5IjZZ379lyTSApYENoltQW6u9ecEvGGCEiA6b4F2+GxmtqG2zSeK7AsD4uYBrcy/Gz+J/uzC4ng8Bchl8jPew5zdZpj+3YUYotd9X0Sj6fo1ug7XvbWK6sAzGkyN0cYjq+AbMPxK1RhmiSVkBiSX9c5T1J7Jvf4UdY6/TKcJTIwOgLHYLj2oY8+7gFfgwMqBoeV2gqE241zO3yfE8u9AXDoKAtEJG0AVoO3V6GNxtlai5H/IxaJQ0W1mCL/6o+lqU0DKp0ZocV1PJ6vZi+ANy2q0mqHKlRm8qPxV3EYLVRLQhIFvB9ajawYJM4tNlJRuxDN/pXmWWTupnvsy9W00gDtRqzihxKCvi/fXwDmFPCuGkbh55m+jWDXU/As3z4lJgl566NPcBjc8P+5rrkoJ0ZqTu8bXV9fNtMMzyncxzXFNtsyflYusUv7wXtL9GdrihopQz0LdiU7U7kHNGP5ofPidSbjp+BGY4DmhhJj04htLai3CNg2F0xgOZX7l7wSYaFFm6n2saiKNSdRPtubj8MwRceCz6zJYZaPsmWQWXdYf3fYqvTetMBFwV9ddtlzynFQBThUlSw/3EfU6GPVtD+/JpwyMB1NEcau34aLrbLAzuNZjjYdJhsOK6ZwsklfnHlQpOxZuDRcSwZICiwHBON6KNp2ETeZg5IG/82TpGEI9mE7gv51F64v3iSLKslGAqQxdl1Tii0JK89KbkhGuyAPx05p/j0Lz8XxXms++ijMnFc+CGKoKlRBi7iqVB+S7VhvSBeipBPm1JcaBUAy1t0Y0HA6WDDUzkwUVCwh6Ig+oA61IbKVe2+OuHTYFLnjCMCaVk8MXQwEwYJKoZIhvcNAQkVMQYEBAEAAAAwXQYJKwYBBAGCNxEBMVAeTgBNAGkAYwByAG8AcwBvAGYAdAAgAFMAbwBmAHQAdwBhAHIAZQAgAEsAZQB5ACAAUwB0AG8AcgBhAGcAZQAgAFAAcgBvAHYAaQBkAGUAcjCCA68GCSqGSIb3DQEHBqCCA6AwggOcAgEAMIIDlQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQMwDgQIhAZcgGVU8n4CAgfQgIIDaOwG5OKTDQd+sjLPjS/zh+OO7w5RTmkgWaEXbt3lrr83AL/boMqjHoiVgUbHOS4HrgG4wL/rugN1P/8TI8fY08Qo7YaE5SZoUqmL2cNvS9BEFIi2aoeAjAK0JhxMi8nDbahcCuq5gPsW+oXhH/W7BzLcDH5Z4R1qLMj9r0i4xgMyMqEWu7gFAZJ9nWEZzHMLQv68/KXxAlWX2CTwHnt4dSD8HpEjIWumSXnc6Jq5216XTNHT4hj/1xBdmiUj4ObSgsqDvt4R7FNC2HCD2V+JqeMWn7GCl+EDEeJiv2Jhr+EQucyzKObiCAWGMBs0tMX6khjGFCrJIi3lzq74QPiCIn9vcJ3l8U2Fxs9SnpstC7Q0iJ2qlBdExK7rK181pG3Uk3u+UI5AQxy7eVLbLXvtxatGz+9+o5IJ8lQgr9iAgMD7hi6pXv5nonCjmJnvd5+torkeXy2QPgVNgH05JJLNzsHcejqq0G+XCcAPCt97H6L/ItTbAWkQaKot5D55cUU/zUTpubl74bQZtjsjpQlWcfeO82nE/Do7f9r5d4OWt0QkaRuhvifinHNR4oQIx4UogGCW5Nr7XunPfDHRYhN3KwTdQEQyQnWGQOySQbd0JbWS8RCMLmqUk29gjNSgebKRle0NasjAZxbJyhAKh8v0QAlLEGJoM3QHc0jV4enqlIa5TrAfyLiixq/WqDoKj6klwaV9hoZ8WxH86B+Iw2Wc5tDgHEOMtUqrBRNnC0MEPYfqEWTSUOeQp8OyjSBfJU96sGU6BZgKo3h1ZGXwGmEfygcmC/Q+1p6MqgCz8NPFWi9C9tyxDkvQbd24mx6/1AzmmC2PS8poA1mR8f5yBzTQdLjR8YFkhHCpBG6baYHtvD6C/srWGlw5FgvKEj4f0SSeHM1RDIYINy5f0SO8ppZ/JKFQYQbOunHmpxkZ7o4d9yeC0QVkb1f07Bh1iRcRaacELyCyZVlr4f0ziYZG1NdUZ6iDW/UWf8/eiOfHCLCCqNVyOzHPCWImyIJ/g1Yb1dVqMA9254TzYmDKEDs9tliHk8muoe/4OLNE5b25gHtdC5QaEddVtMQKkkKU69eqTPfY3RjBd9MfQHF3UmiL/ZXhxohe0aef7cfcl5tI/sZoTwrvIDH7k0Qlsu076z4vvpnsU4RVOzSw5YXDMDswHzAHBgUrDgMCGgQUbYq3b147sxTENvGb53CVxiD6JO4EFBf8DBxW8YtsvzJqW2hDphVsIZ/lAgIH0A==", "Password": "1234" From 9173bb3f193513bd26828258d0af02f522022d5a Mon Sep 17 00:00:00 2001 From: Irinel Nistor Date: Wed, 3 Jun 2020 21:29:33 +0300 Subject: [PATCH 04/13] Revert user service changes --- .../StamAcasa.Common/Services/IUserService.cs | 1 - .../StamAcasa.Common/Services/UserService.cs | 34 ------------------- .../src/StamAcasa.IdentityServer/Startup.cs | 3 -- .../StamAcasa.IdentityServer/appsettings.json | 20 ----------- 4 files changed, 58 deletions(-) diff --git a/backend/src/StamAcasa.Common/Services/IUserService.cs b/backend/src/StamAcasa.Common/Services/IUserService.cs index c2e6334c..e4623b2b 100644 --- a/backend/src/StamAcasa.Common/Services/IUserService.cs +++ b/backend/src/StamAcasa.Common/Services/IUserService.cs @@ -12,6 +12,5 @@ public interface IUserService Task> GetDependentInfo(string sub); Task> GetAll(); Task> GetAllParents(); - Task DeleteUserAndDependentData(string sub); } } diff --git a/backend/src/StamAcasa.Common/Services/UserService.cs b/backend/src/StamAcasa.Common/Services/UserService.cs index 232c383b..cf4754ad 100644 --- a/backend/src/StamAcasa.Common/Services/UserService.cs +++ b/backend/src/StamAcasa.Common/Services/UserService.cs @@ -107,39 +107,5 @@ await _context.Users.ForEachAsync(u => var result = parents.Select(_mapper.Map); return result; } - - public async Task DeleteUserAndDependentData(string sub) - { - var user = await _context.Users - .Include(u => u.DependentUsers) - .FirstOrDefaultAsync(u => u.Sub == sub); - if (user == null) - { - return; - } - - var strategy = _context.Database.CreateExecutionStrategy(); - await strategy.ExecuteAsync(async () => - { - using (var transaction = _context.Database.BeginTransaction()) - { - await DeleteUserAndForms(user); - - foreach (var familyMember in user.DependentUsers) - { - await DeleteUserAndForms(familyMember); - } - - await _context.SaveChangesAsync(); - transaction.Commit(); - } - }); - } - - private async Task DeleteUserAndForms(User user) - { - await _context.Database.ExecuteSqlRawAsync("DELETE FROM \"Forms\" WHERE \"UserId\" = {0}", new object[] { user.Id }); - _context.Remove(user); - } } } diff --git a/backend/src/StamAcasa.IdentityServer/Startup.cs b/backend/src/StamAcasa.IdentityServer/Startup.cs index b7ff4267..dfe1da48 100644 --- a/backend/src/StamAcasa.IdentityServer/Startup.cs +++ b/backend/src/StamAcasa.IdentityServer/Startup.cs @@ -10,7 +10,6 @@ using StamAcasa.IdentityServer; using StamAcasa.Common.Queue; using EasyNetQ; -using IdentityServer4.Services; namespace IdentityServer { @@ -93,8 +92,6 @@ public void ConfigureServices(IServiceCollection services) Configuration.GetValue("RabbitMQ:User"), Configuration.GetValue("RabbitMQ:Password")) )); - services.AddScoped(); - services.AddSingleton(_identityConfiguration); services.AddSingleton(); services.AddSingleton(); } diff --git a/backend/src/StamAcasa.IdentityServer/appsettings.json b/backend/src/StamAcasa.IdentityServer/appsettings.json index 2f39fb92..0826317c 100644 --- a/backend/src/StamAcasa.IdentityServer/appsettings.json +++ b/backend/src/StamAcasa.IdentityServer/appsettings.json @@ -20,11 +20,6 @@ "Name": "usersApi", "Secret": "st4k!b7s$af201cv", "ClaimList": [ "openid", "email" ] - }, - { - "Name": "adminApi", - "Secret": "3rXGcXpcPKxAIshJ", - "ClaimList": [ "openid" ] } ], "ClientApplications": [ @@ -130,21 +125,6 @@ ], "AllowAccessTokensViaBrowser": true, "AccessTokenType": 1 - }, - { - "ClientId": "idsrvClient", - "ClientSecrets": [ - { - "Value": "HcBFXeMlI/5iUCtyLErjz4rJ66u2Bdk+ytqj0aELxhw=" - } - ], - "ClientName": "Identiy Server Client", - "AllowedGrantTypes": [ "client_credentials" ], - "AllowedScopes": [ - "openid", - "adminApi" - ], - "AccessTokenType": 1 } ], "EnableEmailConfirmation": true, From 99c558819a47965992e1f772f46444f84a5764c6 Mon Sep 17 00:00:00 2001 From: Irinel Nistor Date: Wed, 3 Jun 2020 21:36:56 +0300 Subject: [PATCH 05/13] revert config --- backend/src/StamAcasa.Api/Controllers/ProfileController.cs | 3 +-- backend/src/StamAcasa.Api/appsettings.json | 4 ---- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/backend/src/StamAcasa.Api/Controllers/ProfileController.cs b/backend/src/StamAcasa.Api/Controllers/ProfileController.cs index 67d09dc3..a5721e9c 100644 --- a/backend/src/StamAcasa.Api/Controllers/ProfileController.cs +++ b/backend/src/StamAcasa.Api/Controllers/ProfileController.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; diff --git a/backend/src/StamAcasa.Api/appsettings.json b/backend/src/StamAcasa.Api/appsettings.json index fd92998e..68cc5cde 100644 --- a/backend/src/StamAcasa.Api/appsettings.json +++ b/backend/src/StamAcasa.Api/appsettings.json @@ -21,10 +21,6 @@ { "ApiName": "usersApi", "ApiSecret": "st4k!b7s$af201cv" - }, - { - "ApiName": "adminApi", - "ApiSecret": "3rXGcXpcPKxAIshJ" } ], "TargetFolder": "answers", From 26d8ca093b44ab39a08dd1f24c7b8c0b1f89cf3f Mon Sep 17 00:00:00 2001 From: Mircea M Date: Sat, 27 Jun 2020 13:53:53 +0100 Subject: [PATCH 06/13] Adding delete account --- frontend/src/api/accountApi.js | 8 ++ frontend/src/api/auth.js | 2 + .../src/components/DeleteAccount/index.js | 83 +++++++++++++++++++ .../src/components/DeleteAccount/style.scss | 3 + .../src/components/Header/ProfileItems.js | 1 + frontend/src/routes.js | 5 ++ frontend/src/styles/base.scss | 2 + 7 files changed, 104 insertions(+) create mode 100644 frontend/src/api/accountApi.js create mode 100644 frontend/src/components/DeleteAccount/index.js create mode 100644 frontend/src/components/DeleteAccount/style.scss diff --git a/frontend/src/api/accountApi.js b/frontend/src/api/accountApi.js new file mode 100644 index 00000000..16dfc032 --- /dev/null +++ b/frontend/src/api/accountApi.js @@ -0,0 +1,8 @@ +import api from "./api"; + +const AccountApi = { + deleteAccount: (user, password) => + api.post("/DeleteAccount", { Username: user, Password: password }) +}; + +export default AccountApi; diff --git a/frontend/src/api/auth.js b/frontend/src/api/auth.js index d1baef87..814cb19a 100644 --- a/frontend/src/api/auth.js +++ b/frontend/src/api/auth.js @@ -56,3 +56,5 @@ export const getUserToken = async () => { } return user.access_token; }; + +export const removeUser = () => userManager.removeUser(); diff --git a/frontend/src/components/DeleteAccount/index.js b/frontend/src/components/DeleteAccount/index.js new file mode 100644 index 00000000..e2471453 --- /dev/null +++ b/frontend/src/components/DeleteAccount/index.js @@ -0,0 +1,83 @@ +import React, { useState } from "react"; +import PropTypes from "prop-types"; +import SidebarLayout from "../SidebarLayout"; +import AccountApi from "../../api/accountApi"; +import "./style.scss"; +import { removeUser } from "../../api/auth"; +const DeleteAccount = () => { + const [user, setUser] = useState(); + const [password, setPassword] = useState(); + const [loading, setLoading] = useState(false); + const [errorDeleting, setErrorDeleting] = useState(false); + + const fieldsFilled = user && password; + + const updateUser = event => setUser(event.target.value); + const updatePassword = event => setPassword(event.target.value); + + const deleteProfile = () => { + setLoading(true); + AccountApi.deleteAccount(user, password) + .then(() => { + setLoading(false); + }) + .then(removeUser) + .catch(() => { + setErrorDeleting(true); + setLoading(false); + }); + }; + + const buttonClasses = "button is-danger" + (loading ? " is-loading" : ""); + return ( + +
Ești sigur că vrei să ștergi profilul și datele asociate?
+
{}}> +
+ + +
+
+ + +
+
+ +
+
+ +
+ ); +}; + +DeleteAccount.propTypes = { + location: PropTypes.shape({ + state: PropTypes.shape({ + id: PropTypes.number.isRequired, + isSelf: PropTypes.bool.isRequired + }).isRequired + }).isRequired +}; + +export default DeleteAccount; diff --git a/frontend/src/components/DeleteAccount/style.scss b/frontend/src/components/DeleteAccount/style.scss new file mode 100644 index 00000000..75e84143 --- /dev/null +++ b/frontend/src/components/DeleteAccount/style.scss @@ -0,0 +1,3 @@ +.notification { + margin-top: 1em; +} \ No newline at end of file diff --git a/frontend/src/components/Header/ProfileItems.js b/frontend/src/components/Header/ProfileItems.js index e6a5a285..8aea36d1 100644 --- a/frontend/src/components/Header/ProfileItems.js +++ b/frontend/src/components/Header/ProfileItems.js @@ -17,6 +17,7 @@ const ProfileItems = () => { Contul meu
+ Ștergere cont ) : ( <> diff --git a/frontend/src/routes.js b/frontend/src/routes.js index 20543abb..3c9efa27 100644 --- a/frontend/src/routes.js +++ b/frontend/src/routes.js @@ -5,6 +5,7 @@ import UpdateProfile from "./components/UpdateProfile"; import Evaluation from "./components/Evaluation"; import Account from "./components/Account"; import TermsAndConditions from "./components/TermsAndConditions"; +import DeleteAccount from "./components/DeleteAccount"; import { redirectSilentSignin, @@ -75,6 +76,10 @@ export const ROUTES = { updateprofile: { path: "/update-profile", component: UpdateProfile + }, + deleteaccount: { + path: "/delete-account", + component: DeleteAccount } } }; diff --git a/frontend/src/styles/base.scss b/frontend/src/styles/base.scss index 16eb51fe..6369dbdb 100644 --- a/frontend/src/styles/base.scss +++ b/frontend/src/styles/base.scss @@ -3,6 +3,8 @@ @import "~bulma/sass/utilities/_all.sass"; @import "~bulma/sass/base/_all.sass"; @import "~bulma/sass/elements/container.sass"; +@import "~bulma/sass/elements/notification.sass"; +@import "~bulma/sass/elements/other.sass"; @import "~bulma/sass/grid/columns.sass"; @import "~bulma/sass/components/tabs"; \ No newline at end of file From d0271c703716770c0d443b9d02ecbc4717a2e345 Mon Sep 17 00:00:00 2001 From: Mircea M Date: Sat, 27 Jun 2020 14:21:08 +0100 Subject: [PATCH 07/13] Using the identity server URL --- frontend/src/api/accountApi.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frontend/src/api/accountApi.js b/frontend/src/api/accountApi.js index 16dfc032..fbba78d9 100644 --- a/frontend/src/api/accountApi.js +++ b/frontend/src/api/accountApi.js @@ -1,4 +1,9 @@ -import api from "./api"; +import axios from "axios"; +import { Constants } from "../config/constants"; + +const api = axios.create({ + baseURL: `${Constants.idpUrl}/api` +}); const AccountApi = { deleteAccount: (user, password) => From a2b5fcbc4090c8b7f054ff59c3926eb434b20379 Mon Sep 17 00:00:00 2001 From: Mircea M Date: Sat, 27 Jun 2020 15:34:08 +0100 Subject: [PATCH 08/13] Fixing browser warning --- .../src/components/DeleteAccount/index.js | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/frontend/src/components/DeleteAccount/index.js b/frontend/src/components/DeleteAccount/index.js index e2471453..4325e619 100644 --- a/frontend/src/components/DeleteAccount/index.js +++ b/frontend/src/components/DeleteAccount/index.js @@ -1,12 +1,11 @@ import React, { useState } from "react"; -import PropTypes from "prop-types"; import SidebarLayout from "../SidebarLayout"; import AccountApi from "../../api/accountApi"; import "./style.scss"; import { removeUser } from "../../api/auth"; const DeleteAccount = () => { - const [user, setUser] = useState(); - const [password, setPassword] = useState(); + const [user, setUser] = useState(""); + const [password, setPassword] = useState(""); const [loading, setLoading] = useState(false); const [errorDeleting, setErrorDeleting] = useState(false); @@ -31,7 +30,15 @@ const DeleteAccount = () => { const buttonClasses = "button is-danger" + (loading ? " is-loading" : ""); return ( -
Ești sigur că vrei să ștergi profilul și datele asociate?
+
+ Contul tău va fi șters. Pentru a putea reutiliza această aplicație va + trebui să îți refaci contul de utilizator. Informațiile pe care le-ai + transmis pana acum prin intermediul aplicație vor rămâne stocate în baza + de date. Dacă dorești ca toate informațiile sa fie eliminate din baza de + date te rugam sa adresezi aceasta cerere către: +

Adresa: Strada Italiană, nr. 22, Sector 2, 020976, București

+

E-mail: jurnalmedical@adr.gov.ro

+
{}}>
@@ -71,13 +78,4 @@ const DeleteAccount = () => { ); }; -DeleteAccount.propTypes = { - location: PropTypes.shape({ - state: PropTypes.shape({ - id: PropTypes.number.isRequired, - isSelf: PropTypes.bool.isRequired - }).isRequired - }).isRequired -}; - export default DeleteAccount; From 62e066177cf2e8fbccea8504939cf2a03ec73485 Mon Sep 17 00:00:00 2001 From: Mircea Moise Date: Sun, 28 Jun 2020 12:37:34 +0100 Subject: [PATCH 09/13] Update frontend/src/components/DeleteAccount/index.js Co-authored-by: Nicu --- frontend/src/components/DeleteAccount/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/DeleteAccount/index.js b/frontend/src/components/DeleteAccount/index.js index 4325e619..dcc50fb2 100644 --- a/frontend/src/components/DeleteAccount/index.js +++ b/frontend/src/components/DeleteAccount/index.js @@ -33,9 +33,9 @@ const DeleteAccount = () => {
Contul tău va fi șters. Pentru a putea reutiliza această aplicație va trebui să îți refaci contul de utilizator. Informațiile pe care le-ai - transmis pana acum prin intermediul aplicație vor rămâne stocate în baza - de date. Dacă dorești ca toate informațiile sa fie eliminate din baza de - date te rugam sa adresezi aceasta cerere către: + transmis până acum prin intermediul aplicație vor rămâne stocate în baza + de date. Dacă dorești ca toate informațiile să fie eliminate din baza de + date te rugăm să adresezi această cerere către:

Adresa: Strada Italiană, nr. 22, Sector 2, 020976, București

E-mail: jurnalmedical@adr.gov.ro

From b9095c07731066cbe17b1330dea45b356a1513d3 Mon Sep 17 00:00:00 2001 From: "ion.dormenco" Date: Wed, 8 Jul 2020 14:24:27 +0300 Subject: [PATCH 10/13] Integrate delete user backend + frontend initial commit of merged commits removed email input --- .../Account/DeleteAccountController.cs | 33 ++++++++++--------- .../Quickstart/Account/DeleteAccountModel.cs | 8 +---- frontend/src/api/accountApi.js | 6 ++-- .../src/components/DeleteAccount/index.js | 16 ++------- 4 files changed, 23 insertions(+), 40 deletions(-) diff --git a/backend/src/StamAcasa.IdentityServer/Quickstart/Account/DeleteAccountController.cs b/backend/src/StamAcasa.IdentityServer/Quickstart/Account/DeleteAccountController.cs index 38e48dab..0bf87efe 100644 --- a/backend/src/StamAcasa.IdentityServer/Quickstart/Account/DeleteAccountController.cs +++ b/backend/src/StamAcasa.IdentityServer/Quickstart/Account/DeleteAccountController.cs @@ -1,21 +1,12 @@ using System; -using System.Linq; -using System.Net.Http; using System.Threading.Tasks; using IdentityServer.Data; -using IdentityServer4; -using IdentityServer4.Models; -using IdentityServer4.Services; -using IdentityServer4.Validation; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Configuration; namespace StamAcasa.IdentityServer.Quickstart.Account { - [Route("api/[controller]")] [ApiController] [AllowAnonymous] public class DeleteAccountController : ControllerBase @@ -26,22 +17,32 @@ public DeleteAccountController(UserManager userManager) { _userManager = userManager; } - + [Route("/api/delete")] [HttpPost] public async Task DeleteAccountAsync([FromBody] DeleteAccountModel model) { - var user = await _userManager.FindByNameAsync(model.Username); - if (user == null || !await _userManager.CheckPasswordAsync(user, model.Password)) + var user = await _userManager.GetUserAsync(User); + if (user == null) { - return new UnauthorizedResult(); + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); } - var response = await _userManager.DeleteAsync(user); - if (!response.Succeeded) + if (!await _userManager.CheckPasswordAsync(user, model.Password)) { - return StatusCode(StatusCodes.Status500InternalServerError, $"Unexpected error occurred deleting user with ID '{user.Id}'."); + return Problem("Incorrect password"); } + + var result = await _userManager.DeleteAsync(user); + var userId = await _userManager.GetUserIdAsync(user); + if (!result.Succeeded) + { + throw new InvalidOperationException($"Unexpected error occurred deleting user with ID '{userId}'."); + } + + //await _signInManager.SignOutAsync(); + + return Ok(); } } diff --git a/backend/src/StamAcasa.IdentityServer/Quickstart/Account/DeleteAccountModel.cs b/backend/src/StamAcasa.IdentityServer/Quickstart/Account/DeleteAccountModel.cs index f7febb7e..48acae8c 100644 --- a/backend/src/StamAcasa.IdentityServer/Quickstart/Account/DeleteAccountModel.cs +++ b/backend/src/StamAcasa.IdentityServer/Quickstart/Account/DeleteAccountModel.cs @@ -1,14 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - + namespace StamAcasa.IdentityServer.Quickstart.Account { public class DeleteAccountModel { - public string Username { get; set; } - public string Password { get; set; } } } diff --git a/frontend/src/api/accountApi.js b/frontend/src/api/accountApi.js index fbba78d9..e4013bfe 100644 --- a/frontend/src/api/accountApi.js +++ b/frontend/src/api/accountApi.js @@ -2,12 +2,12 @@ import axios from "axios"; import { Constants } from "../config/constants"; const api = axios.create({ - baseURL: `${Constants.idpUrl}/api` + baseURL: `${Constants.idpUrl}/api/` }); const AccountApi = { - deleteAccount: (user, password) => - api.post("/DeleteAccount", { Username: user, Password: password }) + deleteAccount: (password) =>api.post("delete", { password }) + }; export default AccountApi; diff --git a/frontend/src/components/DeleteAccount/index.js b/frontend/src/components/DeleteAccount/index.js index dcc50fb2..ba90cca5 100644 --- a/frontend/src/components/DeleteAccount/index.js +++ b/frontend/src/components/DeleteAccount/index.js @@ -4,19 +4,17 @@ import AccountApi from "../../api/accountApi"; import "./style.scss"; import { removeUser } from "../../api/auth"; const DeleteAccount = () => { - const [user, setUser] = useState(""); const [password, setPassword] = useState(""); const [loading, setLoading] = useState(false); const [errorDeleting, setErrorDeleting] = useState(false); - const fieldsFilled = user && password; + const fieldsFilled = password; - const updateUser = event => setUser(event.target.value); const updatePassword = event => setPassword(event.target.value); const deleteProfile = () => { setLoading(true); - AccountApi.deleteAccount(user, password) + AccountApi.deleteAccount(password) .then(() => { setLoading(false); }) @@ -40,16 +38,6 @@ const DeleteAccount = () => {

E-mail: jurnalmedical@adr.gov.ro

{}}> -
- - -
Date: Wed, 8 Jul 2020 22:12:32 +0300 Subject: [PATCH 11/13] fix cors, moved remove functionality to account endpoint --- .../Quickstart/Account/AccountController.cs | 27 ++++++++++ .../Account/DeleteAccountController.cs | 49 ------------------- .../Quickstart/Account/DeleteAccountModel.cs | 6 +++ .../src/StamAcasa.IdentityServer/Startup.cs | 14 ++++++ frontend/src/api/accountApi.js | 5 +- .../src/components/DeleteAccount/index.js | 26 +++++----- 6 files changed, 62 insertions(+), 65 deletions(-) delete mode 100644 backend/src/StamAcasa.IdentityServer/Quickstart/Account/DeleteAccountController.cs diff --git a/backend/src/StamAcasa.IdentityServer/Quickstart/Account/AccountController.cs b/backend/src/StamAcasa.IdentityServer/Quickstart/Account/AccountController.cs index 59bbc9be..01d8139d 100644 --- a/backend/src/StamAcasa.IdentityServer/Quickstart/Account/AccountController.cs +++ b/backend/src/StamAcasa.IdentityServer/Quickstart/Account/AccountController.cs @@ -18,6 +18,7 @@ using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Hosting; +using StamAcasa.IdentityServer.Quickstart.Account; namespace IdentityServer.Quickstart.Account { @@ -212,6 +213,32 @@ public IActionResult AccessDenied() return View(); } + [Route("account/delete")] + [HttpPost] + public async Task DeleteAccountAsync([FromBody] DeleteAccountModel model) + { + var user = await _userManager.FindByNameAsync(model.Email); + if (user == null) + { + return Problem("Utilizatorul nu a fost sters"); + } + + if (!await _userManager.CheckPasswordAsync(user, model.Password)) + { + return Problem("Utilizatorul nu a fost sters"); + } + + var result = await _userManager.DeleteAsync(user); + var userId = await _userManager.GetUserIdAsync(user); + if (!result.Succeeded) + { + throw new InvalidOperationException($"Unexpected error occurred deleting user with ID '{userId}'."); + } + + await _signInManager.SignOutAsync(); + + return Ok(); + } /*****************************************/ /* helper APIs for the AccountController */ diff --git a/backend/src/StamAcasa.IdentityServer/Quickstart/Account/DeleteAccountController.cs b/backend/src/StamAcasa.IdentityServer/Quickstart/Account/DeleteAccountController.cs deleted file mode 100644 index 0bf87efe..00000000 --- a/backend/src/StamAcasa.IdentityServer/Quickstart/Account/DeleteAccountController.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using System.Threading.Tasks; -using IdentityServer.Data; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; - -namespace StamAcasa.IdentityServer.Quickstart.Account -{ - [ApiController] - [AllowAnonymous] - public class DeleteAccountController : ControllerBase - { - private readonly UserManager _userManager; - - public DeleteAccountController(UserManager userManager) - { - _userManager = userManager; - } - [Route("/api/delete")] - [HttpPost] - public async Task DeleteAccountAsync([FromBody] DeleteAccountModel model) - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - if (!await _userManager.CheckPasswordAsync(user, model.Password)) - { - return Problem("Incorrect password"); - } - - - var result = await _userManager.DeleteAsync(user); - var userId = await _userManager.GetUserIdAsync(user); - if (!result.Succeeded) - { - throw new InvalidOperationException($"Unexpected error occurred deleting user with ID '{userId}'."); - } - - //await _signInManager.SignOutAsync(); - - - return Ok(); - } - } -} \ No newline at end of file diff --git a/backend/src/StamAcasa.IdentityServer/Quickstart/Account/DeleteAccountModel.cs b/backend/src/StamAcasa.IdentityServer/Quickstart/Account/DeleteAccountModel.cs index 48acae8c..095d566d 100644 --- a/backend/src/StamAcasa.IdentityServer/Quickstart/Account/DeleteAccountModel.cs +++ b/backend/src/StamAcasa.IdentityServer/Quickstart/Account/DeleteAccountModel.cs @@ -1,8 +1,14 @@  +using System.ComponentModel.DataAnnotations; + namespace StamAcasa.IdentityServer.Quickstart.Account { public class DeleteAccountModel { + [Required] + public string Email { get; set; } + + [Required] public string Password { get; set; } } } diff --git a/backend/src/StamAcasa.IdentityServer/Startup.cs b/backend/src/StamAcasa.IdentityServer/Startup.cs index 662a9541..3f4ad467 100644 --- a/backend/src/StamAcasa.IdentityServer/Startup.cs +++ b/backend/src/StamAcasa.IdentityServer/Startup.cs @@ -116,6 +116,19 @@ public void ConfigureServices(IServiceCollection services) )); services.AddSingleton(); services.AddSingleton(); + + services.AddCors(options => + { + // this defines a CORS policy called "default" + options.AddPolicy("default", policy => + { + policy = _identityConfiguration.Clients.SelectMany(x => x.AllowedCorsOrigins) + .Aggregate(policy, (current, url) => current.WithOrigins(url)); + + policy.AllowAnyHeader() + .AllowAnyMethod(); + }); + }); } private X509Certificate2 LoadCertificate(string base64EncodedCertificate, string password) @@ -140,6 +153,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseHttpsRedirection(); } + app.UseCors("default"); app.UseRouting(); app.UseStaticFiles(); var cookiePolicyOptions = new CookiePolicyOptions diff --git a/frontend/src/api/accountApi.js b/frontend/src/api/accountApi.js index e4013bfe..4eaa721b 100644 --- a/frontend/src/api/accountApi.js +++ b/frontend/src/api/accountApi.js @@ -2,12 +2,11 @@ import axios from "axios"; import { Constants } from "../config/constants"; const api = axios.create({ - baseURL: `${Constants.idpUrl}/api/` + baseURL: `${Constants.idpUrl}/account/` }); const AccountApi = { - deleteAccount: (password) =>api.post("delete", { password }) - + deleteAccount: (email, password) => api.post("delete", { email, password }) }; export default AccountApi; diff --git a/frontend/src/components/DeleteAccount/index.js b/frontend/src/components/DeleteAccount/index.js index ba90cca5..14f62b05 100644 --- a/frontend/src/components/DeleteAccount/index.js +++ b/frontend/src/components/DeleteAccount/index.js @@ -2,27 +2,27 @@ import React, { useState } from "react"; import SidebarLayout from "../SidebarLayout"; import AccountApi from "../../api/accountApi"; import "./style.scss"; -import { removeUser } from "../../api/auth"; +import { removeUser, getUser } from "../../api/auth"; const DeleteAccount = () => { const [password, setPassword] = useState(""); const [loading, setLoading] = useState(false); const [errorDeleting, setErrorDeleting] = useState(false); - const fieldsFilled = password; + const fieldsFilled = password; const updatePassword = event => setPassword(event.target.value); - const deleteProfile = () => { - setLoading(true); - AccountApi.deleteAccount(password) - .then(() => { - setLoading(false); - }) - .then(removeUser) - .catch(() => { - setErrorDeleting(true); - setLoading(false); - }); + const deleteProfile = async () => { + try { + setLoading(true); + const user = await getUser(); + await AccountApi.deleteAccount(user.profile.email, password); + setLoading(false); + await removeUser(); + } catch { + setErrorDeleting(true); + setLoading(false); + } }; const buttonClasses = "button is-danger" + (loading ? " is-loading" : ""); From 80b29381073657029d61f16d29df49cbd363427b Mon Sep 17 00:00:00 2001 From: Ion Dormenco Date: Wed, 22 Jul 2020 13:32:13 +0300 Subject: [PATCH 12/13] Update frontend/src/components/DeleteAccount/index.js Co-authored-by: Nicu --- frontend/src/components/DeleteAccount/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/DeleteAccount/index.js b/frontend/src/components/DeleteAccount/index.js index 14f62b05..0c39a3cc 100644 --- a/frontend/src/components/DeleteAccount/index.js +++ b/frontend/src/components/DeleteAccount/index.js @@ -31,7 +31,7 @@ const DeleteAccount = () => {
Contul tău va fi șters. Pentru a putea reutiliza această aplicație va trebui să îți refaci contul de utilizator. Informațiile pe care le-ai - transmis până acum prin intermediul aplicație vor rămâne stocate în baza + transmis până acum prin intermediul aplicației vor rămâne stocate în baza de date. Dacă dorești ca toate informațiile să fie eliminate din baza de date te rugăm să adresezi această cerere către:

Adresa: Strada Italiană, nr. 22, Sector 2, 020976, București

From 576f3182f193f0d60bd5ac59981c390e3d438f76 Mon Sep 17 00:00:00 2001 From: Ion Dormenco Date: Fri, 31 Jul 2020 15:50:00 +0300 Subject: [PATCH 13/13] linter fix --- frontend/src/components/DeleteAccount/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/DeleteAccount/index.js b/frontend/src/components/DeleteAccount/index.js index 0c39a3cc..ed77bb72 100644 --- a/frontend/src/components/DeleteAccount/index.js +++ b/frontend/src/components/DeleteAccount/index.js @@ -31,9 +31,9 @@ const DeleteAccount = () => {
Contul tău va fi șters. Pentru a putea reutiliza această aplicație va trebui să îți refaci contul de utilizator. Informațiile pe care le-ai - transmis până acum prin intermediul aplicației vor rămâne stocate în baza - de date. Dacă dorești ca toate informațiile să fie eliminate din baza de - date te rugăm să adresezi această cerere către: + transmis până acum prin intermediul aplicației vor rămâne stocate în + baza de date. Dacă dorești ca toate informațiile să fie eliminate din + baza de date te rugăm să adresezi această cerere către:

Adresa: Strada Italiană, nr. 22, Sector 2, 020976, București

E-mail: jurnalmedical@adr.gov.ro