From 17a6bf627d0aa575fc3f67c845a3c60983be2c40 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Morten=20Remb=C3=B8l=20Jacobsen?=
Date: Mon, 25 Apr 2022 11:10:27 +0200
Subject: [PATCH 001/432] added draft for user deletion
---
Core.ApplicationServices/AdviceService.cs | 4 ++
.../OrganizationAuthorizationContext.cs | 5 +-
Core.ApplicationServices/IUserService.cs | 1 +
.../OrganizationRoleService.cs | 5 --
.../Organizations/OrganizationService.cs | 20 ++++---
Core.ApplicationServices/UserService.cs | 59 ++++++++++++++++++-
Core.DomainModel/User.cs | 5 +-
.../IOrganizationRoleService.cs | 2 -
.../Controllers/API/V1/UserController.cs | 16 +++--
.../CustomMembershipProvider.cs | 5 ++
.../ApplicationServices/UserServiceTest.cs | 4 +-
.../Services/OrganizationServiceTest.cs | 4 +-
12 files changed, 101 insertions(+), 29 deletions(-)
diff --git a/Core.ApplicationServices/AdviceService.cs b/Core.ApplicationServices/AdviceService.cs
index 603ac3401d..b83c7ed246 100644
--- a/Core.ApplicationServices/AdviceService.cs
+++ b/Core.ApplicationServices/AdviceService.cs
@@ -239,6 +239,7 @@ private void AddRecipientByRole(Advice advice, AdviceUserRelation r, MailAddress
&& I.Role.Name == r.Name);
foreach (var t in itContractRoles)
{
+ if(t.User.Deleted) continue;
mailAddressCollection.Add(t.User.Email);
}
@@ -248,6 +249,7 @@ private void AddRecipientByRole(Advice advice, AdviceUserRelation r, MailAddress
&& I.Role.Name == r.Name);
foreach (var t in projectRoles)
{
+ if(t.User.Deleted) continue;
mailAddressCollection.Add(t.User.Email);
}
@@ -258,6 +260,7 @@ private void AddRecipientByRole(Advice advice, AdviceUserRelation r, MailAddress
&& I.Role.Name == r.Name);
foreach (var t in systemRoles)
{
+ if(t.User.Deleted) continue;
mailAddressCollection.Add(t.User.Email);
}
@@ -269,6 +272,7 @@ private void AddRecipientByRole(Advice advice, AdviceUserRelation r, MailAddress
&& I.Role.Name == r.Name);
foreach (var t in dpaRoles)
{
+ if(t.User.Deleted) continue;
mailAddressCollection.Add(t.User.Email);
}
diff --git a/Core.ApplicationServices/Authorization/OrganizationAuthorizationContext.cs b/Core.ApplicationServices/Authorization/OrganizationAuthorizationContext.cs
index b1b80e46e2..59035d6c29 100644
--- a/Core.ApplicationServices/Authorization/OrganizationAuthorizationContext.cs
+++ b/Core.ApplicationServices/Authorization/OrganizationAuthorizationContext.cs
@@ -236,6 +236,7 @@ public bool AllowDelete(IEntity entity)
{
result = entity switch
{
+ User _ => IsGlobalAdmin(),
ItInterface itInterface =>
//Even rightsholders are not allowed to delete interfaces
IsGlobalAdmin() || IsLocalAdmin(itInterface.OrganizationId),
@@ -368,10 +369,10 @@ bool IPermissionVisitor.Visit(VisibilityControlPermission permission)
return target switch
{
- IContractModule _ => IsGlobalAdmin() ||
+ IContractModule _ => IsGlobalAdmin() ||
IsLocalAdmin(ownedByOrganization.OrganizationId) ||
IsContractModuleAdmin(ownedByOrganization.OrganizationId),
- IOrganizationModule _ => IsGlobalAdmin() ||
+ IOrganizationModule _ => IsGlobalAdmin() ||
IsLocalAdmin(ownedByOrganization.OrganizationId),
_ => IsGlobalAdmin()
};
diff --git a/Core.ApplicationServices/IUserService.cs b/Core.ApplicationServices/IUserService.cs
index d4e0e0460d..2794579c0e 100644
--- a/Core.ApplicationServices/IUserService.cs
+++ b/Core.ApplicationServices/IUserService.cs
@@ -18,5 +18,6 @@ public interface IUserService : IDisposable
Result, OperationError> GetUsersWithRoleAssignedInAnyOrganization(OrganizationRole role);
Result, OperationError> GetUsersInOrganization(Guid organizationUuid, params IDomainQuery[] queries);
Result GetUserInOrganization(Guid organizationUuid, Guid userUuid);
+ Maybe DeleteUserFromKitos(Guid userUuid);
}
}
\ No newline at end of file
diff --git a/Core.ApplicationServices/OrganizationRoleService.cs b/Core.ApplicationServices/OrganizationRoleService.cs
index 1c2679538c..5ff5a71b75 100644
--- a/Core.ApplicationServices/OrganizationRoleService.cs
+++ b/Core.ApplicationServices/OrganizationRoleService.cs
@@ -40,11 +40,6 @@ public OrganizationRight MakeUser(User user, Organization organization)
return AddOrganizationRoleToUser(user, organization, OrganizationRole.User);
}
- public OrganizationRight MakeLocalAdmin(User user, Organization organization)
- {
- return AddOrganizationRoleToUser(user, organization, OrganizationRole.LocalAdmin);
- }
-
public IReadOnlyDictionary> GetOrganizationRoles(User user)
{
var rolesByRights = user.OrganizationRights
diff --git a/Core.ApplicationServices/Organizations/OrganizationService.cs b/Core.ApplicationServices/Organizations/OrganizationService.cs
index 7575caeb76..f88c041123 100644
--- a/Core.ApplicationServices/Organizations/OrganizationService.cs
+++ b/Core.ApplicationServices/Organizations/OrganizationService.cs
@@ -1,5 +1,4 @@
using System;
-using System.Data;
using System.Linq;
using Core.Abstractions.Extensions;
using Core.Abstractions.Types;
@@ -24,12 +23,12 @@ public class OrganizationService : IOrganizationService
private readonly IGenericRepository _orgRepository;
private readonly IOrganizationRepository _repository;
private readonly IOrgUnitService _orgUnitService;
+ private readonly IOrganizationRightsService _organizationRightsService;
private readonly IGenericRepository _orgRightRepository;
private readonly IGenericRepository _userRepository;
private readonly IAuthorizationContext _authorizationContext;
private readonly IOrganizationalUserContext _userContext;
private readonly ILogger _logger;
- private readonly IOrganizationRoleService _organizationRoleService;
private readonly ITransactionManager _transactionManager;
public OrganizationService(
@@ -39,10 +38,10 @@ public OrganizationService(
IAuthorizationContext authorizationContext,
IOrganizationalUserContext userContext,
ILogger logger,
- IOrganizationRoleService organizationRoleService,
ITransactionManager transactionManager,
IOrganizationRepository repository,
- IOrgUnitService orgUnitService)
+ IOrgUnitService orgUnitService,
+ IOrganizationRightsService organizationRightsService)
{
_orgRepository = orgRepository;
_orgRightRepository = orgRightRepository;
@@ -50,10 +49,10 @@ public OrganizationService(
_authorizationContext = authorizationContext;
_userContext = userContext;
_logger = logger;
- _organizationRoleService = organizationRoleService;
_transactionManager = transactionManager;
_repository = repository;
_orgUnitService = orgUnitService;
+ _organizationRightsService = organizationRightsService;
}
//returns the default org unit for that user inside that organization
@@ -82,6 +81,7 @@ public void SetDefaultOrgUnit(User user, int orgId, int orgUnitId)
/// The user to be removed.
public Result RemoveUser(int organizationId, int userId)
{
+ using var transaction = _transactionManager.Begin();
var organization = _orgRepository.GetByKey(organizationId);
if (organization == null)
{
@@ -100,9 +100,15 @@ public Result RemoveUser(int organizationId, int
foreach (var right in rights)
{
- _orgRightRepository.DeleteByKey(right.Id);
+ var result = _organizationRightsService.RemoveRole(right.Id);
+ if (result.Failed)
+ {
+ _logger.Error("Failed to delete right with id {rightId} due to error: {errorCode}", right.Id, result.Error);
+ transaction.Rollback();
+ return Result.Failure(OperationFailure.UnknownError);
+ }
}
- _orgRightRepository.Save();
+ transaction.Commit();
return organization;
}
diff --git a/Core.ApplicationServices/UserService.cs b/Core.ApplicationServices/UserService.cs
index ee5830e583..80b7061aef 100644
--- a/Core.ApplicationServices/UserService.cs
+++ b/Core.ApplicationServices/UserService.cs
@@ -13,10 +13,12 @@
using Core.ApplicationServices.Authorization;
using Core.ApplicationServices.Organizations;
using Core.DomainModel.Events;
+using Core.DomainModel.Organization.DomainEvents;
using Infrastructure.Services.Cryptography;
using Core.DomainServices.Authorization;
using Core.DomainServices.Extensions;
using Core.DomainServices.Queries;
+using Infrastructure.Services.DataAccess;
namespace Core.ApplicationServices
@@ -30,6 +32,7 @@ public class UserService : IUserService
private readonly bool _useDefaultUserPassword;
private readonly IUserRepository _repository;
private readonly IOrganizationService _organizationService;
+ private readonly ITransactionManager _transactionManager;
private readonly IGenericRepository _userRepository;
private readonly IGenericRepository _orgRepository;
private readonly IGenericRepository _passwordResetRequestRepository;
@@ -54,7 +57,8 @@ public UserService(TimeSpan ttl,
IAuthorizationContext authorizationContext,
IDomainEvents domainEvents,
IUserRepository repository,
- IOrganizationService organizationService)
+ IOrganizationService organizationService,
+ ITransactionManager transactionManager)
{
_ttl = ttl;
_baseUrl = baseUrl;
@@ -70,6 +74,7 @@ public UserService(TimeSpan ttl,
_domainEvents = domainEvents;
_repository = repository;
_organizationService = organizationService;
+ _transactionManager = transactionManager;
_crypt = new SHA256Managed();
if (useDefaultUserPassword && string.IsNullOrWhiteSpace(defaultUserPassword))
{
@@ -124,7 +129,7 @@ public void IssueAdvisMail(User user, bool reminder, int orgId)
"'>her, hvor du første gang bliver bedt om at indtaste et nyt password for din KITOS profil.
" +
"Linket udløber om " + _ttl.TotalDays + " dage. Klik her, " +
"hvis dit link er udløbet og du vil blive ledt til 'Glemt password' proceduren.
" +
- "Klik her for at få Hjælp til log ind og brugerkonto
" +
+ "Klik her for at få Hjælp til log ind og brugerkonto
" +
"Bemærk at denne mail ikke kan besvares.
";
IssuePasswordReset(user, subject, content);
@@ -149,7 +154,7 @@ public PasswordResetRequest IssuePasswordReset(User user, string subject, string
"Klik her for at nulstille passwordet for din KITOS profil.
" +
"Linket udløber om " + _ttl.TotalDays + " dage.
" +
- "Klik her for at få Hjælp til log ind og brugerkonto
" +
+ "Klik her for at få Hjælp til log ind og brugerkonto
" +
"Bemærk at denne mail ikke kan besvares.
";
}
var mailSubject = "Nulstilning af dit KITOS password" + _mailSuffix;
@@ -280,5 +285,53 @@ public Result GetUserInOrganization(Guid organizationUuid,
)
);
}
+
+ public Maybe DeleteUserFromKitos(Guid userUuid)
+ {
+ using var transaction = _transactionManager.Begin();
+
+ var user = _userRepository.AsQueryable().ByUuid(userUuid);
+ if (user == null)
+ return Maybe.Some(new OperationError(OperationFailure.NotFound));
+
+ if (!_authorizationContext.AllowDelete(user))
+ return Maybe.Some(new OperationError(OperationFailure.Forbidden));
+
+ Delete(user);
+ _domainEvents.Raise(new AccessRightsChanged(user.Id));
+ _userRepository.Save();
+
+ transaction.Commit();
+ return Maybe.None;
+ }
+
+ private void Delete(User user)
+ {
+ //TODO: Disable automatic advice emails sent to user.. set a flag "disabled".. also disable the user for login with the same flag (disabled)
+ user.LockedOutDate = DateTime.Now;
+ user.EmailBeforeDeletion = user.Email;
+ user.Email = $"{Guid.NewGuid()}_deleted_user@kitos.dk";
+ user.PhoneNumber = null;
+ user.LastName = $"{(user.LastName ?? "").TrimEnd()} (SLETTET)";
+ user.DeletedDate = DateTime.Now;
+ user.IsGlobalAdmin = false;
+ user.HasApiAccess = false;
+ user.HasStakeHolderAccess = false;
+
+
+ //TODO: Check cascades
+ user.ItProjectRights.Clear();
+ user.ItContractRights.Clear();
+ user.DataProcessingRegistrationRights.Clear();
+ user.ItSystemRights.Clear();
+ user.OrganizationRights.Clear();
+ user.OrganizationUnitRights.Clear();
+ user.ItProjectStatuses.Clear();
+ user.ResponsibleForCommunications.Clear();
+ user.HandoverParticipants.Clear();
+ user.LifeCycleTrackingEvents.Clear();
+ user.ResponsibleForRisks.Clear();
+ user.SsoIdentities.Clear();
+ }
}
}
diff --git a/Core.DomainModel/User.cs b/Core.DomainModel/User.cs
index 852299b15b..c4424147ef 100644
--- a/Core.DomainModel/User.cs
+++ b/Core.DomainModel/User.cs
@@ -46,6 +46,9 @@ public User()
public string Password { get; set; }
public string Salt { get; set; }
public DateTime? LastAdvisDate { get; set; }
+ public string EmailBeforeDeletion { get; set; }
+ public DateTime? DeletedDate { get; set; }
+ public bool Deleted { get; set; }
public string DefaultUserStartPreference { get; set; }
///
@@ -59,7 +62,7 @@ public User()
public bool CanAuthenticate()
{
- return IsGlobalAdmin || OrganizationRights.Any();
+ return !Deleted && (IsGlobalAdmin || OrganizationRights.Any());
}
///
diff --git a/Core.DomainServices/IOrganizationRoleService.cs b/Core.DomainServices/IOrganizationRoleService.cs
index 6e38910dd4..6a1f74df6c 100644
--- a/Core.DomainServices/IOrganizationRoleService.cs
+++ b/Core.DomainServices/IOrganizationRoleService.cs
@@ -8,8 +8,6 @@ public interface IOrganizationRoleService
{
OrganizationRight MakeUser(User user, Organization organization);
- OrganizationRight MakeLocalAdmin(User user, Organization organization);
-
IReadOnlyDictionary> GetOrganizationRoles(User user);
IEnumerable GetRolesInOrganization(User user, int organizationId);
diff --git a/Presentation.Web/Controllers/API/V1/UserController.cs b/Presentation.Web/Controllers/API/V1/UserController.cs
index 63134cce29..259ecc8b90 100644
--- a/Presentation.Web/Controllers/API/V1/UserController.cs
+++ b/Presentation.Web/Controllers/API/V1/UserController.cs
@@ -4,7 +4,6 @@
using System.Net.Http;
using System.Web.Http;
using Core.ApplicationServices;
-using Core.ApplicationServices.Authorization;
using Core.ApplicationServices.Authorization.Permissions;
using Core.ApplicationServices.Model.RightsHolder;
using Core.ApplicationServices.Organizations;
@@ -13,6 +12,7 @@
using Core.DomainModel.Organization;
using Core.DomainServices;
using Core.DomainServices.Extensions;
+using Core.DomainServices.Generic;
using Newtonsoft.Json.Linq;
using Presentation.Web.Infrastructure.Attributes;
using Presentation.Web.Models.API.V1;
@@ -25,21 +25,21 @@ public class UserController : GenericApiController
{
private readonly IUserService _userService;
private readonly IOrganizationService _organizationService;
- private readonly IOrganizationalUserContext _userContext;
private readonly IUserRightsService _userRightsService;
+ private readonly IEntityIdentityResolver _identityResolver;
public UserController(
IGenericRepository repository,
IUserService userService,
IOrganizationService organizationService,
- IOrganizationalUserContext userContext,
- IUserRightsService userRightsService)
+ IUserRightsService userRightsService,
+ IEntityIdentityResolver identityResolver)
: base(repository)
{
_userService = userService;
_organizationService = organizationService;
- _userContext = userContext;
_userRightsService = userRightsService;
+ _identityResolver = identityResolver;
}
[NonAction]
@@ -183,8 +183,12 @@ public HttpResponseMessage PostDefaultOrgUnit(bool? updateDefaultOrgUnit, Update
public override HttpResponseMessage Delete(int id, int organizationId = 0)
{
// NOTE: Only exists to apply optional param for org id
- return base.Delete(id, organizationId);
+ var uuid = _identityResolver.ResolveUuid(id);
+ return uuid.IsNone
+ ? NotFound()
+ : _userService.DeleteUserFromKitos(uuid.Value).Match(FromOperationError, Ok);
}
+
[HttpGet]
[Route("api/user/with-rightsholder-access")]
diff --git a/Presentation.Web/Infrastructure/CustomMembershipProvider.cs b/Presentation.Web/Infrastructure/CustomMembershipProvider.cs
index 86b350f25a..7adcfb5964 100644
--- a/Presentation.Web/Infrastructure/CustomMembershipProvider.cs
+++ b/Presentation.Web/Infrastructure/CustomMembershipProvider.cs
@@ -179,6 +179,11 @@ public override bool ValidateUser(string username, string password)
return isValid;
}
+ if (user.Deleted)
+ {
+ Logger.Warn("Attempt to authenticate deleted user with id:{id}",user.Id);
+ return false;
+ }
// having a LockedOutDate means that the user is locked out
if (user.LockedOutDate != null)
{
diff --git a/Tests.Unit.Core.ApplicationServices/ApplicationServices/UserServiceTest.cs b/Tests.Unit.Core.ApplicationServices/ApplicationServices/UserServiceTest.cs
index 1d81680b15..4e147c109a 100644
--- a/Tests.Unit.Core.ApplicationServices/ApplicationServices/UserServiceTest.cs
+++ b/Tests.Unit.Core.ApplicationServices/ApplicationServices/UserServiceTest.cs
@@ -13,6 +13,7 @@
using Core.ApplicationServices.Organizations;
using Core.DomainModel.Events;
using Core.DomainServices.Queries;
+using Infrastructure.Services.DataAccess;
using Tests.Toolkit.Patterns;
using Xunit;
@@ -58,7 +59,8 @@ public UserServiceTest()
_authorizationContextMock.Object,
_domainEventsMock.Object,
_repositoryMock.Object,
- _organizationServiceMock.Object);
+ _organizationServiceMock.Object,
+ Mock.Of());
}
[Fact]
diff --git a/Tests.Unit.Presentation.Web/Services/OrganizationServiceTest.cs b/Tests.Unit.Presentation.Web/Services/OrganizationServiceTest.cs
index 6ce18263c1..098cedd6e2 100644
--- a/Tests.Unit.Presentation.Web/Services/OrganizationServiceTest.cs
+++ b/Tests.Unit.Presentation.Web/Services/OrganizationServiceTest.cs
@@ -58,10 +58,10 @@ public OrganizationServiceTest()
_authorizationContext.Object,
_userContext.Object,
Mock.Of(),
- _roleService.Object,
_transactionManager.Object,
_repositoryMock.Object,
- _orgUnitServiceMock.Object);
+ _orgUnitServiceMock.Object,
+ Mock.Of());
}
[Fact]
From c24000cd7a99737fb4598405fa839c7968b2c45a Mon Sep 17 00:00:00 2001
From: Aleksander Naskret
Date: Thu, 19 May 2022 09:44:20 +0200
Subject: [PATCH 002/432] Delete TODO
---
Core.ApplicationServices/UserService.cs | 1 -
.../Tests.Integration.Presentation.Web.csproj | 1 +
.../Tools/RightsHelper.cs | 12 ++++++++++++
Tests.Integration.Presentation.Web/Users/UserTest.cs | 6 ++++++
4 files changed, 19 insertions(+), 1 deletion(-)
create mode 100644 Tests.Integration.Presentation.Web/Tools/RightsHelper.cs
diff --git a/Core.ApplicationServices/UserService.cs b/Core.ApplicationServices/UserService.cs
index 4bc127d3e8..a11e991a8e 100644
--- a/Core.ApplicationServices/UserService.cs
+++ b/Core.ApplicationServices/UserService.cs
@@ -307,7 +307,6 @@ public Maybe DeleteUserFromKitos(Guid userUuid)
private void Delete(User user)
{
- //TODO: Disable automatic advice emails sent to user.. set a flag "disabled".. also disable the user for login with the same flag (disabled)
user.LockedOutDate = DateTime.Now;
user.EmailBeforeDeletion = user.Email;
user.Email = $"{Guid.NewGuid()}_deleted_user@kitos.dk";
diff --git a/Tests.Integration.Presentation.Web/Tests.Integration.Presentation.Web.csproj b/Tests.Integration.Presentation.Web/Tests.Integration.Presentation.Web.csproj
index 5ddca70de5..ca1f2c1cb6 100644
--- a/Tests.Integration.Presentation.Web/Tests.Integration.Presentation.Web.csproj
+++ b/Tests.Integration.Presentation.Web/Tests.Integration.Presentation.Web.csproj
@@ -193,6 +193,7 @@
+
diff --git a/Tests.Integration.Presentation.Web/Tools/RightsHelper.cs b/Tests.Integration.Presentation.Web/Tools/RightsHelper.cs
new file mode 100644
index 0000000000..12c00de386
--- /dev/null
+++ b/Tests.Integration.Presentation.Web/Tools/RightsHelper.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tests.Integration.Presentation.Web.Tools
+{
+ internal class RightsHelper
+ {
+ }
+}
diff --git a/Tests.Integration.Presentation.Web/Users/UserTest.cs b/Tests.Integration.Presentation.Web/Users/UserTest.cs
index 9302adb9e8..d82336da28 100644
--- a/Tests.Integration.Presentation.Web/Users/UserTest.cs
+++ b/Tests.Integration.Presentation.Web/Users/UserTest.cs
@@ -119,6 +119,12 @@ public async Task Cannot_Get_Users_Where_User_Has_StakeHolder_Or_ApiAccess_If_No
Assert.Equal(HttpStatusCode.Forbidden, result.StatusCode);
}
+ [Fact]
+ public async Task Delete_User()
+ {
+
+ }
+
private async Task<(int userId, string userEmail, string orgName)> CreateStakeHolderUserInNewOrganizationAsync(bool hasApiAccess, bool hasStakeholderAccess)
{
var email = CreateEmail();
From 59b74574cc1485df943c50b3287ef0aa2da97fff Mon Sep 17 00:00:00 2001
From: Aleksander Naskret
Date: Thu, 19 May 2022 11:18:21 +0200
Subject: [PATCH 003/432] Delete test
---
.../Organizations/OrganizationService.cs | 3 +-
Core.ApplicationServices/UserService.cs | 4 +-
.../Infrastructure.DataAccess.csproj | 7 +
.../202205190801320_UserDelete.Designer.cs | 29 ++++
.../Migrations/202205190801320_UserDelete.cs | 22 +++
.../202205190801320_UserDelete.resx | 126 ++++++++++++++++++
.../Tests.Integration.Presentation.Web.csproj | 4 +-
.../Tools/External/Rights/RightsHelper.cs | 49 +++++++
.../Tools/External/Rights/RightsType.cs | 18 +++
.../Tools/Model/RightDTO.cs | 14 ++
.../Tools/RightsHelper.cs | 12 --
.../Tools/UserHelper.cs | 6 +
.../Users/UserTest.cs | 15 +++
.../Services/OrganizationServiceTest.cs | 3 +-
14 files changed, 294 insertions(+), 18 deletions(-)
create mode 100644 Infrastructure.DataAccess/Migrations/202205190801320_UserDelete.Designer.cs
create mode 100644 Infrastructure.DataAccess/Migrations/202205190801320_UserDelete.cs
create mode 100644 Infrastructure.DataAccess/Migrations/202205190801320_UserDelete.resx
create mode 100644 Tests.Integration.Presentation.Web/Tools/External/Rights/RightsHelper.cs
create mode 100644 Tests.Integration.Presentation.Web/Tools/External/Rights/RightsType.cs
create mode 100644 Tests.Integration.Presentation.Web/Tools/Model/RightDTO.cs
delete mode 100644 Tests.Integration.Presentation.Web/Tools/RightsHelper.cs
diff --git a/Core.ApplicationServices/Organizations/OrganizationService.cs b/Core.ApplicationServices/Organizations/OrganizationService.cs
index 2545a9270e..654c749563 100644
--- a/Core.ApplicationServices/Organizations/OrganizationService.cs
+++ b/Core.ApplicationServices/Organizations/OrganizationService.cs
@@ -43,8 +43,7 @@ public OrganizationService(
ILogger logger,
ITransactionManager transactionManager,
IOrganizationRepository repository,
- IOrgUnitService orgUnitService,
- IOrganizationRightsService organizationRightsService)
+ IOrganizationRightsService organizationRightsService,
IOrgUnitService orgUnitService,
IDomainEvents domainEvents)
{
diff --git a/Core.ApplicationServices/UserService.cs b/Core.ApplicationServices/UserService.cs
index a11e991a8e..ab54407fdb 100644
--- a/Core.ApplicationServices/UserService.cs
+++ b/Core.ApplicationServices/UserService.cs
@@ -319,8 +319,9 @@ private void Delete(User user)
//TODO: Check cascades
- user.ItProjectRights.Clear();
user.ItContractRights.Clear();
+ /*
+ user.ItProjectRights.Clear();
user.DataProcessingRegistrationRights.Clear();
user.ItSystemRights.Clear();
user.OrganizationRights.Clear();
@@ -331,6 +332,7 @@ private void Delete(User user)
user.LifeCycleTrackingEvents.Clear();
user.ResponsibleForRisks.Clear();
user.SsoIdentities.Clear();
+ user.PasswordResetRequests.Clear();*/
}
}
}
diff --git a/Infrastructure.DataAccess/Infrastructure.DataAccess.csproj b/Infrastructure.DataAccess/Infrastructure.DataAccess.csproj
index 4171b2b856..515928ff77 100644
--- a/Infrastructure.DataAccess/Infrastructure.DataAccess.csproj
+++ b/Infrastructure.DataAccess/Infrastructure.DataAccess.csproj
@@ -860,6 +860,10 @@
202205181142292_Added_UIModuleCustomization.cs
+
+
+ 202205190801320_UserDelete.cs
+
@@ -1420,6 +1424,9 @@
202205181142292_Added_UIModuleCustomization.cs
+
+ 202205190801320_UserDelete.cs
+
diff --git a/Infrastructure.DataAccess/Migrations/202205190801320_UserDelete.Designer.cs b/Infrastructure.DataAccess/Migrations/202205190801320_UserDelete.Designer.cs
new file mode 100644
index 0000000000..d09cf0d6d4
--- /dev/null
+++ b/Infrastructure.DataAccess/Migrations/202205190801320_UserDelete.Designer.cs
@@ -0,0 +1,29 @@
+//
+namespace Infrastructure.DataAccess.Migrations
+{
+ using System.CodeDom.Compiler;
+ using System.Data.Entity.Migrations;
+ using System.Data.Entity.Migrations.Infrastructure;
+ using System.Resources;
+
+ [GeneratedCode("EntityFramework.Migrations", "6.4.4")]
+ public sealed partial class UserDelete : IMigrationMetadata
+ {
+ private readonly ResourceManager Resources = new ResourceManager(typeof(UserDelete));
+
+ string IMigrationMetadata.Id
+ {
+ get { return "202205190801320_UserDelete"; }
+ }
+
+ string IMigrationMetadata.Source
+ {
+ get { return null; }
+ }
+
+ string IMigrationMetadata.Target
+ {
+ get { return Resources.GetString("Target"); }
+ }
+ }
+}
diff --git a/Infrastructure.DataAccess/Migrations/202205190801320_UserDelete.cs b/Infrastructure.DataAccess/Migrations/202205190801320_UserDelete.cs
new file mode 100644
index 0000000000..b377275c4c
--- /dev/null
+++ b/Infrastructure.DataAccess/Migrations/202205190801320_UserDelete.cs
@@ -0,0 +1,22 @@
+namespace Infrastructure.DataAccess.Migrations
+{
+ using System;
+ using System.Data.Entity.Migrations;
+
+ public partial class UserDelete : DbMigration
+ {
+ public override void Up()
+ {
+ AddColumn("dbo.User", "EmailBeforeDeletion", c => c.String());
+ AddColumn("dbo.User", "DeletedDate", c => c.DateTime(precision: 7, storeType: "datetime2"));
+ AddColumn("dbo.User", "Deleted", c => c.Boolean(nullable: false));
+ }
+
+ public override void Down()
+ {
+ DropColumn("dbo.User", "Deleted");
+ DropColumn("dbo.User", "DeletedDate");
+ DropColumn("dbo.User", "EmailBeforeDeletion");
+ }
+ }
+}
diff --git a/Infrastructure.DataAccess/Migrations/202205190801320_UserDelete.resx b/Infrastructure.DataAccess/Migrations/202205190801320_UserDelete.resx
new file mode 100644
index 0000000000..f8c34db33d
--- /dev/null
+++ b/Infrastructure.DataAccess/Migrations/202205190801320_UserDelete.resx
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 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
+
+
+ dbo
+
+
\ No newline at end of file
diff --git a/Tests.Integration.Presentation.Web/Tests.Integration.Presentation.Web.csproj b/Tests.Integration.Presentation.Web/Tests.Integration.Presentation.Web.csproj
index b7ad6df1d8..985f60c9c5 100644
--- a/Tests.Integration.Presentation.Web/Tests.Integration.Presentation.Web.csproj
+++ b/Tests.Integration.Presentation.Web/Tests.Integration.Presentation.Web.csproj
@@ -173,6 +173,7 @@
+
@@ -191,10 +192,11 @@
+
-
+
diff --git a/Tests.Integration.Presentation.Web/Tools/External/Rights/RightsHelper.cs b/Tests.Integration.Presentation.Web/Tools/External/Rights/RightsHelper.cs
new file mode 100644
index 0000000000..7e16d40f63
--- /dev/null
+++ b/Tests.Integration.Presentation.Web/Tools/External/Rights/RightsHelper.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Text;
+using System.Threading.Tasks;
+using Core.DomainModel.Organization;
+using Tests.Integration.Presentation.Web.Tools.Model;
+using Xunit;
+
+namespace Tests.Integration.Presentation.Web.Tools.External.Rights
+{
+ public class RightsHelper
+ {
+ public static async Task AddUserRight(int userId, int orgId, RightsType rightsType, Cookie optionalLogin = null)
+ {
+ var cookie = optionalLogin ?? await HttpApi.GetCookieAsync(OrganizationRole.GlobalAdmin);
+
+ var roleDto = new RightDTO
+ {
+ UserId = userId,
+ RoleId = (int)OrganizationRole.LocalAdmin //role.ToString("G")
+ };
+
+ var response = await HttpApi.PostWithCookieAsync(TestEnvironment.CreateUrl(PrepareUrl(1, orgId, rightsType)), cookie, roleDto);
+ Assert.Equal(HttpStatusCode.Created, response.StatusCode);
+ }
+
+ private static string PrepareUrl(int id, int orgId, RightsType rightsType)
+ {
+ switch (rightsType)
+ {
+ case RightsType.ItContractRights:
+ return $"api/itcontractright/{id}?organizationId={orgId}";
+ case RightsType.ItProjectRights:
+ return $"api/itcontractright/{id}?organizationId={orgId}";
+ case RightsType.DprRights:
+ return $"api/itcontractright/{id}?organizationId={orgId}";
+ case RightsType.ItSystemRights:
+ return $"api/itsystemusageright/{id}?organizationId={orgId}";
+ case RightsType.OrganizationRights:
+ return $"api/itcontractright/{id}?organizationId={orgId}";
+ case RightsType.OrganizationUnitRights:
+ return $"api/organizationunitright/{id}?organizationId={orgId}";
+ default: throw new Exception();
+ }
+ }
+ }
+}
diff --git a/Tests.Integration.Presentation.Web/Tools/External/Rights/RightsType.cs b/Tests.Integration.Presentation.Web/Tools/External/Rights/RightsType.cs
new file mode 100644
index 0000000000..32c5e0c0ef
--- /dev/null
+++ b/Tests.Integration.Presentation.Web/Tools/External/Rights/RightsType.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tests.Integration.Presentation.Web.Tools.External.Rights
+{
+ public enum RightsType
+ {
+ ItContractRights = 1,
+ ItProjectRights = 2,
+ DprRights = 3,
+ ItSystemRights = 4,
+ OrganizationRights = 5,
+ OrganizationUnitRights = 6
+ }
+}
diff --git a/Tests.Integration.Presentation.Web/Tools/Model/RightDTO.cs b/Tests.Integration.Presentation.Web/Tools/Model/RightDTO.cs
new file mode 100644
index 0000000000..75f77548a6
--- /dev/null
+++ b/Tests.Integration.Presentation.Web/Tools/Model/RightDTO.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tests.Integration.Presentation.Web.Tools.Model
+{
+ public class RightDTO
+ {
+ public int UserId { get; set; }
+ public int RoleId{ get; set; }
+ }
+}
diff --git a/Tests.Integration.Presentation.Web/Tools/RightsHelper.cs b/Tests.Integration.Presentation.Web/Tools/RightsHelper.cs
deleted file mode 100644
index 12c00de386..0000000000
--- a/Tests.Integration.Presentation.Web/Tools/RightsHelper.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Tests.Integration.Presentation.Web.Tools
-{
- internal class RightsHelper
- {
- }
-}
diff --git a/Tests.Integration.Presentation.Web/Tools/UserHelper.cs b/Tests.Integration.Presentation.Web/Tools/UserHelper.cs
index e7d076cbcf..49f7adde08 100644
--- a/Tests.Integration.Presentation.Web/Tools/UserHelper.cs
+++ b/Tests.Integration.Presentation.Web/Tools/UserHelper.cs
@@ -37,5 +37,11 @@ public static async Task SendGetUsersWithCrossAccessAsync(C
var cookie = optionalLogin ?? await HttpApi.GetCookieAsync(OrganizationRole.GlobalAdmin);
return await HttpApi.GetWithCookieAsync(TestEnvironment.CreateUrl("api/user/with-cross-organization-permissions"), cookie);
}
+
+ public static async Task SendDeleteUserAsync(int userId, Cookie optionalLogin = null)
+ {
+ var cookie = optionalLogin ?? await HttpApi.GetCookieAsync(OrganizationRole.GlobalAdmin);
+ return await HttpApi.DeleteWithCookieAsync(TestEnvironment.CreateUrl($"api/user/{userId}"), cookie);
+ }
}
}
diff --git a/Tests.Integration.Presentation.Web/Users/UserTest.cs b/Tests.Integration.Presentation.Web/Users/UserTest.cs
index d82336da28..03833ac709 100644
--- a/Tests.Integration.Presentation.Web/Users/UserTest.cs
+++ b/Tests.Integration.Presentation.Web/Users/UserTest.cs
@@ -5,6 +5,8 @@
using System.Threading.Tasks;
using Presentation.Web.Models.API.V1;
using Tests.Integration.Presentation.Web.Tools;
+using Tests.Integration.Presentation.Web.Tools.External.Rights;
+using Tests.Integration.Presentation.Web.Tools.Internal.UI_Configuration;
using Tests.Toolkit.Patterns;
using Xunit;
@@ -122,7 +124,20 @@ public async Task Cannot_Get_Users_Where_User_Has_StakeHolder_Or_ApiAccess_If_No
[Fact]
public async Task Delete_User()
{
+ var (cookie, userId, organization) = await CreatePrerequisitesAsync();
+ await RightsHelper.AddUserRight(userId, organization.Id, RightsType.ItContractRights);
+ var res = await UserHelper.SendDeleteUserAsync(userId);
+
+ Assert.Equal(HttpStatusCode.OK, res.StatusCode);
+ }
+
+ private async Task<(Cookie loginCookie, int userId, OrganizationDTO organization)> CreatePrerequisitesAsync()
+ {
+ var organization = await CreateOrganizationAsync();
+ var (userId, _, loginCookie) =
+ await HttpApi.CreateUserAndLogin(UIConfigurationHelper.CreateEmail(), OrganizationRole.LocalAdmin, organization.Id);
+ return (loginCookie, userId, organization);
}
private async Task<(int userId, string userEmail, string orgName)> CreateStakeHolderUserInNewOrganizationAsync(bool hasApiAccess, bool hasStakeholderAccess)
diff --git a/Tests.Unit.Presentation.Web/Services/OrganizationServiceTest.cs b/Tests.Unit.Presentation.Web/Services/OrganizationServiceTest.cs
index 4f5cabebe8..a255f6096b 100644
--- a/Tests.Unit.Presentation.Web/Services/OrganizationServiceTest.cs
+++ b/Tests.Unit.Presentation.Web/Services/OrganizationServiceTest.cs
@@ -65,10 +65,9 @@ public OrganizationServiceTest()
Mock.Of(),
_transactionManager.Object,
_repositoryMock.Object,
+ Mock.Of(),
_orgUnitServiceMock.Object,
_domainEventsMock.Object);
- _orgUnitServiceMock.Object,
- Mock.Of());
}
[Fact]
From b9dd4fe339e7853269adb908cae47402b56f8491 Mon Sep 17 00:00:00 2001
From: Aleksander Naskret
Date: Thu, 19 May 2022 17:21:06 +0200
Subject: [PATCH 004/432] Added user roles deletion
---
.../Core.ApplicationServices.csproj | 1 +
.../Rights/UserRightsService.cs | 4 +-
.../Handlers/HandleUserBeingDeleted.cs | 117 ++++++++++++++++++
Core.ApplicationServices/UserService.cs | 10 +-
.../API/V1/OrganizationUnitController.cs | 1 -
Presentation.Web/Ninject/KernelBuilder.cs | 11 ++
.../Tools/External/Rights/RightsHelper.cs | 58 +++++++--
.../Tools/External/Rights/RightsType.cs | 6 +-
.../Tools/ItSystemUsageHelper.cs | 15 +++
.../Users/UserTest.cs | 8 +-
10 files changed, 202 insertions(+), 29 deletions(-)
create mode 100644 Core.ApplicationServices/UIConfiguration/Handlers/HandleUserBeingDeleted.cs
diff --git a/Core.ApplicationServices/Core.ApplicationServices.csproj b/Core.ApplicationServices/Core.ApplicationServices.csproj
index 04bf99f728..f0153de913 100644
--- a/Core.ApplicationServices/Core.ApplicationServices.csproj
+++ b/Core.ApplicationServices/Core.ApplicationServices.csproj
@@ -264,6 +264,7 @@
+
diff --git a/Core.ApplicationServices/Rights/UserRightsService.cs b/Core.ApplicationServices/Rights/UserRightsService.cs
index 5705e899c2..5b92b0baec 100644
--- a/Core.ApplicationServices/Rights/UserRightsService.cs
+++ b/Core.ApplicationServices/Rights/UserRightsService.cs
@@ -1,6 +1,5 @@
using Core.ApplicationServices.Authorization;
using Core.ApplicationServices.Organizations;
-using Core.DomainServices;
using System.Collections.Generic;
using System.Linq;
using Core.Abstractions.Types;
@@ -24,8 +23,7 @@ public UserRightsService(IUserService userService, IOrganizationService organiza
_organizationService = organizationService;
_authorizationContext = authorizationContext;
}
-
-
+
public Result, OperationError> GetUsersWithRoleAssignment(OrganizationRole role)
{
if (_authorizationContext.GetCrossOrganizationReadAccess() < CrossOrganizationDataReadAccessLevel.All)
diff --git a/Core.ApplicationServices/UIConfiguration/Handlers/HandleUserBeingDeleted.cs b/Core.ApplicationServices/UIConfiguration/Handlers/HandleUserBeingDeleted.cs
new file mode 100644
index 0000000000..c72d598607
--- /dev/null
+++ b/Core.ApplicationServices/UIConfiguration/Handlers/HandleUserBeingDeleted.cs
@@ -0,0 +1,117 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Policy;
+using Core.Abstractions.Extensions;
+using Core.ApplicationServices.GDPR;
+using Core.ApplicationServices.Organizations;
+using Core.DomainModel;
+using Core.DomainModel.Events;
+using Core.DomainModel.GDPR;
+using Core.DomainModel.ItContract;
+using Core.DomainModel.ItProject;
+using Core.DomainModel.ItSystem;
+using Core.DomainModel.ItSystemUsage;
+using Core.DomainModel.Organization;
+using Core.DomainServices.Role;
+
+namespace Core.ApplicationServices.UIConfiguration.Handlers
+{
+ public class HandleUserBeingDeleted : IDomainEventHandler>
+ {
+ private readonly IDataProcessingRegistrationApplicationService _dataProcessingRegistrationApplicationService;
+ private readonly IOrganizationRightsService _organizationRightsService;
+ private readonly IRoleAssignmentService _itContractRightService;
+ private readonly IRoleAssignmentService _itSystemRightService;
+ private readonly IRoleAssignmentService _itProjectRightService;
+ private readonly IRoleAssignmentService _organizationUnitRightService;
+
+
+ public HandleUserBeingDeleted(IDataProcessingRegistrationApplicationService dataProcessingRegistrationApplicationService,
+ IOrganizationRightsService organizationRightsService,
+ IRoleAssignmentService itContractRightService,
+ IRoleAssignmentService itSystemRightService,
+ IRoleAssignmentService itProjectRightService,
+ IRoleAssignmentService organizationUnitRightService)
+ {
+ _dataProcessingRegistrationApplicationService = dataProcessingRegistrationApplicationService;
+ _organizationRightsService = organizationRightsService;
+ _itContractRightService = itContractRightService;
+ _itSystemRightService = itSystemRightService;
+ _itProjectRightService = itProjectRightService;
+ _organizationUnitRightService = organizationUnitRightService;
+ }
+
+ public void Handle(EntityBeingDeletedEvent domainEvent)
+ {
+ var user = domainEvent.Entity;
+
+ ClearDataProcessingRegistrationRight(user);
+ ClearOrganizationRights(user);
+ ClearItContractRights(user);
+ ClearItSystemRights(user);
+ ClearItProjectRights(user);
+ ClearOrganizationUnitRights(user);
+ }
+
+ private void ClearDataProcessingRegistrationRight(User user)
+ {
+ var roles = user.DataProcessingRegistrationRights;
+ if (roles == null)
+ return;
+
+ roles.ToList().ForEach(x => _dataProcessingRegistrationApplicationService.RemoveRole(x.Id, x.RoleId, user.Id).ThrowOnFailure());
+ roles.Clear();
+ }
+
+ private void ClearOrganizationRights(User user)
+ {
+ var roles = user.OrganizationRights;
+ if (roles == null)
+ return;
+
+ roles.ToList().ForEach(x => _organizationRightsService.RemoveRole(x.OrganizationId, user.Id, x.Role).ThrowOnFailure());
+ roles.Clear();
+ }
+
+ private void ClearItContractRights(User user)
+ {
+ var roles = user.ItContractRights;
+ if (roles == null)
+ return;
+
+ roles.ToList().ForEach(x => _itContractRightService.RemoveRole(x.Object, x.RoleId, user.Id).ThrowOnFailure());
+ roles.Clear();
+ }
+
+ private void ClearItSystemRights(User user)
+ {
+ var roles = user.ItSystemRights;
+ if (roles == null)
+ return;
+
+ roles.ToList().ForEach(x => _itSystemRightService.RemoveRole(x.Object, x.RoleId, user.Id).ThrowOnFailure());
+ roles.Clear();
+ }
+
+ private void ClearItProjectRights(User user)
+ {
+ var roles = user.ItProjectRights;
+ if (roles == null)
+ return;
+
+ roles.ToList().ForEach(x => _itProjectRightService.RemoveRole(x.Object, x.RoleId, user.Id).ThrowOnFailure());
+ roles.Clear();
+ }
+
+ private void ClearOrganizationUnitRights(User user)
+ {
+ var roles = user.OrganizationUnitRights;
+ if (roles == null)
+ return;
+
+ roles.ToList().ForEach(x => _organizationUnitRightService.RemoveRole(x.Object, x.RoleId, user.Id).ThrowOnFailure());
+ roles.Clear();
+ }
+
+ }
+}
diff --git a/Core.ApplicationServices/UserService.cs b/Core.ApplicationServices/UserService.cs
index ab54407fdb..a7b6cc59ee 100644
--- a/Core.ApplicationServices/UserService.cs
+++ b/Core.ApplicationServices/UserService.cs
@@ -297,6 +297,8 @@ public Maybe DeleteUserFromKitos(Guid userUuid)
if (!_authorizationContext.AllowDelete(user))
return Maybe.Some(new OperationError(OperationFailure.Forbidden));
+ _domainEvents.Raise(new EntityBeingDeletedEvent(user));
+
Delete(user);
_domainEvents.Raise(new AccessRightsChanged(user.Id));
_userRepository.Save();
@@ -319,20 +321,12 @@ private void Delete(User user)
//TODO: Check cascades
- user.ItContractRights.Clear();
- /*
- user.ItProjectRights.Clear();
- user.DataProcessingRegistrationRights.Clear();
- user.ItSystemRights.Clear();
- user.OrganizationRights.Clear();
- user.OrganizationUnitRights.Clear();
user.ItProjectStatuses.Clear();
user.ResponsibleForCommunications.Clear();
user.HandoverParticipants.Clear();
user.LifeCycleTrackingEvents.Clear();
user.ResponsibleForRisks.Clear();
user.SsoIdentities.Clear();
- user.PasswordResetRequests.Clear();*/
}
}
}
diff --git a/Presentation.Web/Controllers/API/V1/OrganizationUnitController.cs b/Presentation.Web/Controllers/API/V1/OrganizationUnitController.cs
index d8b8145b5a..480869998f 100644
--- a/Presentation.Web/Controllers/API/V1/OrganizationUnitController.cs
+++ b/Presentation.Web/Controllers/API/V1/OrganizationUnitController.cs
@@ -10,7 +10,6 @@
using Core.DomainServices.Extensions;
using Newtonsoft.Json.Linq;
using Presentation.Web.Infrastructure.Attributes;
-using Presentation.Web.Models;
using Presentation.Web.Models.API.V1;
namespace Presentation.Web.Controllers.API.V1
diff --git a/Presentation.Web/Ninject/KernelBuilder.cs b/Presentation.Web/Ninject/KernelBuilder.cs
index 7b9a59f7b2..fe28005ca0 100644
--- a/Presentation.Web/Ninject/KernelBuilder.cs
+++ b/Presentation.Web/Ninject/KernelBuilder.cs
@@ -111,6 +111,7 @@
using Core.ApplicationServices.Organizations.Handlers;
using Core.ApplicationServices.Tracking;
using Core.ApplicationServices.UIConfiguration;
+using Core.ApplicationServices.UIConfiguration.Handlers;
using Core.DomainServices.Repositories.UICustomization;
using Core.DomainServices.Tracking;
using Presentation.Web.Controllers.API.V2.External.ItSystems.Mapping;
@@ -250,6 +251,8 @@ public void RegisterServices(IKernel kernel)
RegisterRoleAssignmentService(kernel);
RegisterRoleAssignmentService(kernel);
RegisterRoleAssignmentService(kernel);
+ RegisterRoleAssignmentService(kernel);
+ RegisterRoleAssignmentService(kernel);
//MembershipProvider & Roleprovider injection - see ProviderInitializationHttpModule.cs
kernel.Bind().ToMethod(ctx => Membership.Provider);
@@ -391,6 +394,8 @@ private void RegisterDomainEventsEngine(IKernel kernel)
//Organization
RegisterDomainEvent, HandleOrganizationBeingDeleted>(kernel);
//TODO: Read models where it is involved must be scheduled for rebuild.. do it in a different handler (one of the read model handlers)
+
+ RegisterDomainEvent, HandleUserBeingDeleted>(kernel);
}
private void RegisterDomainEvent(IKernel kernel)
@@ -458,6 +463,12 @@ private void RegisterOptions(IKernel kernel)
RegisterOptionsService(kernel);
+ //IT-Project
+ RegisterOptionsService(kernel);
+
+ //OrganizationUnit
+ RegisterOptionsService(kernel);
+
//Attached options services
kernel.Bind>().ToMethod(ctx =>
new AttachedOptionsAssignmentService(OptionType.REGISTERTYPEDATA,
diff --git a/Tests.Integration.Presentation.Web/Tools/External/Rights/RightsHelper.cs b/Tests.Integration.Presentation.Web/Tools/External/Rights/RightsHelper.cs
index 7e16d40f63..0f42f3675c 100644
--- a/Tests.Integration.Presentation.Web/Tools/External/Rights/RightsHelper.cs
+++ b/Tests.Integration.Presentation.Web/Tools/External/Rights/RightsHelper.cs
@@ -4,6 +4,7 @@
using System.Net;
using System.Text;
using System.Threading.Tasks;
+using Core.DomainModel;
using Core.DomainModel.Organization;
using Tests.Integration.Presentation.Web.Tools.Model;
using Xunit;
@@ -12,36 +13,69 @@ namespace Tests.Integration.Presentation.Web.Tools.External.Rights
{
public class RightsHelper
{
- public static async Task AddUserRight(int userId, int orgId, RightsType rightsType, Cookie optionalLogin = null)
+ public static async Task AddUserRole(int userId, int orgId, RightsType rightsType, string name, Cookie optionalLogin = null)
{
var cookie = optionalLogin ?? await HttpApi.GetCookieAsync(OrganizationRole.GlobalAdmin);
-
+
var roleDto = new RightDTO
{
UserId = userId,
RoleId = (int)OrganizationRole.LocalAdmin //role.ToString("G")
};
- var response = await HttpApi.PostWithCookieAsync(TestEnvironment.CreateUrl(PrepareUrl(1, orgId, rightsType)), cookie, roleDto);
+ var url = TestEnvironment.CreateUrl(await PrepareUrl(orgId, name, rightsType));
+ var response = await HttpApi.PostWithCookieAsync(url, cookie, roleDto);
Assert.Equal(HttpStatusCode.Created, response.StatusCode);
}
- private static string PrepareUrl(int id, int orgId, RightsType rightsType)
+ public static async Task AddOrganizationRoleToUser(int userId, int orgId, Cookie optionalLogin = null)
+ {
+ var cookie = optionalLogin ?? await HttpApi.GetCookieAsync(OrganizationRole.GlobalAdmin);
+
+ var roleDto = new OrgRightDTO
+ {
+ UserId = userId,
+ Role = OrganizationRole.LocalAdmin.ToString("G")
+ };
+
+ var response = await HttpApi.PostWithCookieAsync(TestEnvironment.CreateUrl($"odata/organizations({orgId})/Rights"), cookie, roleDto);
+ Assert.Equal(HttpStatusCode.Created, response.StatusCode);
+ }
+
+ public static async Task AddDprRoleToUser(int userId, int orgId, string name, Cookie optionalLogin = null)
+ {
+ //var cookie = optionalLogin ?? await HttpApi.GetCookieAsync(OrganizationRole.GlobalAdmin);
+ var dpr= await DataProcessingRegistrationHelper.CreateAsync(orgId, name);
+
+ var roles = await DataProcessingRegistrationHelper.GetAvailableRolesAsync(dpr.Id);
+ var roleDtos = roles.ToList();
+ Assert.True(roleDtos.Any());
+
+ var singleRole = roleDtos.FirstOrDefault();
+ Assert.NotNull(singleRole);
+
+ var response = await DataProcessingRegistrationHelper.SendAssignRoleRequestAsync(dpr.Id, singleRole.Id, userId);
+
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ }
+
+ private static async Task PrepareUrl(int orgId, string name, RightsType rightsType)
{
switch (rightsType)
{
case RightsType.ItContractRights:
- return $"api/itcontractright/{id}?organizationId={orgId}";
+ var contract = await ItContractHelper.CreateContract(name, orgId);
+ return $"api/itcontractright/{contract.Id}?organizationId={orgId}";
case RightsType.ItProjectRights:
- return $"api/itcontractright/{id}?organizationId={orgId}";
- case RightsType.DprRights:
- return $"api/itcontractright/{id}?organizationId={orgId}";
+ var project= await ItProjectHelper.CreateProject(name, orgId);
+ return $"api/itprojectright/{project.Id}?organizationId={orgId}";
case RightsType.ItSystemRights:
- return $"api/itsystemusageright/{id}?organizationId={orgId}";
- case RightsType.OrganizationRights:
- return $"api/itcontractright/{id}?organizationId={orgId}";
+ var itSystem = await ItSystemHelper.CreateItSystemInOrganizationAsync(name, orgId, AccessModifier.Local);
+ var itSystemUsage = await ItSystemUsageHelper.CreateItSystemUsage(orgId, itSystem.Id);
+ return $"api/itSystemUsageRights/{itSystemUsage.Id}?organizationId={orgId}";
case RightsType.OrganizationUnitRights:
- return $"api/organizationunitright/{id}?organizationId={orgId}";
+ //TODO: Create org unit
+ return $"api/organizationunitrights/{orgId}?organizationId={orgId}";
default: throw new Exception();
}
}
diff --git a/Tests.Integration.Presentation.Web/Tools/External/Rights/RightsType.cs b/Tests.Integration.Presentation.Web/Tools/External/Rights/RightsType.cs
index 32c5e0c0ef..3ef35b2cd7 100644
--- a/Tests.Integration.Presentation.Web/Tools/External/Rights/RightsType.cs
+++ b/Tests.Integration.Presentation.Web/Tools/External/Rights/RightsType.cs
@@ -10,9 +10,7 @@ public enum RightsType
{
ItContractRights = 1,
ItProjectRights = 2,
- DprRights = 3,
- ItSystemRights = 4,
- OrganizationRights = 5,
- OrganizationUnitRights = 6
+ ItSystemRights = 3,
+ OrganizationUnitRights = 4
}
}
diff --git a/Tests.Integration.Presentation.Web/Tools/ItSystemUsageHelper.cs b/Tests.Integration.Presentation.Web/Tools/ItSystemUsageHelper.cs
index 77de3d210b..7ed3dfcf8a 100644
--- a/Tests.Integration.Presentation.Web/Tools/ItSystemUsageHelper.cs
+++ b/Tests.Integration.Presentation.Web/Tools/ItSystemUsageHelper.cs
@@ -226,5 +226,20 @@ public static async Task SetIsHoldingDocumentRequestAsync(i
Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
return response;
}
+
+ public static async Task CreateItSystemUsage(int orgId, int itSystemId, Cookie optionalLogin = null)
+ {
+ var cookie = optionalLogin ?? await HttpApi.GetCookieAsync(OrganizationRole.GlobalAdmin);
+ var url = TestEnvironment.CreateUrl($"api/itSystemUsage");
+ var body = new
+ {
+ OrganizationId = orgId,
+ ItSystemId = itSystemId
+ };
+
+ using var response = await HttpApi.PostWithCookieAsync(url, cookie, body);
+ Assert.Equal(HttpStatusCode.Created, response.StatusCode);
+ return await response.ReadResponseBodyAsKitosApiResponseAsync();
+ }
}
}
\ No newline at end of file
diff --git a/Tests.Integration.Presentation.Web/Users/UserTest.cs b/Tests.Integration.Presentation.Web/Users/UserTest.cs
index 03833ac709..4ec0c80079 100644
--- a/Tests.Integration.Presentation.Web/Users/UserTest.cs
+++ b/Tests.Integration.Presentation.Web/Users/UserTest.cs
@@ -125,8 +125,14 @@ public async Task Cannot_Get_Users_Where_User_Has_StakeHolder_Or_ApiAccess_If_No
public async Task Delete_User()
{
var (cookie, userId, organization) = await CreatePrerequisitesAsync();
+ var name = A();
+
+ await RightsHelper.AddUserRole(userId, organization.Id, RightsType.ItContractRights, name);
+ await RightsHelper.AddUserRole(userId, organization.Id, RightsType.ItProjectRights, name);
+ await RightsHelper.AddUserRole(userId, organization.Id, RightsType.ItSystemRights, name);
+ await RightsHelper.AddDprRoleToUser(userId, organization.Id, name);
+ await RightsHelper.AddOrganizationRoleToUser(userId, organization.Id);
- await RightsHelper.AddUserRight(userId, organization.Id, RightsType.ItContractRights);
var res = await UserHelper.SendDeleteUserAsync(userId);
Assert.Equal(HttpStatusCode.OK, res.StatusCode);
From dd94766842174845d0c72866b8f8b1e53caac7c5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Morten=20Remb=C3=B8l=20Jacobsen?=
Date: Fri, 20 May 2022 12:53:15 +0200
Subject: [PATCH 005/432] Fixed incorrect order of the access rights changed
event
---
Core.ApplicationServices/OrganizationRoleService.cs | 3 ++-
.../Organizations/OrganizationRightsService.cs | 4 ++--
Presentation.Web/Controllers/API/V1/GlobalAdminController.cs | 4 ++--
3 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/Core.ApplicationServices/OrganizationRoleService.cs b/Core.ApplicationServices/OrganizationRoleService.cs
index 1c2679538c..b06feb9e30 100644
--- a/Core.ApplicationServices/OrganizationRoleService.cs
+++ b/Core.ApplicationServices/OrganizationRoleService.cs
@@ -29,8 +29,9 @@ private OrganizationRight AddOrganizationRoleToUser(User user, Organization orga
User = user,
Role = organizationRole,
});
- _domainEvents.Raise(new AccessRightsChanged(user.Id));
+
_organizationRights.Save();
+ _domainEvents.Raise(new AccessRightsChanged(user.Id));
return result;
}
diff --git a/Core.ApplicationServices/Organizations/OrganizationRightsService.cs b/Core.ApplicationServices/Organizations/OrganizationRightsService.cs
index 6de584d62f..e2abb4e685 100644
--- a/Core.ApplicationServices/Organizations/OrganizationRightsService.cs
+++ b/Core.ApplicationServices/Organizations/OrganizationRightsService.cs
@@ -43,8 +43,8 @@ public Result AssignRole(int organizationId
}
right = _organizationRightRepository.Insert(right);
- _domainEvents.Raise(new AccessRightsChanged(userId));
_organizationRightRepository.Save();
+ _domainEvents.Raise(new AccessRightsChanged(userId));
return right;
}
@@ -85,8 +85,8 @@ private Result RemoveRight(OrganizationRigh
}
_organizationRightRepository.DeleteByKey(right.Id);
- _domainEvents.Raise(new AccessRightsChanged(right.UserId));
_organizationRightRepository.Save();
+ _domainEvents.Raise(new AccessRightsChanged(right.UserId));
return right;
}
diff --git a/Presentation.Web/Controllers/API/V1/GlobalAdminController.cs b/Presentation.Web/Controllers/API/V1/GlobalAdminController.cs
index 285b615043..8c4e888289 100644
--- a/Presentation.Web/Controllers/API/V1/GlobalAdminController.cs
+++ b/Presentation.Web/Controllers/API/V1/GlobalAdminController.cs
@@ -71,8 +71,8 @@ public HttpResponseMessage Post(CreateGlobalAdminDTO dto)
}
user.IsGlobalAdmin = true;
- _domainEvents.Raise(new AccessRightsChanged(dto.UserId));
_userRepository.Save();
+ _domainEvents.Raise(new AccessRightsChanged(dto.UserId));
var outDto = Mapper.Map(user);
@@ -99,8 +99,8 @@ public HttpResponseMessage Delete([FromUri] int userId)
var user = _userRepository.GetByKey(userId);
user.IsGlobalAdmin = false;
- _domainEvents.Raise(new AccessRightsChanged(userId));
_userRepository.Save();
+ _domainEvents.Raise(new AccessRightsChanged(userId));
var outDto = Mapper.Map(user);
From 6b3798f2e525328c33c24fa9b4d978a6982c9228 Mon Sep 17 00:00:00 2001
From: Aleksander Naskret
Date: Fri, 20 May 2022 12:57:12 +0200
Subject: [PATCH 006/432] Delete user api tests
---
.../Handlers/HandleUserBeingDeleted.cs | 2 +-
Core.ApplicationServices/UserService.cs | 11 ++++-
.../API/V1/OData/BaseEntityController.cs | 2 +-
.../Tests.Integration.Presentation.Web.csproj | 1 +
.../Tools/External/Rights/RightsHelper.cs | 4 +-
.../Tools/OrganizationUnitHelper.cs | 49 +++++++++++++++++++
.../Tools/UserHelper.cs | 16 ++++++
.../Users/UserTest.cs | 26 ++++++++--
8 files changed, 102 insertions(+), 9 deletions(-)
create mode 100644 Tests.Integration.Presentation.Web/Tools/OrganizationUnitHelper.cs
diff --git a/Core.ApplicationServices/UIConfiguration/Handlers/HandleUserBeingDeleted.cs b/Core.ApplicationServices/UIConfiguration/Handlers/HandleUserBeingDeleted.cs
index c72d598607..8bc55025c1 100644
--- a/Core.ApplicationServices/UIConfiguration/Handlers/HandleUserBeingDeleted.cs
+++ b/Core.ApplicationServices/UIConfiguration/Handlers/HandleUserBeingDeleted.cs
@@ -59,7 +59,7 @@ private void ClearDataProcessingRegistrationRight(User user)
if (roles == null)
return;
- roles.ToList().ForEach(x => _dataProcessingRegistrationApplicationService.RemoveRole(x.Id, x.RoleId, user.Id).ThrowOnFailure());
+ roles.ToList().ForEach(x => _dataProcessingRegistrationApplicationService.RemoveRole(x.ObjectId, x.RoleId, user.Id).ThrowOnFailure());
roles.Clear();
}
diff --git a/Core.ApplicationServices/UserService.cs b/Core.ApplicationServices/UserService.cs
index a7b6cc59ee..c03313a4b3 100644
--- a/Core.ApplicationServices/UserService.cs
+++ b/Core.ApplicationServices/UserService.cs
@@ -16,6 +16,7 @@
using Core.DomainModel.Organization.DomainEvents;
using Infrastructure.Services.Cryptography;
using Core.DomainServices.Authorization;
+using Core.DomainServices.Context;
using Core.DomainServices.Extensions;
using Core.DomainServices.Queries;
using Infrastructure.Services.DataAccess;
@@ -41,6 +42,7 @@ public class UserService : IUserService
private readonly IAuthorizationContext _authorizationContext;
private readonly IDomainEvents _domainEvents;
private readonly SHA256Managed _crypt;
+ private readonly Maybe _activeUserIdContext;
private static readonly RNGCryptoServiceProvider rngCsp = new();
private const string KitosManualsLink = "https://os2.eu/Kitosvejledning";
@@ -58,7 +60,8 @@ public UserService(TimeSpan ttl,
IDomainEvents domainEvents,
IUserRepository repository,
IOrganizationService organizationService,
- ITransactionManager transactionManager)
+ ITransactionManager transactionManager,
+ Maybe activeUserIdContext)
{
_ttl = ttl;
_baseUrl = baseUrl;
@@ -75,6 +78,7 @@ public UserService(TimeSpan ttl,
_repository = repository;
_organizationService = organizationService;
_transactionManager = transactionManager;
+ _activeUserIdContext = activeUserIdContext;
_crypt = new SHA256Managed();
if (useDefaultUserPassword && string.IsNullOrWhiteSpace(defaultUserPassword))
{
@@ -288,11 +292,15 @@ public Result GetUserInOrganization(Guid organizationUuid,
public Maybe DeleteUserFromKitos(Guid userUuid)
{
+
using var transaction = _transactionManager.Begin();
var user = _userRepository.AsQueryable().ByUuid(userUuid);
if (user == null)
return Maybe.Some(new OperationError(OperationFailure.NotFound));
+ if(_activeUserIdContext.GetValueOrDefault().ActiveUserId == user.Id)
+ return Maybe.Some(new OperationError("You cannot delete an user you are currently logged in as", OperationFailure.Forbidden));
+
if (!_authorizationContext.AllowDelete(user))
return Maybe.Some(new OperationError(OperationFailure.Forbidden));
@@ -315,6 +323,7 @@ private void Delete(User user)
user.PhoneNumber = null;
user.LastName = $"{(user.LastName ?? "").TrimEnd()} (SLETTET)";
user.DeletedDate = DateTime.Now;
+ user.Deleted = true;
user.IsGlobalAdmin = false;
user.HasApiAccess = false;
user.HasStakeHolderAccess = false;
diff --git a/Presentation.Web/Controllers/API/V1/OData/BaseEntityController.cs b/Presentation.Web/Controllers/API/V1/OData/BaseEntityController.cs
index 080afebfa7..f81d6444b5 100644
--- a/Presentation.Web/Controllers/API/V1/OData/BaseEntityController.cs
+++ b/Presentation.Web/Controllers/API/V1/OData/BaseEntityController.cs
@@ -77,7 +77,7 @@ public override IHttpActionResult Get(int key)
{
return Forbidden();
}
-
+
return Ok(SingleResult.Create(result));
}
diff --git a/Tests.Integration.Presentation.Web/Tests.Integration.Presentation.Web.csproj b/Tests.Integration.Presentation.Web/Tests.Integration.Presentation.Web.csproj
index 985f60c9c5..e35a7a9e8a 100644
--- a/Tests.Integration.Presentation.Web/Tests.Integration.Presentation.Web.csproj
+++ b/Tests.Integration.Presentation.Web/Tests.Integration.Presentation.Web.csproj
@@ -195,6 +195,7 @@
+
diff --git a/Tests.Integration.Presentation.Web/Tools/External/Rights/RightsHelper.cs b/Tests.Integration.Presentation.Web/Tools/External/Rights/RightsHelper.cs
index 0f42f3675c..87c1e4929f 100644
--- a/Tests.Integration.Presentation.Web/Tools/External/Rights/RightsHelper.cs
+++ b/Tests.Integration.Presentation.Web/Tools/External/Rights/RightsHelper.cs
@@ -6,6 +6,7 @@
using System.Threading.Tasks;
using Core.DomainModel;
using Core.DomainModel.Organization;
+using Presentation.Web.Models.API.V1;
using Tests.Integration.Presentation.Web.Tools.Model;
using Xunit;
@@ -75,7 +76,8 @@ private static async Task PrepareUrl(int orgId, string name, RightsType
return $"api/itSystemUsageRights/{itSystemUsage.Id}?organizationId={orgId}";
case RightsType.OrganizationUnitRights:
//TODO: Create org unit
- return $"api/organizationunitrights/{orgId}?organizationId={orgId}";
+ var orgUnit = OrganizationUnitHelper.GetOrganizationUnits(orgId);
+ return $"api/organizationunitright/{orgUnit.Result.Id}?organizationId={orgId}";
default: throw new Exception();
}
}
diff --git a/Tests.Integration.Presentation.Web/Tools/OrganizationUnitHelper.cs b/Tests.Integration.Presentation.Web/Tools/OrganizationUnitHelper.cs
new file mode 100644
index 0000000000..5433371e27
--- /dev/null
+++ b/Tests.Integration.Presentation.Web/Tools/OrganizationUnitHelper.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Text;
+using System.Threading.Tasks;
+using Core.DomainModel.Organization;
+using Presentation.Web.Models.API.V1;
+using Xunit;
+
+namespace Tests.Integration.Presentation.Web.Tools
+{
+ public class OrganizationUnitHelper
+ {
+ public static async Task CreateOrganizationUnit(int orgId, string name, Cookie optionalLogin = null)
+ {
+ var cookie = await HttpApi.GetCookieAsync(OrganizationRole.GlobalAdmin);
+ var orgUnitDto = new OrgUnitDTO()
+ {
+ Id = 0,
+ Name = name,
+ OrganizationId = orgId,
+ LocalId = "test",
+ ObjectOwnerName = "test",
+ ObjectOwnerLastName = "last name",
+ LastChangedByUserId = 1,
+ LastChanged = DateTime.Now,
+ Uuid = Guid.NewGuid()
+ };
+ var orgUnitUrl = TestEnvironment.CreateUrl($"odata/OrganizationUnit");
+
+ var response = await HttpApi.PostWithCookieAsync(orgUnitUrl, cookie, orgUnitDto);
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+
+ return await response.ReadResponseBodyAsAsync();
+ }
+
+ public static async Task GetOrganizationUnits(int orgId, Cookie optionalLogin = null)
+ {
+ var cookie = await HttpApi.GetCookieAsync(OrganizationRole.GlobalAdmin);
+ var orgUnitUrl = TestEnvironment.CreateUrl($"api/OrganizationUnit?organization={orgId}");
+
+ var response = await HttpApi.GetWithCookieAsync(orgUnitUrl, cookie);
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+
+ return await response.ReadResponseBodyAsKitosApiResponseAsync();
+ }
+ }
+}
diff --git a/Tests.Integration.Presentation.Web/Tools/UserHelper.cs b/Tests.Integration.Presentation.Web/Tools/UserHelper.cs
index 49f7adde08..93a9810cda 100644
--- a/Tests.Integration.Presentation.Web/Tools/UserHelper.cs
+++ b/Tests.Integration.Presentation.Web/Tools/UserHelper.cs
@@ -3,6 +3,8 @@
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
+using Core.DomainModel;
+using Presentation.Web.Models.API.V1;
using Presentation.Web.Models.API.V1.Users;
using Xunit;
@@ -38,6 +40,20 @@ public static async Task SendGetUsersWithCrossAccessAsync(C
return await HttpApi.GetWithCookieAsync(TestEnvironment.CreateUrl("api/user/with-cross-organization-permissions"), cookie);
}
+ public static async Task GetUserByIdAsync(int id, Cookie optionalLogin = null)
+ {
+ using var response = await SendGetUserByIdAsync(id, optionalLogin);
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+
+ return await response.ReadResponseBodyAsAsync();
+ }
+
+ public static async Task SendGetUserByIdAsync(int id, Cookie optionalLogin = null)
+ {
+ var cookie = optionalLogin ?? await HttpApi.GetCookieAsync(OrganizationRole.GlobalAdmin);
+ return await HttpApi.GetWithCookieAsync(TestEnvironment.CreateUrl($"odata/users({id})"), cookie);
+ }
+
public static async Task SendDeleteUserAsync(int userId, Cookie optionalLogin = null)
{
var cookie = optionalLogin ?? await HttpApi.GetCookieAsync(OrganizationRole.GlobalAdmin);
diff --git a/Tests.Integration.Presentation.Web/Users/UserTest.cs b/Tests.Integration.Presentation.Web/Users/UserTest.cs
index 4ec0c80079..22802eda45 100644
--- a/Tests.Integration.Presentation.Web/Users/UserTest.cs
+++ b/Tests.Integration.Presentation.Web/Users/UserTest.cs
@@ -124,25 +124,41 @@ public async Task Cannot_Get_Users_Where_User_Has_StakeHolder_Or_ApiAccess_If_No
[Fact]
public async Task Delete_User()
{
- var (cookie, userId, organization) = await CreatePrerequisitesAsync();
+ var userRole = OrganizationRole.LocalAdmin;
+
+ var (_, userId, organization) = await CreatePrerequisitesAsync(userRole);
var name = A();
+ await RightsHelper.AddUserRole(userId, organization.Id, RightsType.OrganizationUnitRights, name);
await RightsHelper.AddUserRole(userId, organization.Id, RightsType.ItContractRights, name);
await RightsHelper.AddUserRole(userId, organization.Id, RightsType.ItProjectRights, name);
await RightsHelper.AddUserRole(userId, organization.Id, RightsType.ItSystemRights, name);
await RightsHelper.AddDprRoleToUser(userId, organization.Id, name);
await RightsHelper.AddOrganizationRoleToUser(userId, organization.Id);
- var res = await UserHelper.SendDeleteUserAsync(userId);
+ var deleteResponse = await UserHelper.SendDeleteUserAsync(userId);
+ Assert.Equal(HttpStatusCode.OK, deleteResponse.StatusCode);
+
+ var getDeletedUserResponse = await UserHelper.GetUserByIdAsync(userId);
+ Assert.True(getDeletedUserResponse.Deleted);
+ }
+
+ [Fact]
+ public async Task Delete_User_Returns_Forbidden_When_User_Tries_To_Delete_Himself()
+ {
+ var userRole = OrganizationRole.GlobalAdmin;
+
+ var (cookie, userId, _) = await CreatePrerequisitesAsync(userRole);
- Assert.Equal(HttpStatusCode.OK, res.StatusCode);
+ var deleteResponse = await UserHelper.SendDeleteUserAsync(userId, cookie);
+ Assert.Equal(HttpStatusCode.Forbidden, deleteResponse.StatusCode);
}
- private async Task<(Cookie loginCookie, int userId, OrganizationDTO organization)> CreatePrerequisitesAsync()
+ private async Task<(Cookie loginCookie, int userId, OrganizationDTO organization)> CreatePrerequisitesAsync(OrganizationRole role)
{
var organization = await CreateOrganizationAsync();
var (userId, _, loginCookie) =
- await HttpApi.CreateUserAndLogin(UIConfigurationHelper.CreateEmail(), OrganizationRole.LocalAdmin, organization.Id);
+ await HttpApi.CreateUserAndLogin(UIConfigurationHelper.CreateEmail(), role, organization.Id);
return (loginCookie, userId, organization);
}
From 08e2a6ca6c3a7898ec5438f0679f66fcdd2495f5 Mon Sep 17 00:00:00 2001
From: Aleksander Naskret
Date: Fri, 20 May 2022 14:33:13 +0200
Subject: [PATCH 007/432] Added if user check is different than user to be
deleted
---
Core.ApplicationServices/UserService.cs | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/Core.ApplicationServices/UserService.cs b/Core.ApplicationServices/UserService.cs
index c03313a4b3..942d74e8cf 100644
--- a/Core.ApplicationServices/UserService.cs
+++ b/Core.ApplicationServices/UserService.cs
@@ -292,19 +292,18 @@ public Result GetUserInOrganization(Guid organizationUuid,
public Maybe DeleteUserFromKitos(Guid userUuid)
{
-
using var transaction = _transactionManager.Begin();
var user = _userRepository.AsQueryable().ByUuid(userUuid);
if (user == null)
return Maybe.Some(new OperationError(OperationFailure.NotFound));
if(_activeUserIdContext.GetValueOrDefault().ActiveUserId == user.Id)
- return Maybe.Some(new OperationError("You cannot delete an user you are currently logged in as", OperationFailure.Forbidden));
+ return Maybe.Some(new OperationError("You cannot delete a user you are currently logged in as", OperationFailure.Forbidden));
if (!_authorizationContext.AllowDelete(user))
return Maybe.Some(new OperationError(OperationFailure.Forbidden));
-
+
_domainEvents.Raise(new EntityBeingDeletedEvent(user));
Delete(user);
From 17444aa4eed9e7ec8944374bd959c7d4defec839 Mon Sep 17 00:00:00 2001
From: Aleksander Naskret
Date: Fri, 20 May 2022 14:37:58 +0200
Subject: [PATCH 008/432] Unit test fix
---
.../ApplicationServices/UserServiceTest.cs | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/Tests.Unit.Core.ApplicationServices/ApplicationServices/UserServiceTest.cs b/Tests.Unit.Core.ApplicationServices/ApplicationServices/UserServiceTest.cs
index 4e147c109a..fbe1e1fc06 100644
--- a/Tests.Unit.Core.ApplicationServices/ApplicationServices/UserServiceTest.cs
+++ b/Tests.Unit.Core.ApplicationServices/ApplicationServices/UserServiceTest.cs
@@ -12,6 +12,7 @@
using Core.Abstractions.Types;
using Core.ApplicationServices.Organizations;
using Core.DomainModel.Events;
+using Core.DomainServices.Context;
using Core.DomainServices.Queries;
using Infrastructure.Services.DataAccess;
using Tests.Toolkit.Patterns;
@@ -32,6 +33,7 @@ public class UserServiceTest : WithAutoFixture
private readonly Mock _authorizationContextMock;
private readonly Mock _domainEventsMock;
private readonly Mock _organizationServiceMock;
+ private readonly Mock> _activeUserIdContextMock;
public UserServiceTest()
{
@@ -43,6 +45,7 @@ public UserServiceTest()
_cryptoServiceMock = new Mock();
_authorizationContextMock = new Mock();
_domainEventsMock = new Mock();
+ _activeUserIdContextMock = new Mock>();
_organizationServiceMock = new Mock();
_sut = new UserService(
@@ -60,7 +63,8 @@ public UserServiceTest()
_domainEventsMock.Object,
_repositoryMock.Object,
_organizationServiceMock.Object,
- Mock.Of());
+ Mock.Of(),
+ _activeUserIdContextMock.Object);
}
[Fact]
From 730694e1e15d7ed74be6a83c5321640aa28b2256 Mon Sep 17 00:00:00 2001
From: Aleksander Naskret
Date: Fri, 20 May 2022 14:37:58 +0200
Subject: [PATCH 009/432] Unit test fix
---
Core.DomainServices/Context/ActiveUserIdContext.cs | 5 +++++
.../ApplicationServices/UserServiceTest.cs | 6 +++++-
2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/Core.DomainServices/Context/ActiveUserIdContext.cs b/Core.DomainServices/Context/ActiveUserIdContext.cs
index 0df1b2df95..27f7c11c6a 100644
--- a/Core.DomainServices/Context/ActiveUserIdContext.cs
+++ b/Core.DomainServices/Context/ActiveUserIdContext.cs
@@ -8,5 +8,10 @@ public ActiveUserIdContext(int activeUserId)
{
ActiveUserId = activeUserId;
}
+
+ public ActiveUserIdContext()
+ {
+
+ }
}
}
diff --git a/Tests.Unit.Core.ApplicationServices/ApplicationServices/UserServiceTest.cs b/Tests.Unit.Core.ApplicationServices/ApplicationServices/UserServiceTest.cs
index 4e147c109a..799072abc9 100644
--- a/Tests.Unit.Core.ApplicationServices/ApplicationServices/UserServiceTest.cs
+++ b/Tests.Unit.Core.ApplicationServices/ApplicationServices/UserServiceTest.cs
@@ -12,6 +12,7 @@
using Core.Abstractions.Types;
using Core.ApplicationServices.Organizations;
using Core.DomainModel.Events;
+using Core.DomainServices.Context;
using Core.DomainServices.Queries;
using Infrastructure.Services.DataAccess;
using Tests.Toolkit.Patterns;
@@ -32,6 +33,7 @@ public class UserServiceTest : WithAutoFixture
private readonly Mock _authorizationContextMock;
private readonly Mock _domainEventsMock;
private readonly Mock _organizationServiceMock;
+ private readonly Mock _activeUserIdContextMock;
public UserServiceTest()
{
@@ -43,6 +45,7 @@ public UserServiceTest()
_cryptoServiceMock = new Mock();
_authorizationContextMock = new Mock();
_domainEventsMock = new Mock();
+ _activeUserIdContextMock = new Mock();
_organizationServiceMock = new Mock();
_sut = new UserService(
@@ -60,7 +63,8 @@ public UserServiceTest()
_domainEventsMock.Object,
_repositoryMock.Object,
_organizationServiceMock.Object,
- Mock.Of());
+ Mock.Of(),
+ _activeUserIdContextMock.Object);
}
[Fact]
From e012188828573bda0d274438671895450bc8b8eb Mon Sep 17 00:00:00 2001
From: Aleksander Naskret
Date: Fri, 20 May 2022 15:15:35 +0200
Subject: [PATCH 010/432] WIP unit test fix
---
Core.ApplicationServices/UserService.cs | 1 -
.../Services/OrganizationServiceTest.cs | 17 ++++++++++++++++-
2 files changed, 16 insertions(+), 2 deletions(-)
diff --git a/Core.ApplicationServices/UserService.cs b/Core.ApplicationServices/UserService.cs
index 942d74e8cf..0f4d4f83c0 100644
--- a/Core.ApplicationServices/UserService.cs
+++ b/Core.ApplicationServices/UserService.cs
@@ -327,7 +327,6 @@ private void Delete(User user)
user.HasApiAccess = false;
user.HasStakeHolderAccess = false;
-
//TODO: Check cascades
user.ItProjectStatuses.Clear();
user.ResponsibleForCommunications.Clear();
diff --git a/Tests.Unit.Presentation.Web/Services/OrganizationServiceTest.cs b/Tests.Unit.Presentation.Web/Services/OrganizationServiceTest.cs
index a255f6096b..83447bf02f 100644
--- a/Tests.Unit.Presentation.Web/Services/OrganizationServiceTest.cs
+++ b/Tests.Unit.Presentation.Web/Services/OrganizationServiceTest.cs
@@ -41,6 +41,7 @@ public class OrganizationServiceTest : WithAutoFixture
private readonly Mock _userContext;
private readonly Mock _orgUnitServiceMock;
private readonly Mock _domainEventsMock;
+ private readonly Mock _organizationRightsServiceMock;
public OrganizationServiceTest()
{
@@ -56,6 +57,7 @@ public OrganizationServiceTest()
_repositoryMock = new Mock();
_orgUnitServiceMock = new Mock();
_domainEventsMock = new Mock();
+ _organizationRightsServiceMock = new Mock();
_sut = new OrganizationService(
_organizationRepository.Object,
_orgRightRepository.Object,
@@ -65,7 +67,7 @@ public OrganizationServiceTest()
Mock.Of(),
_transactionManager.Object,
_repositoryMock.Object,
- Mock.Of(),
+ _organizationRightsServiceMock.Object,
_orgUnitServiceMock.Object,
_domainEventsMock.Object);
}
@@ -261,6 +263,7 @@ public void RemoveUser_Returns_Ok()
var organization = new Organization();
ExpectGetOrganizationByKeyReturns(organizationId, organization);
ExpectAllowModifyReturns(organization, true);
+ ExpectTransactionBeginReturns();
var matchedRight1 = CreateRight(organizationId, userId);
var matchedRight2 = CreateRight(organizationId, userId);
var unmatchedRight1 = CreateRight(A(), userId);
@@ -795,6 +798,18 @@ private Organization CreateOrganization()
return organization;
}
+ private void ExpectTransactionBeginReturns()
+ {
+ var transaction = new Mock();
+ _transactionManager.Setup(x => x.Begin()).Returns(transaction.Object);
+ }
+
+
+ private void ExpectOrganizationRightsRemoveRoleReturns(int rightId, Result expectedResult)
+ {
+ _organizationRightsServiceMock.Setup(x => x.RemoveRole(rightId)).Returns(expectedResult);
+ }
+
private void ExpectAllowDeleteReturns(Organization organization, bool value)
{
_authorizationContext.Setup(x => x.AllowDelete(organization)).Returns(value);
From d65d598bc1643e7b9d9bbf5d55961bffeea22163 Mon Sep 17 00:00:00 2001
From: Aleksander Naskret
Date: Mon, 23 May 2022 09:34:22 +0200
Subject: [PATCH 011/432] Fixed unit tests
---
.../OrganizationAuthorizationContext.cs | 2 +-
.../ApplicationServices/UserServiceTest.cs | 1 +
.../OrganizationAuthorizationContextTest.cs | 4 ++--
.../Services/OrganizationServiceTest.cs | 13 +++++++------
4 files changed, 11 insertions(+), 9 deletions(-)
diff --git a/Core.ApplicationServices/Authorization/OrganizationAuthorizationContext.cs b/Core.ApplicationServices/Authorization/OrganizationAuthorizationContext.cs
index 59035d6c29..937a7e46c6 100644
--- a/Core.ApplicationServices/Authorization/OrganizationAuthorizationContext.cs
+++ b/Core.ApplicationServices/Authorization/OrganizationAuthorizationContext.cs
@@ -236,7 +236,7 @@ public bool AllowDelete(IEntity entity)
{
result = entity switch
{
- User _ => IsGlobalAdmin(),
+ User user => IsGlobalAdmin() && EntityEqualsActiveUser(user) == false,
ItInterface itInterface =>
//Even rightsholders are not allowed to delete interfaces
IsGlobalAdmin() || IsLocalAdmin(itInterface.OrganizationId),
diff --git a/Tests.Unit.Core.ApplicationServices/ApplicationServices/UserServiceTest.cs b/Tests.Unit.Core.ApplicationServices/ApplicationServices/UserServiceTest.cs
index 8f01954128..799072abc9 100644
--- a/Tests.Unit.Core.ApplicationServices/ApplicationServices/UserServiceTest.cs
+++ b/Tests.Unit.Core.ApplicationServices/ApplicationServices/UserServiceTest.cs
@@ -45,6 +45,7 @@ public UserServiceTest()
_cryptoServiceMock = new Mock();
_authorizationContextMock = new Mock();
_domainEventsMock = new Mock();
+ _activeUserIdContextMock = new Mock();
_organizationServiceMock = new Mock();
_sut = new UserService(
diff --git a/Tests.Unit.Presentation.Web/Authorization/OrganizationAuthorizationContextTest.cs b/Tests.Unit.Presentation.Web/Authorization/OrganizationAuthorizationContextTest.cs
index 415ea28668..a90b2ac076 100644
--- a/Tests.Unit.Presentation.Web/Authorization/OrganizationAuthorizationContextTest.cs
+++ b/Tests.Unit.Presentation.Web/Authorization/OrganizationAuthorizationContextTest.cs
@@ -544,7 +544,7 @@ public void Allow_Create_User_Returns(bool expectedResult)
[Theory]
//Checks not bound to context condition
[InlineData(true, false, false, false, false, false, true)]
- [InlineData(false, true, false, false, false, false, true)]
+ [InlineData(false, true, false, false, false, false, false)]
[InlineData(false, false, true, true, false, false, true)]
//Same organization - positive matches
@@ -581,7 +581,7 @@ public void AllowDelete_For_Context_Dependent_Object_Returns(
[Theory]
[InlineData(true, false, false, true)]
- [InlineData(false, true, false, true)]
+ [InlineData(false, true, false, false)]
[InlineData(false, false, true, true)]
[InlineData(false, false, false, false)]
public void AllowDelete_For_Context_Independent_Object_Returns(
diff --git a/Tests.Unit.Presentation.Web/Services/OrganizationServiceTest.cs b/Tests.Unit.Presentation.Web/Services/OrganizationServiceTest.cs
index 83447bf02f..51e94bb764 100644
--- a/Tests.Unit.Presentation.Web/Services/OrganizationServiceTest.cs
+++ b/Tests.Unit.Presentation.Web/Services/OrganizationServiceTest.cs
@@ -269,16 +269,17 @@ public void RemoveUser_Returns_Ok()
var unmatchedRight1 = CreateRight(A(), userId);
var unmatchedRight2 = CreateRight(organizationId, A());
_orgRightRepository.Setup(x => x.AsQueryable()).Returns(new[] { matchedRight1, unmatchedRight1, matchedRight2, unmatchedRight2 }.AsQueryable());
+ ExpectOrganizationRightsRemoveRoleReturnsSuccess();
//Act
var result = _sut.RemoveUser(organizationId, userId);
//Assert that only the right entities were removed
Assert.True(result.Ok);
- _orgRightRepository.Verify(x => x.DeleteByKey(matchedRight1.Id), Times.Once);
- _orgRightRepository.Verify(x => x.DeleteByKey(matchedRight2.Id), Times.Once);
- _orgRightRepository.Verify(x => x.DeleteByKey(unmatchedRight1.Id), Times.Never);
- _orgRightRepository.Verify(x => x.DeleteByKey(unmatchedRight2.Id), Times.Never);
+ _organizationRightsServiceMock.Verify(x => x.RemoveRole(matchedRight1.Id), Times.Once);
+ _organizationRightsServiceMock.Verify(x => x.RemoveRole(matchedRight2.Id), Times.Once);
+ _organizationRightsServiceMock.Verify(x => x.RemoveRole(unmatchedRight1.Id), Times.Never);
+ _organizationRightsServiceMock.Verify(x => x.RemoveRole(unmatchedRight2.Id), Times.Never);
}
[Fact]
@@ -805,9 +806,9 @@ private void ExpectTransactionBeginReturns()
}
- private void ExpectOrganizationRightsRemoveRoleReturns(int rightId, Result expectedResult)
+ private void ExpectOrganizationRightsRemoveRoleReturnsSuccess()
{
- _organizationRightsServiceMock.Setup(x => x.RemoveRole(rightId)).Returns(expectedResult);
+ _organizationRightsServiceMock.Setup(x => x.RemoveRole(It.IsAny())).Returns(It.IsAny());
}
private void ExpectAllowDeleteReturns(Organization organization, bool value)
From c7485b418b2a340f2c4cd6e633dbc80df2081b06 Mon Sep 17 00:00:00 2001
From: Aleksander Naskret
Date: Mon, 23 May 2022 09:57:54 +0200
Subject: [PATCH 012/432] Changes
---
Core.DomainServices/Context/ActiveUserIdContext.cs | 5 -----
.../Tools/External/Rights/RightsHelper.cs | 8 +++-----
.../ApplicationServices/UserServiceTest.cs | 4 +++-
.../Services/OrganizationServiceTest.cs | 2 +-
4 files changed, 7 insertions(+), 12 deletions(-)
diff --git a/Core.DomainServices/Context/ActiveUserIdContext.cs b/Core.DomainServices/Context/ActiveUserIdContext.cs
index 27f7c11c6a..0df1b2df95 100644
--- a/Core.DomainServices/Context/ActiveUserIdContext.cs
+++ b/Core.DomainServices/Context/ActiveUserIdContext.cs
@@ -8,10 +8,5 @@ public ActiveUserIdContext(int activeUserId)
{
ActiveUserId = activeUserId;
}
-
- public ActiveUserIdContext()
- {
-
- }
}
}
diff --git a/Tests.Integration.Presentation.Web/Tools/External/Rights/RightsHelper.cs b/Tests.Integration.Presentation.Web/Tools/External/Rights/RightsHelper.cs
index 87c1e4929f..d130a27048 100644
--- a/Tests.Integration.Presentation.Web/Tools/External/Rights/RightsHelper.cs
+++ b/Tests.Integration.Presentation.Web/Tools/External/Rights/RightsHelper.cs
@@ -21,7 +21,7 @@ public static async Task AddUserRole(int userId, int orgId, RightsType rightsTyp
var roleDto = new RightDTO
{
UserId = userId,
- RoleId = (int)OrganizationRole.LocalAdmin //role.ToString("G")
+ RoleId = (int)OrganizationRole.LocalAdmin
};
var url = TestEnvironment.CreateUrl(await PrepareUrl(orgId, name, rightsType));
@@ -43,9 +43,8 @@ public static async Task AddOrganizationRoleToUser(int userId, int orgId, Cookie
Assert.Equal(HttpStatusCode.Created, response.StatusCode);
}
- public static async Task AddDprRoleToUser(int userId, int orgId, string name, Cookie optionalLogin = null)
+ public static async Task AddDprRoleToUser(int userId, int orgId, string name)
{
- //var cookie = optionalLogin ?? await HttpApi.GetCookieAsync(OrganizationRole.GlobalAdmin);
var dpr= await DataProcessingRegistrationHelper.CreateAsync(orgId, name);
var roles = await DataProcessingRegistrationHelper.GetAvailableRolesAsync(dpr.Id);
@@ -75,10 +74,9 @@ private static async Task PrepareUrl(int orgId, string name, RightsType
var itSystemUsage = await ItSystemUsageHelper.CreateItSystemUsage(orgId, itSystem.Id);
return $"api/itSystemUsageRights/{itSystemUsage.Id}?organizationId={orgId}";
case RightsType.OrganizationUnitRights:
- //TODO: Create org unit
var orgUnit = OrganizationUnitHelper.GetOrganizationUnits(orgId);
return $"api/organizationunitright/{orgUnit.Result.Id}?organizationId={orgId}";
- default: throw new Exception();
+ default: throw new Exception("Incorrect Rights Type");
}
}
}
diff --git a/Tests.Unit.Core.ApplicationServices/ApplicationServices/UserServiceTest.cs b/Tests.Unit.Core.ApplicationServices/ApplicationServices/UserServiceTest.cs
index 799072abc9..3d9a24ec8a 100644
--- a/Tests.Unit.Core.ApplicationServices/ApplicationServices/UserServiceTest.cs
+++ b/Tests.Unit.Core.ApplicationServices/ApplicationServices/UserServiceTest.cs
@@ -45,7 +45,9 @@ public UserServiceTest()
_cryptoServiceMock = new Mock();
_authorizationContextMock = new Mock();
_domainEventsMock = new Mock();
- _activeUserIdContextMock = new Mock();
+
+ var activeUserId = A();
+ _activeUserIdContextMock = new Mock(activeUserId);
_organizationServiceMock = new Mock();
_sut = new UserService(
diff --git a/Tests.Unit.Presentation.Web/Services/OrganizationServiceTest.cs b/Tests.Unit.Presentation.Web/Services/OrganizationServiceTest.cs
index 51e94bb764..6bd456188c 100644
--- a/Tests.Unit.Presentation.Web/Services/OrganizationServiceTest.cs
+++ b/Tests.Unit.Presentation.Web/Services/OrganizationServiceTest.cs
@@ -289,7 +289,7 @@ public void GetAllOrganizations_Returns_All()
var expectedOrg1 = new Organization() { Id = A() };
var expectedOrg2 = new Organization() { Id = A() };
var expectedOrg3 = new Organization() { Id = A() };
- _repositoryMock.Setup(x => x.GetAll()).Returns(new List() { expectedOrg1, expectedOrg2, expectedOrg3 }.AsQueryable());
+ _repositoryMock.Setup(x => x.GetAll()).Returns(new List { expectedOrg1, expectedOrg2, expectedOrg3 }.AsQueryable());
_authorizationContext.Setup(x => x.GetCrossOrganizationReadAccess()).Returns(CrossOrganizationDataReadAccessLevel.All);
//Act
From c065210acf4c559bbdd4d3f6d60f61535be67efc Mon Sep 17 00:00:00 2001
From: Aleksander Naskret
Date: Mon, 23 May 2022 15:55:03 +0200
Subject: [PATCH 013/432] Changes after review
---
Core.ApplicationServices/UserService.cs | 12 ++--
.../API/V1/ItProjectStatusController.cs | 1 -
.../Tools/External/Rights/RightsHelper.cs | 30 ++++++---
.../Tools/ItProjectHelper.cs | 63 +++++++++++++++++++
.../Tools/ItSystemUsageHelper.cs | 15 -----
.../Tools/OrganizationUnitHelper.cs | 15 +----
.../Tools/UserHelper.cs | 1 -
.../Users/UserTest.cs | 12 +++-
8 files changed, 101 insertions(+), 48 deletions(-)
diff --git a/Core.ApplicationServices/UserService.cs b/Core.ApplicationServices/UserService.cs
index 0f4d4f83c0..aa17005a19 100644
--- a/Core.ApplicationServices/UserService.cs
+++ b/Core.ApplicationServices/UserService.cs
@@ -296,13 +296,13 @@ public Maybe DeleteUserFromKitos(Guid userUuid)
var user = _userRepository.AsQueryable().ByUuid(userUuid);
if (user == null)
- return Maybe.Some(new OperationError(OperationFailure.NotFound));
+ return new OperationError(OperationFailure.NotFound);
if(_activeUserIdContext.GetValueOrDefault().ActiveUserId == user.Id)
- return Maybe.Some(new OperationError("You cannot delete a user you are currently logged in as", OperationFailure.Forbidden));
+ return new OperationError("You cannot delete a user you are currently logged in as", OperationFailure.Forbidden);
if (!_authorizationContext.AllowDelete(user))
- return Maybe.Some(new OperationError(OperationFailure.Forbidden));
+ return new OperationError(OperationFailure.Forbidden);
_domainEvents.Raise(new EntityBeingDeletedEvent(user));
@@ -314,7 +314,7 @@ public Maybe DeleteUserFromKitos(Guid userUuid)
return Maybe.None;
}
- private void Delete(User user)
+ private static void Delete(User user)
{
user.LockedOutDate = DateTime.Now;
user.EmailBeforeDeletion = user.Email;
@@ -326,12 +326,10 @@ private void Delete(User user)
user.IsGlobalAdmin = false;
user.HasApiAccess = false;
user.HasStakeHolderAccess = false;
-
- //TODO: Check cascades
+
user.ItProjectStatuses.Clear();
user.ResponsibleForCommunications.Clear();
user.HandoverParticipants.Clear();
- user.LifeCycleTrackingEvents.Clear();
user.ResponsibleForRisks.Clear();
user.SsoIdentities.Clear();
}
diff --git a/Presentation.Web/Controllers/API/V1/ItProjectStatusController.cs b/Presentation.Web/Controllers/API/V1/ItProjectStatusController.cs
index d0755cf39e..216e652bcf 100644
--- a/Presentation.Web/Controllers/API/V1/ItProjectStatusController.cs
+++ b/Presentation.Web/Controllers/API/V1/ItProjectStatusController.cs
@@ -11,7 +11,6 @@
using Core.DomainServices.Repositories.Project;
using Presentation.Web.Infrastructure.Attributes;
using Presentation.Web.Infrastructure.Authorization.Controller.Crud;
-using Presentation.Web.Models;
using Presentation.Web.Models.API.V1;
using Swashbuckle.Swagger.Annotations;
diff --git a/Tests.Integration.Presentation.Web/Tools/External/Rights/RightsHelper.cs b/Tests.Integration.Presentation.Web/Tools/External/Rights/RightsHelper.cs
index d130a27048..d72fc37354 100644
--- a/Tests.Integration.Presentation.Web/Tools/External/Rights/RightsHelper.cs
+++ b/Tests.Integration.Presentation.Web/Tools/External/Rights/RightsHelper.cs
@@ -1,12 +1,10 @@
using System;
-using System.Collections.Generic;
using System.Linq;
using System.Net;
-using System.Text;
using System.Threading.Tasks;
using Core.DomainModel;
using Core.DomainModel.Organization;
-using Presentation.Web.Models.API.V1;
+using Core.DomainModel.SSO;
using Tests.Integration.Presentation.Web.Tools.Model;
using Xunit;
@@ -14,7 +12,7 @@ namespace Tests.Integration.Presentation.Web.Tools.External.Rights
{
public class RightsHelper
{
- public static async Task AddUserRole(int userId, int orgId, RightsType rightsType, string name, Cookie optionalLogin = null)
+ public static async Task AddUserRole(int userId, int orgId, RightsType rightsType, string name, int projectId = 0, Cookie optionalLogin = null)
{
var cookie = optionalLogin ?? await HttpApi.GetCookieAsync(OrganizationRole.GlobalAdmin);
@@ -24,7 +22,7 @@ public static async Task AddUserRole(int userId, int orgId, RightsType rightsTyp
RoleId = (int)OrganizationRole.LocalAdmin
};
- var url = TestEnvironment.CreateUrl(await PrepareUrl(orgId, name, rightsType));
+ var url = TestEnvironment.CreateUrl(await PrepareUrl(orgId, name, rightsType, projectId));
var response = await HttpApi.PostWithCookieAsync(url, cookie, roleDto);
Assert.Equal(HttpStatusCode.Created, response.StatusCode);
}
@@ -59,7 +57,22 @@ public static async Task AddDprRoleToUser(int userId, int orgId, string name)
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
- private static async Task PrepareUrl(int orgId, string name, RightsType rightsType)
+ public static async Task AddSsoIdentityToUser(int userId)
+ {
+ var user = await UserHelper.GetUserByIdAsync(userId);
+ var ssoIdentity = new SsoUserIdentity
+ {
+ ExternalUuid = user.Uuid
+ };
+
+ DatabaseAccess.MutateDatabase(x =>
+ {
+ x.SsoUserIdentities.Add(ssoIdentity);
+ x.SaveChanges();
+ });
+ }
+
+ private static async Task PrepareUrl(int orgId, string name, RightsType rightsType, int projectId = 0)
{
switch (rightsType)
{
@@ -67,11 +80,10 @@ private static async Task PrepareUrl(int orgId, string name, RightsType
var contract = await ItContractHelper.CreateContract(name, orgId);
return $"api/itcontractright/{contract.Id}?organizationId={orgId}";
case RightsType.ItProjectRights:
- var project= await ItProjectHelper.CreateProject(name, orgId);
- return $"api/itprojectright/{project.Id}?organizationId={orgId}";
+ return $"api/itprojectright/{projectId}?organizationId={orgId}";
case RightsType.ItSystemRights:
var itSystem = await ItSystemHelper.CreateItSystemInOrganizationAsync(name, orgId, AccessModifier.Local);
- var itSystemUsage = await ItSystemUsageHelper.CreateItSystemUsage(orgId, itSystem.Id);
+ var itSystemUsage = await ItSystemHelper.TakeIntoUseAsync(itSystem.Id, orgId);
return $"api/itSystemUsageRights/{itSystemUsage.Id}?organizationId={orgId}";
case RightsType.OrganizationUnitRights:
var orgUnit = OrganizationUnitHelper.GetOrganizationUnits(orgId);
diff --git a/Tests.Integration.Presentation.Web/Tools/ItProjectHelper.cs b/Tests.Integration.Presentation.Web/Tools/ItProjectHelper.cs
index d115ee09d6..bb8bc369b1 100644
--- a/Tests.Integration.Presentation.Web/Tools/ItProjectHelper.cs
+++ b/Tests.Integration.Presentation.Web/Tools/ItProjectHelper.cs
@@ -229,6 +229,27 @@ public static async Task SendAddRiskRequestAsync(int organi
return await HttpApi.PostWithCookieAsync(url, cookie, body);
}
+ public static async Task AddRiskAsync(int orgId, int userId, int projectId, Cookie optionalLogin = null)
+ {
+ using var response = await SendAddRiskRequestAsync(orgId, userId, projectId, optionalLogin);
+
+ Assert.Equal(HttpStatusCode.Created, response.StatusCode);
+ return await response.ReadResponseBodyAsKitosApiResponseAsync();
+ }
+
+ public static async Task SendAddRiskRequestAsync(int orgId, int userId, int projectId, Cookie optionalLogin = null)
+ {
+ var cookie = optionalLogin ?? await HttpApi.GetCookieAsync(OrganizationRole.GlobalAdmin);
+
+ var communication = new RiskDTO()
+ {
+ ItProjectId = projectId,
+ ResponsibleUserId = userId
+ };
+
+ return await HttpApi.PostWithCookieAsync(TestEnvironment.CreateUrl($"api/risk?organizationId={orgId}"), cookie, communication);
+ }
+
public static async Task AddCommunicationAsync(int organizationId, int projectId, string media, string message, string purpose, int responsibleUserId, string targetAudience, DateTime dueDate, Cookie optionalLogin = null)
{
using (var response = await SendAddCommunicationRequestAsync(organizationId, projectId, media, message, purpose, responsibleUserId, targetAudience, dueDate, optionalLogin))
@@ -257,6 +278,27 @@ public static async Task SendAddCommunicationRequestAsync(i
return await HttpApi.PostWithCookieAsync(url, cookie, body);
}
+ public static async Task AddCommunicationAsync(int orgId, int userId, int projectId, Cookie optionalLogin = null)
+ {
+ using var response = await SendAddCommunicationRequestAsync(orgId, userId, projectId, optionalLogin);
+
+ Assert.Equal(HttpStatusCode.Created, response.StatusCode);
+ return await response.ReadResponseBodyAsKitosApiResponseAsync();
+ }
+
+ public static async Task SendAddCommunicationRequestAsync(int orgId, int userId, int projectId, Cookie optionalLogin = null)
+ {
+ var cookie = optionalLogin ?? await HttpApi.GetCookieAsync(OrganizationRole.GlobalAdmin);
+
+ var communication = new CommunicationDTO
+ {
+ ItProjectId = projectId,
+ ResponsibleUserId = userId
+ };
+
+ return await HttpApi.PostWithCookieAsync(TestEnvironment.CreateUrl($"api/communication?organizationId={orgId}"), cookie, communication);
+ }
+
public static async Task AddHandoverResponsibleAsync(int handoverId, int responsibleUserId, Cookie optionalLogin = null)
{
using (var response = await SendAddHandoverResponsibleRequestAsync(handoverId, responsibleUserId, optionalLogin))
@@ -275,6 +317,27 @@ public static async Task SendAddHandoverResponsibleRequestA
return await HttpApi.PostWithCookieAsync(url, cookie, new object());
}
+ public static async Task AddAssignmentAsync(int orgId, int userId, int projectId, Cookie optionalLogin = null)
+ {
+ using var response = await SendAddAssignmentRequestAsync(orgId, userId, projectId, optionalLogin);
+
+ Assert.Equal(HttpStatusCode.Created, response.StatusCode);
+ return await response.ReadResponseBodyAsKitosApiResponseAsync();
+ }
+
+ public static async Task SendAddAssignmentRequestAsync(int orgId, int userId, int projectId, Cookie optionalLogin = null)
+ {
+ var cookie = optionalLogin ?? await HttpApi.GetCookieAsync(OrganizationRole.GlobalAdmin);
+
+ var assignment = new AssignmentDTO
+ {
+ AssociatedItProjectId = projectId,
+ AssociatedUserId = userId
+ };
+
+ return await HttpApi.PostWithCookieAsync(TestEnvironment.CreateUrl($"api/assignment?organizationId={orgId}"), cookie, assignment);
+ }
+
public static async Task DeleteProjectAsync(int projectId)
{
using var response = await SendDeleteProjectAsync(projectId);
diff --git a/Tests.Integration.Presentation.Web/Tools/ItSystemUsageHelper.cs b/Tests.Integration.Presentation.Web/Tools/ItSystemUsageHelper.cs
index 7ed3dfcf8a..77de3d210b 100644
--- a/Tests.Integration.Presentation.Web/Tools/ItSystemUsageHelper.cs
+++ b/Tests.Integration.Presentation.Web/Tools/ItSystemUsageHelper.cs
@@ -226,20 +226,5 @@ public static async Task SetIsHoldingDocumentRequestAsync(i
Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
return response;
}
-
- public static async Task CreateItSystemUsage(int orgId, int itSystemId, Cookie optionalLogin = null)
- {
- var cookie = optionalLogin ?? await HttpApi.GetCookieAsync(OrganizationRole.GlobalAdmin);
- var url = TestEnvironment.CreateUrl($"api/itSystemUsage");
- var body = new
- {
- OrganizationId = orgId,
- ItSystemId = itSystemId
- };
-
- using var response = await HttpApi.PostWithCookieAsync(url, cookie, body);
- Assert.Equal(HttpStatusCode.Created, response.StatusCode);
- return await response.ReadResponseBodyAsKitosApiResponseAsync();
- }
}
}
\ No newline at end of file
diff --git a/Tests.Integration.Presentation.Web/Tools/OrganizationUnitHelper.cs b/Tests.Integration.Presentation.Web/Tools/OrganizationUnitHelper.cs
index 5433371e27..a4fbcbd692 100644
--- a/Tests.Integration.Presentation.Web/Tools/OrganizationUnitHelper.cs
+++ b/Tests.Integration.Presentation.Web/Tools/OrganizationUnitHelper.cs
@@ -1,8 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Net;
-using System.Text;
+using System.Net;
using System.Threading.Tasks;
using Core.DomainModel.Organization;
using Presentation.Web.Models.API.V1;
@@ -17,15 +13,8 @@ public static async Task CreateOrganizationUnit(int orgId, str
var cookie = await HttpApi.GetCookieAsync(OrganizationRole.GlobalAdmin);
var orgUnitDto = new OrgUnitDTO()
{
- Id = 0,
Name = name,
- OrganizationId = orgId,
- LocalId = "test",
- ObjectOwnerName = "test",
- ObjectOwnerLastName = "last name",
- LastChangedByUserId = 1,
- LastChanged = DateTime.Now,
- Uuid = Guid.NewGuid()
+ OrganizationId = orgId
};
var orgUnitUrl = TestEnvironment.CreateUrl($"odata/OrganizationUnit");
diff --git a/Tests.Integration.Presentation.Web/Tools/UserHelper.cs b/Tests.Integration.Presentation.Web/Tools/UserHelper.cs
index 93a9810cda..254d8ec1fb 100644
--- a/Tests.Integration.Presentation.Web/Tools/UserHelper.cs
+++ b/Tests.Integration.Presentation.Web/Tools/UserHelper.cs
@@ -4,7 +4,6 @@
using System.Net.Http;
using System.Threading.Tasks;
using Core.DomainModel;
-using Presentation.Web.Models.API.V1;
using Presentation.Web.Models.API.V1.Users;
using Xunit;
diff --git a/Tests.Integration.Presentation.Web/Users/UserTest.cs b/Tests.Integration.Presentation.Web/Users/UserTest.cs
index 22802eda45..86a25ece4b 100644
--- a/Tests.Integration.Presentation.Web/Users/UserTest.cs
+++ b/Tests.Integration.Presentation.Web/Users/UserTest.cs
@@ -20,7 +20,7 @@ public async Task Can_Get_Users_And_Organizations_Where_User_Has_RightsholderAcc
{
//Arrange
var (userId, userEmail, organization) = await CreateRightsHolderAccessUserInNewOrganizationAsync();
-
+
//Act
var result = await UserHelper.GetUsersWithRightsholderAccessAsync();
@@ -129,13 +129,21 @@ public async Task Delete_User()
var (_, userId, organization) = await CreatePrerequisitesAsync(userRole);
var name = A();
+ var project = await ItProjectHelper.CreateProject(name, organization.Id);
+
await RightsHelper.AddUserRole(userId, organization.Id, RightsType.OrganizationUnitRights, name);
await RightsHelper.AddUserRole(userId, organization.Id, RightsType.ItContractRights, name);
- await RightsHelper.AddUserRole(userId, organization.Id, RightsType.ItProjectRights, name);
+ await RightsHelper.AddUserRole(userId, organization.Id, RightsType.ItProjectRights, name, project.Id);
await RightsHelper.AddUserRole(userId, organization.Id, RightsType.ItSystemRights, name);
await RightsHelper.AddDprRoleToUser(userId, organization.Id, name);
await RightsHelper.AddOrganizationRoleToUser(userId, organization.Id);
+ await ItProjectHelper.AddAssignmentAsync(organization.Id, userId, project.Id);
+ await ItProjectHelper.AddCommunicationAsync(organization.Id, userId, project.Id);
+ await ItProjectHelper.AddRiskAsync(organization.Id, userId, project.Id);
+ await ItProjectHelper.AddHandoverResponsibleAsync(project.Id, userId);
+ await RightsHelper.AddSsoIdentityToUser(userId);
+
var deleteResponse = await UserHelper.SendDeleteUserAsync(userId);
Assert.Equal(HttpStatusCode.OK, deleteResponse.StatusCode);
From 3f443a725f901bc0d6b3805a63b192ce55b9a9d3 Mon Sep 17 00:00:00 2001
From: Aleksander Naskret
Date: Tue, 24 May 2022 13:22:51 +0200
Subject: [PATCH 014/432] Changes after review
---
.../Handlers/HandleUserBeingDeleted.cs | 21 +-
Core.ApplicationServices/UserService.cs | 3 +-
.../SSO/ISsoUserIdentityRepository.cs | 2 +
.../SSO/SsoUserIdentityRepository.cs | 18 ++
.../Tools/External/Rights/RightsHelper.cs | 82 +++++---
.../Tools/ItContractHelper.cs | 11 +
.../Tools/ItProjectHelper.cs | 13 ++
.../Tools/ItSystemHelper.cs | 12 ++
.../Tools/OrganizationUnitHelper.cs | 22 +-
.../Users/UserTest.cs | 16 +-
.../Handlers/HandleUserBeingDeletedTests.cs | 191 ++++++++++++++++++
.../ApplicationServices/UserServiceTest.cs | 44 +++-
.../Tests.Unit.Core.csproj | 1 +
.../Services/OrganizationServiceTest.cs | 4 +
14 files changed, 394 insertions(+), 46 deletions(-)
create mode 100644 Tests.Unit.Core.ApplicationServices/ApplicationServices/Handlers/HandleUserBeingDeletedTests.cs
diff --git a/Core.ApplicationServices/UIConfiguration/Handlers/HandleUserBeingDeleted.cs b/Core.ApplicationServices/UIConfiguration/Handlers/HandleUserBeingDeleted.cs
index 8bc55025c1..ce9ec51d11 100644
--- a/Core.ApplicationServices/UIConfiguration/Handlers/HandleUserBeingDeleted.cs
+++ b/Core.ApplicationServices/UIConfiguration/Handlers/HandleUserBeingDeleted.cs
@@ -12,6 +12,7 @@
using Core.DomainModel.ItSystem;
using Core.DomainModel.ItSystemUsage;
using Core.DomainModel.Organization;
+using Core.DomainServices.Repositories.SSO;
using Core.DomainServices.Role;
namespace Core.ApplicationServices.UIConfiguration.Handlers
@@ -24,6 +25,7 @@ public class HandleUserBeingDeleted : IDomainEventHandler _itSystemRightService;
private readonly IRoleAssignmentService _itProjectRightService;
private readonly IRoleAssignmentService _organizationUnitRightService;
+ private readonly ISsoUserIdentityRepository _ssoUserIdentityRepository;
public HandleUserBeingDeleted(IDataProcessingRegistrationApplicationService dataProcessingRegistrationApplicationService,
@@ -31,7 +33,8 @@ public HandleUserBeingDeleted(IDataProcessingRegistrationApplicationService data
IRoleAssignmentService itContractRightService,
IRoleAssignmentService itSystemRightService,
IRoleAssignmentService itProjectRightService,
- IRoleAssignmentService organizationUnitRightService)
+ IRoleAssignmentService organizationUnitRightService,
+ ISsoUserIdentityRepository ssoUserIdentityRepository)
{
_dataProcessingRegistrationApplicationService = dataProcessingRegistrationApplicationService;
_organizationRightsService = organizationRightsService;
@@ -39,18 +42,20 @@ public HandleUserBeingDeleted(IDataProcessingRegistrationApplicationService data
_itSystemRightService = itSystemRightService;
_itProjectRightService = itProjectRightService;
_organizationUnitRightService = organizationUnitRightService;
+ _ssoUserIdentityRepository = ssoUserIdentityRepository;
}
public void Handle(EntityBeingDeletedEvent domainEvent)
{
var user = domainEvent.Entity;
- ClearDataProcessingRegistrationRight(user);
+ ClearDataProcessingRegistrationRight(user);
ClearOrganizationRights(user);
ClearItContractRights(user);
ClearItSystemRights(user);
ClearItProjectRights(user);
ClearOrganizationUnitRights(user);
+ ClearSsoIdentities(user);
}
private void ClearDataProcessingRegistrationRight(User user)
@@ -113,5 +118,15 @@ private void ClearOrganizationUnitRights(User user)
roles.Clear();
}
+ private void ClearSsoIdentities(User user)
+ {
+ var roles = user.SsoIdentities;
+ if (roles == null)
+ return;
+
+ _ssoUserIdentityRepository.DeleteIdentitiesForUser(roles);
+ roles.Clear();
+ }
+
}
-}
+}
\ No newline at end of file
diff --git a/Core.ApplicationServices/UserService.cs b/Core.ApplicationServices/UserService.cs
index aa17005a19..393581eb70 100644
--- a/Core.ApplicationServices/UserService.cs
+++ b/Core.ApplicationServices/UserService.cs
@@ -326,12 +326,11 @@ private static void Delete(User user)
user.IsGlobalAdmin = false;
user.HasApiAccess = false;
user.HasStakeHolderAccess = false;
-
+
user.ItProjectStatuses.Clear();
user.ResponsibleForCommunications.Clear();
user.HandoverParticipants.Clear();
user.ResponsibleForRisks.Clear();
- user.SsoIdentities.Clear();
}
}
}
diff --git a/Core.DomainServices/Repositories/SSO/ISsoUserIdentityRepository.cs b/Core.DomainServices/Repositories/SSO/ISsoUserIdentityRepository.cs
index c8bb0c4cf2..da31cc2f84 100644
--- a/Core.DomainServices/Repositories/SSO/ISsoUserIdentityRepository.cs
+++ b/Core.DomainServices/Repositories/SSO/ISsoUserIdentityRepository.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using Core.Abstractions.Types;
using Core.DomainModel;
using Core.DomainModel.SSO;
@@ -10,5 +11,6 @@ public interface ISsoUserIdentityRepository
{
Maybe GetByExternalUuid(Guid externalId);
Result AddNew(User user, Guid externalId);
+ void DeleteIdentitiesForUser(IEnumerable users);
}
}
diff --git a/Core.DomainServices/Repositories/SSO/SsoUserIdentityRepository.cs b/Core.DomainServices/Repositories/SSO/SsoUserIdentityRepository.cs
index d06534454c..002c582013 100644
--- a/Core.DomainServices/Repositories/SSO/SsoUserIdentityRepository.cs
+++ b/Core.DomainServices/Repositories/SSO/SsoUserIdentityRepository.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Linq;
using Core.Abstractions.Types;
using Core.DomainModel;
@@ -40,5 +41,22 @@ public Result AddNew(User user, Guid externalId
_repository.Save();
return identity;
}
+
+ public void DeleteIdentitiesForUser(IEnumerable identities)
+ {
+ if (identities == null)
+ {
+ throw new ArgumentNullException(nameof(identities));
+ }
+
+ var identities2 = identities.ToList();
+
+ foreach (var identity in identities2)
+ {
+ _repository.Delete(identity);
+ }
+
+ _repository.Save();
+ }
}
}
diff --git a/Tests.Integration.Presentation.Web/Tools/External/Rights/RightsHelper.cs b/Tests.Integration.Presentation.Web/Tools/External/Rights/RightsHelper.cs
index d72fc37354..6cec349996 100644
--- a/Tests.Integration.Presentation.Web/Tools/External/Rights/RightsHelper.cs
+++ b/Tests.Integration.Presentation.Web/Tools/External/Rights/RightsHelper.cs
@@ -1,8 +1,10 @@
using System;
+using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Core.DomainModel;
+using Core.DomainModel.ItContract;
using Core.DomainModel.Organization;
using Core.DomainModel.SSO;
using Tests.Integration.Presentation.Web.Tools.Model;
@@ -15,32 +17,19 @@ public class RightsHelper
public static async Task AddUserRole(int userId, int orgId, RightsType rightsType, string name, int projectId = 0, Cookie optionalLogin = null)
{
var cookie = optionalLogin ?? await HttpApi.GetCookieAsync(OrganizationRole.GlobalAdmin);
-
+
+ var roleId = await GetDefaultRoleIdForRight(rightsType, cookie);
var roleDto = new RightDTO
{
UserId = userId,
- RoleId = (int)OrganizationRole.LocalAdmin
+ RoleId = roleId
};
- var url = TestEnvironment.CreateUrl(await PrepareUrl(orgId, name, rightsType, projectId));
+ var url = TestEnvironment.CreateUrl(await PrepareUrl(orgId, name, rightsType, cookie, projectId));
var response = await HttpApi.PostWithCookieAsync(url, cookie, roleDto);
Assert.Equal(HttpStatusCode.Created, response.StatusCode);
}
- public static async Task AddOrganizationRoleToUser(int userId, int orgId, Cookie optionalLogin = null)
- {
- var cookie = optionalLogin ?? await HttpApi.GetCookieAsync(OrganizationRole.GlobalAdmin);
-
- var roleDto = new OrgRightDTO
- {
- UserId = userId,
- Role = OrganizationRole.LocalAdmin.ToString("G")
- };
-
- var response = await HttpApi.PostWithCookieAsync(TestEnvironment.CreateUrl($"odata/organizations({orgId})/Rights"), cookie, roleDto);
- Assert.Equal(HttpStatusCode.Created, response.StatusCode);
- }
-
public static async Task AddDprRoleToUser(int userId, int orgId, string name)
{
var dpr= await DataProcessingRegistrationHelper.CreateAsync(orgId, name);
@@ -57,27 +46,32 @@ public static async Task AddDprRoleToUser(int userId, int orgId, string name)
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
- public static async Task AddSsoIdentityToUser(int userId)
+ public static void AddSsoIdentityToUser(int userId)
{
- var user = await UserHelper.GetUserByIdAsync(userId);
var ssoIdentity = new SsoUserIdentity
{
- ExternalUuid = user.Uuid
+ ExternalUuid = Guid.NewGuid()
};
DatabaseAccess.MutateDatabase(x =>
{
- x.SsoUserIdentities.Add(ssoIdentity);
+ var user = x.Users.FirstOrDefault(x => x.Id == userId);
+ if (user == null)
+ return;
+
+ user.SsoIdentities.Add(ssoIdentity);
x.SaveChanges();
});
}
- private static async Task PrepareUrl(int orgId, string name, RightsType rightsType, int projectId = 0)
+ private static async Task PrepareUrl(int orgId, string name, RightsType rightsType, Cookie cookie, int projectId = 0)
{
switch (rightsType)
{
case RightsType.ItContractRights:
var contract = await ItContractHelper.CreateContract(name, orgId);
+ var response = await HttpApi.GetWithCookieAsync(TestEnvironment.CreateUrl("odata/ItContractRoles"), cookie);
+ var roles = await response.ReadOdataListResponseBodyAsAsync();
return $"api/itcontractright/{contract.Id}?organizationId={orgId}";
case RightsType.ItProjectRights:
return $"api/itprojectright/{projectId}?organizationId={orgId}";
@@ -86,8 +80,48 @@ private static async Task PrepareUrl(int orgId, string name, RightsType
var itSystemUsage = await ItSystemHelper.TakeIntoUseAsync(itSystem.Id, orgId);
return $"api/itSystemUsageRights/{itSystemUsage.Id}?organizationId={orgId}";
case RightsType.OrganizationUnitRights:
- var orgUnit = OrganizationUnitHelper.GetOrganizationUnits(orgId);
- return $"api/organizationunitright/{orgUnit.Result.Id}?organizationId={orgId}";
+ var orgUnit = await OrganizationUnitHelper.GetOrganizationUnitsAsync(orgId);
+ return $"api/organizationunitright/{orgUnit.Id}?organizationId={orgId}";
+ default: throw new Exception("Incorrect Rights Type");
+ }
+ }
+
+ private static async Task GetDefaultRoleIdForRight(RightsType rightsType, Cookie cookie)
+ {
+ switch (rightsType)
+ {
+ case RightsType.ItContractRights:
+ var contractRoles = await ItContractHelper.GetRolesAsync(cookie);
+ Assert.NotEmpty(contractRoles);
+
+ var singleContractRole = contractRoles.FirstOrDefault();
+ Assert.NotNull(singleContractRole);
+
+ return singleContractRole.Id;
+ case RightsType.ItProjectRights:
+ var projectRoles = await ItProjectHelper.GetRolesAsync(cookie);
+ Assert.NotEmpty(projectRoles);
+
+ var singleProjectRole = projectRoles.FirstOrDefault();
+ Assert.NotNull(singleProjectRole);
+
+ return singleProjectRole.Id;
+ case RightsType.ItSystemRights:
+ var systemRoles = await ItSystemHelper.GetRolesAsync(cookie);
+ Assert.NotEmpty(systemRoles);
+
+ var singleSystemRole = systemRoles.FirstOrDefault();
+ Assert.NotNull(singleSystemRole);
+
+ return singleSystemRole.Id;
+ case RightsType.OrganizationUnitRights:
+ var organizationUnitRoles = await OrganizationUnitHelper.GetOrganizationUnitRolesAsync(cookie);
+ Assert.NotEmpty(organizationUnitRoles);
+
+ var singleOrganizationUnit = organizationUnitRoles.FirstOrDefault();
+ Assert.NotNull(singleOrganizationUnit);
+
+ return singleOrganizationUnit.Id;
default: throw new Exception("Incorrect Rights Type");
}
}
diff --git a/Tests.Integration.Presentation.Web/Tools/ItContractHelper.cs b/Tests.Integration.Presentation.Web/Tools/ItContractHelper.cs
index 36c8f707d5..f6d7750ff2 100644
--- a/Tests.Integration.Presentation.Web/Tools/ItContractHelper.cs
+++ b/Tests.Integration.Presentation.Web/Tools/ItContractHelper.cs
@@ -4,6 +4,7 @@
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
+using Core.DomainModel.ItContract;
using Core.DomainModel.Organization;
using Presentation.Web.Models.API.V1;
using Xunit;
@@ -185,5 +186,15 @@ public static async Task SendAssignSupplierAsync(int contra
return await HttpApi.PatchWithCookieAsync(TestEnvironment.CreateUrl($"api/itcontract/{contractId}?organizationId={organizationId}"), cookie, new { supplierId = supplierId });
}
+
+ public static async Task> GetRolesAsync(Cookie optionalLogin = null)
+ {
+ var cookie = optionalLogin ?? await HttpApi.GetCookieAsync(OrganizationRole.GlobalAdmin);
+
+ var response = await HttpApi.GetWithCookieAsync(TestEnvironment.CreateUrl("odata/ItContractRoles"), cookie);
+
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ return await response.ReadOdataListResponseBodyAsAsync();
+ }
}
}
\ No newline at end of file
diff --git a/Tests.Integration.Presentation.Web/Tools/ItProjectHelper.cs b/Tests.Integration.Presentation.Web/Tools/ItProjectHelper.cs
index bb8bc369b1..9485e1b4d1 100644
--- a/Tests.Integration.Presentation.Web/Tools/ItProjectHelper.cs
+++ b/Tests.Integration.Presentation.Web/Tools/ItProjectHelper.cs
@@ -1,7 +1,10 @@
using System;
+using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
+using Core.DomainModel.ItContract;
+using Core.DomainModel.ItProject;
using Core.DomainModel.Organization;
using Presentation.Web.Models;
using Presentation.Web.Models.API.V1;
@@ -343,5 +346,15 @@ public static async Task DeleteProjectAsync(int projectId)
using var response = await SendDeleteProjectAsync(projectId);
Assert.Equal(HttpStatusCode.OK,response.StatusCode);
}
+
+ public static async Task> GetRolesAsync(Cookie optionalLogin = null)
+ {
+ var cookie = optionalLogin ?? await HttpApi.GetCookieAsync(OrganizationRole.GlobalAdmin);
+
+ var response = await HttpApi.GetWithCookieAsync(TestEnvironment.CreateUrl("odata/ItProjectRoles"), cookie);
+
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ return await response.ReadOdataListResponseBodyAsAsync();
+ }
}
}
diff --git a/Tests.Integration.Presentation.Web/Tools/ItSystemHelper.cs b/Tests.Integration.Presentation.Web/Tools/ItSystemHelper.cs
index cef78afdee..a61460e81b 100644
--- a/Tests.Integration.Presentation.Web/Tools/ItSystemHelper.cs
+++ b/Tests.Integration.Presentation.Web/Tools/ItSystemHelper.cs
@@ -3,6 +3,8 @@
using System.Net.Http;
using System.Threading.Tasks;
using Core.DomainModel;
+using Core.DomainModel.ItProject;
+using Core.DomainModel.ItSystem;
using Core.DomainModel.Organization;
using Presentation.Web.Models.API.V1;
using Xunit;
@@ -262,5 +264,15 @@ public static async Task SendAddTaskRefRequestAsync(int sys
};
return await HttpApi.PostWithCookieAsync(url, cookie, body);
}
+
+ public static async Task> GetRolesAsync(Cookie optionalLogin = null)
+ {
+ var cookie = optionalLogin ?? await HttpApi.GetCookieAsync(OrganizationRole.GlobalAdmin);
+
+ var response = await HttpApi.GetWithCookieAsync(TestEnvironment.CreateUrl("odata/ItSystemRoles"), cookie);
+
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ return await response.ReadOdataListResponseBodyAsAsync();
+ }
}
}
diff --git a/Tests.Integration.Presentation.Web/Tools/OrganizationUnitHelper.cs b/Tests.Integration.Presentation.Web/Tools/OrganizationUnitHelper.cs
index a4fbcbd692..3e71909622 100644
--- a/Tests.Integration.Presentation.Web/Tools/OrganizationUnitHelper.cs
+++ b/Tests.Integration.Presentation.Web/Tools/OrganizationUnitHelper.cs
@@ -1,4 +1,5 @@
-using System.Net;
+using System.Collections.Generic;
+using System.Net;
using System.Threading.Tasks;
using Core.DomainModel.Organization;
using Presentation.Web.Models.API.V1;
@@ -8,31 +9,26 @@ namespace Tests.Integration.Presentation.Web.Tools
{
public class OrganizationUnitHelper
{
- public static async Task CreateOrganizationUnit(int orgId, string name, Cookie optionalLogin = null)
+ public static async Task GetOrganizationUnitsAsync(int orgId, Cookie optionalLogin = null)
{
var cookie = await HttpApi.GetCookieAsync(OrganizationRole.GlobalAdmin);
- var orgUnitDto = new OrgUnitDTO()
- {
- Name = name,
- OrganizationId = orgId
- };
- var orgUnitUrl = TestEnvironment.CreateUrl($"odata/OrganizationUnit");
+ var orgUnitUrl = TestEnvironment.CreateUrl($"api/OrganizationUnit?organization={orgId}");
- var response = await HttpApi.PostWithCookieAsync(orgUnitUrl, cookie, orgUnitDto);
+ var response = await HttpApi.GetWithCookieAsync(orgUnitUrl, cookie);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
- return await response.ReadResponseBodyAsAsync();
+ return await response.ReadResponseBodyAsKitosApiResponseAsync();
}
- public static async Task GetOrganizationUnits(int orgId, Cookie optionalLogin = null)
+ public static async Task> GetOrganizationUnitRolesAsync(Cookie optionalLogin = null)
{
var cookie = await HttpApi.GetCookieAsync(OrganizationRole.GlobalAdmin);
- var orgUnitUrl = TestEnvironment.CreateUrl($"api/OrganizationUnit?organization={orgId}");
+ var orgUnitUrl = TestEnvironment.CreateUrl($"odata/OrganizationUnitRoles");
var response = await HttpApi.GetWithCookieAsync(orgUnitUrl, cookie);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
- return await response.ReadResponseBodyAsKitosApiResponseAsync();
+ return await response.ReadOdataListResponseBodyAsAsync();
}
}
}
diff --git a/Tests.Integration.Presentation.Web/Users/UserTest.cs b/Tests.Integration.Presentation.Web/Users/UserTest.cs
index 86a25ece4b..66c23489b7 100644
--- a/Tests.Integration.Presentation.Web/Users/UserTest.cs
+++ b/Tests.Integration.Presentation.Web/Users/UserTest.cs
@@ -131,24 +131,34 @@ public async Task Delete_User()
var project = await ItProjectHelper.CreateProject(name, organization.Id);
- await RightsHelper.AddUserRole(userId, organization.Id, RightsType.OrganizationUnitRights, name);
await RightsHelper.AddUserRole(userId, organization.Id, RightsType.ItContractRights, name);
+ await RightsHelper.AddUserRole(userId, organization.Id, RightsType.OrganizationUnitRights, name);
await RightsHelper.AddUserRole(userId, organization.Id, RightsType.ItProjectRights, name, project.Id);
await RightsHelper.AddUserRole(userId, organization.Id, RightsType.ItSystemRights, name);
await RightsHelper.AddDprRoleToUser(userId, organization.Id, name);
- await RightsHelper.AddOrganizationRoleToUser(userId, organization.Id);
await ItProjectHelper.AddAssignmentAsync(organization.Id, userId, project.Id);
await ItProjectHelper.AddCommunicationAsync(organization.Id, userId, project.Id);
await ItProjectHelper.AddRiskAsync(organization.Id, userId, project.Id);
await ItProjectHelper.AddHandoverResponsibleAsync(project.Id, userId);
- await RightsHelper.AddSsoIdentityToUser(userId);
+ RightsHelper.AddSsoIdentityToUser(userId);
var deleteResponse = await UserHelper.SendDeleteUserAsync(userId);
Assert.Equal(HttpStatusCode.OK, deleteResponse.StatusCode);
var getDeletedUserResponse = await UserHelper.GetUserByIdAsync(userId);
+
Assert.True(getDeletedUserResponse.Deleted);
+ Assert.False(getDeletedUserResponse.IsGlobalAdmin);
+ Assert.False(getDeletedUserResponse.HasApiAccess);
+ Assert.False(getDeletedUserResponse.HasStakeHolderAccess);
+
+ Assert.Contains("_deleted_user@kitos.dk", getDeletedUserResponse.Email);
+ Assert.Contains("(SLETTET)", getDeletedUserResponse.LastName);
+ Assert.NotNull(getDeletedUserResponse.EmailBeforeDeletion);
+ Assert.NotNull(getDeletedUserResponse.LockedOutDate);
+ Assert.NotNull(getDeletedUserResponse.DeletedDate);
+ Assert.Null(getDeletedUserResponse.PhoneNumber);
}
[Fact]
diff --git a/Tests.Unit.Core.ApplicationServices/ApplicationServices/Handlers/HandleUserBeingDeletedTests.cs b/Tests.Unit.Core.ApplicationServices/ApplicationServices/Handlers/HandleUserBeingDeletedTests.cs
new file mode 100644
index 0000000000..764bec73dc
--- /dev/null
+++ b/Tests.Unit.Core.ApplicationServices/ApplicationServices/Handlers/HandleUserBeingDeletedTests.cs
@@ -0,0 +1,191 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Core.Abstractions.Types;
+using Core.ApplicationServices.GDPR;
+using Core.ApplicationServices.Organizations;
+using Core.ApplicationServices.UIConfiguration.Handlers;
+using Core.DomainModel;
+using Core.DomainModel.Events;
+using Core.DomainModel.GDPR;
+using Core.DomainModel.ItContract;
+using Core.DomainModel.ItProject;
+using Core.DomainModel.ItSystem;
+using Core.DomainModel.ItSystemUsage;
+using Core.DomainModel.Organization;
+using Core.DomainModel.SSO;
+using Core.DomainServices.Repositories.SSO;
+using Core.DomainServices.Role;
+using Moq;
+using Tests.Toolkit.Patterns;
+using Xunit;
+
+namespace Tests.Unit.Core.ApplicationServices.Handlers
+{
+ public class HandleUserBeingDeletedTests : WithAutoFixture
+ {
+ private readonly Mock _dprApplicationServiceMock;
+ private readonly Mock _organizationRightsServiceMock;
+ private readonly Mock> _itContractRightServiceMock;
+ private readonly Mock> _itSystemRightServiceMock;
+ private readonly Mock> _itProjectRightServiceMock;
+ private readonly Mock> _organizationUnitRightServiceMock;
+ private readonly Mock _ssoUserIdentityRepository;
+ private readonly HandleUserBeingDeleted _sut;
+
+ public HandleUserBeingDeletedTests()
+ {
+ _dprApplicationServiceMock = new Mock();
+ _organizationRightsServiceMock = new Mock();
+ _itContractRightServiceMock = new Mock>();
+ _itSystemRightServiceMock = new Mock>();
+ _itProjectRightServiceMock = new Mock>();
+ _organizationUnitRightServiceMock = new Mock>();
+ _ssoUserIdentityRepository = new Mock();
+
+ _sut = new HandleUserBeingDeleted(_dprApplicationServiceMock.Object,
+ _organizationRightsServiceMock.Object,
+ _itContractRightServiceMock.Object,
+ _itSystemRightServiceMock.Object,
+ _itProjectRightServiceMock.Object,
+ _organizationUnitRightServiceMock.Object,
+ _ssoUserIdentityRepository.Object);
+ }
+
+ [Fact]
+ public void Handle_Deletes_All_User_Rights()
+ {
+ var roleId = A();
+ var organizationRole = A();
+ var user = SetupUser(roleId, organizationRole);
+
+ _sut.Handle(new EntityBeingDeletedEvent(user));
+
+ _dprApplicationServiceMock.Verify(x => x.RemoveRole(It.IsAny(), roleId, user.Id), Times.AtLeastOnce);
+ Assert.Empty(user.DataProcessingRegistrationRights);
+
+ _organizationRightsServiceMock.Verify(x => x.RemoveRole(It.IsAny(), user.Id, organizationRole), Times.AtLeastOnce);
+ Assert.Empty(user.OrganizationRights);
+
+ _itContractRightServiceMock.Verify(x => x.RemoveRole(It.IsAny(), roleId, user.Id), Times.AtLeastOnce);
+ Assert.Empty(user.ItContractRights);
+
+ _itSystemRightServiceMock.Verify(x => x.RemoveRole(It.IsAny(), roleId, user.Id), Times.AtLeastOnce);
+ Assert.Empty(user.ItSystemRights);
+
+ _itProjectRightServiceMock.Verify(x => x.RemoveRole(It.IsAny(), roleId, user.Id), Times.AtLeastOnce);
+ Assert.Empty(user.ItProjectRights);
+
+ _organizationUnitRightServiceMock.Verify(x => x.RemoveRole(It.IsAny(), roleId, user.Id), Times.AtLeastOnce);
+ Assert.Empty(user.OrganizationUnitRights);
+
+ _ssoUserIdentityRepository.Verify(x => x.DeleteIdentitiesForUser(user.SsoIdentities), Times.AtLeastOnce);
+ Assert.Empty(user.SsoIdentities);
+ }
+
+ private void ExpectDprApplicationRemoveRoleReturns(int objectId, int roleId, int userId, Result result)
+ {
+ _dprApplicationServiceMock.Setup(x => x.RemoveRole(objectId, roleId, userId)).Returns(result);
+ }
+
+ private void ExpectOrganizationRemoveRoleReturns(int organizationId, OrganizationRole organizationRole, int userId, Result result)
+ {
+ _organizationRightsServiceMock.Setup(x => x.RemoveRole(organizationId, userId, organizationRole)).Returns(result);
+ }
+
+ private void ExpectItContractRemoveRoleReturns(ItContract roleObject, int roleId, int userId, Result result)
+ {
+ _itContractRightServiceMock.Setup(x => x.RemoveRole(roleObject, roleId, userId)).Returns(result);
+ }
+
+ private void ExpectItSystemRemoveRoleReturns(ItSystemUsage roleObject, int roleId, int userId, Result result)
+ {
+ _itSystemRightServiceMock.Setup(x => x.RemoveRole(roleObject, roleId, userId)).Returns(result);
+ }
+
+ private void ExpectItProjectRemoveRoleReturns(ItProject roleObject, int roleId, int userId, Result result)
+ {
+ _itProjectRightServiceMock.Setup(x => x.RemoveRole(roleObject, roleId, userId)).Returns(result);
+ }
+
+ private void ExpectOrganizationUnitRemoveRoleReturns(OrganizationUnit roleObject, int roleId, int userId, Result result)
+ {
+ _organizationUnitRightServiceMock.Setup(x => x.RemoveRole(roleObject, roleId, userId)).Returns(result);
+ }
+
+ private void ExpectDeleteIdentitiesForUserReturns(IEnumerable identities)
+ {
+ _ssoUserIdentityRepository.Setup(x => x.DeleteIdentitiesForUser(identities));
+ }
+
+ private User SetupUser(int roleId, OrganizationRole organizationRole)
+ {
+ var user = new User
+ {
+ Id = A(),
+ DataProcessingRegistrationRights = new List(),
+ OrganizationRights = new List(),
+ ItContractRights = new List(),
+ ItSystemRights = new List(),
+ ItProjectStatuses = new List