1
0
mirror of https://github.com/bitwarden/server.git synced 2024-11-21 12:05:42 +01:00

[PM-11667] Remove all code related to the outdated custom permissions 'Edit/Delete Assigned Collections' (#4736)

This commit is contained in:
Rui Tomé 2024-09-10 15:06:13 +01:00 committed by GitHub
parent 4c0f8d54f3
commit add8783e31
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 5 additions and 580 deletions

View File

@ -17,7 +17,6 @@ using Bit.Core.Context;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Exceptions; using Bit.Core.Exceptions;
using Bit.Core.Models.Business; using Bit.Core.Models.Business;
using Bit.Core.Models.Data;
using Bit.Core.Models.Data.Organizations.OrganizationUsers; using Bit.Core.Models.Data.Organizations.OrganizationUsers;
using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface; using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface;
using Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces; using Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces;
@ -108,16 +107,6 @@ public class OrganizationUsersController : Controller
var response = new OrganizationUserDetailsResponseModel(organizationUser.Item1, organizationUser.Item2); var response = new OrganizationUserDetailsResponseModel(organizationUser.Item1, organizationUser.Item2);
// Downgrade Custom users with no other permissions than 'Edit/Delete Assigned Collections' to User
response.Type = GetFlexibleCollectionsUserType(response.Type, response.Permissions);
// Set 'Edit/Delete Assigned Collections' custom permissions to false
if (response.Permissions is not null)
{
response.Permissions.EditAssignedCollections = false;
response.Permissions.DeleteAssignedCollections = false;
}
if (includeGroups) if (includeGroups)
{ {
response.Groups = await _groupRepository.GetManyIdsByUserIdAsync(organizationUser.Item1.Id); response.Groups = await _groupRepository.GetManyIdsByUserIdAsync(organizationUser.Item1.Id);
@ -638,35 +627,6 @@ public class OrganizationUsersController : Controller
new OrganizationUserBulkResponseModel(r.Item1.Id, r.Item2))); new OrganizationUserBulkResponseModel(r.Item1.Id, r.Item2)));
} }
private OrganizationUserType GetFlexibleCollectionsUserType(OrganizationUserType type, Permissions permissions)
{
// Downgrade Custom users with no other permissions than 'Edit/Delete Assigned Collections' to User
if (type == OrganizationUserType.Custom && permissions is not null)
{
if ((permissions.EditAssignedCollections || permissions.DeleteAssignedCollections) &&
permissions is
{
AccessEventLogs: false,
AccessImportExport: false,
AccessReports: false,
CreateNewCollections: false,
EditAnyCollection: false,
DeleteAnyCollection: false,
ManageGroups: false,
ManagePolicies: false,
ManageSso: false,
ManageUsers: false,
ManageResetPassword: false,
ManageScim: false
})
{
return OrganizationUserType.User;
}
}
return type;
}
private async Task<ListResponseModel<OrganizationUserUserDetailsResponseModel>> Get_vNext(Guid orgId, private async Task<ListResponseModel<OrganizationUserUserDetailsResponseModel>> Get_vNext(Guid orgId,
bool includeGroups = false, bool includeCollections = false) bool includeGroups = false, bool includeCollections = false)
{ {

View File

@ -71,37 +71,6 @@ public class ProfileOrganizationResponseModel : ResponseModel
KeyConnectorEnabled = ssoConfigData.MemberDecryptionType == MemberDecryptionType.KeyConnector && !string.IsNullOrEmpty(ssoConfigData.KeyConnectorUrl); KeyConnectorEnabled = ssoConfigData.MemberDecryptionType == MemberDecryptionType.KeyConnector && !string.IsNullOrEmpty(ssoConfigData.KeyConnectorUrl);
KeyConnectorUrl = ssoConfigData.KeyConnectorUrl; KeyConnectorUrl = ssoConfigData.KeyConnectorUrl;
} }
// Downgrade Custom users with no other permissions than 'Edit/Delete Assigned Collections' to User
if (Type == OrganizationUserType.Custom && Permissions is not null)
{
if ((Permissions.EditAssignedCollections || Permissions.DeleteAssignedCollections) &&
Permissions is
{
AccessEventLogs: false,
AccessImportExport: false,
AccessReports: false,
CreateNewCollections: false,
EditAnyCollection: false,
DeleteAnyCollection: false,
ManageGroups: false,
ManagePolicies: false,
ManageSso: false,
ManageUsers: false,
ManageResetPassword: false,
ManageScim: false
})
{
organization.Type = OrganizationUserType.User;
}
}
// Set 'Edit/Delete Assigned Collections' custom permissions to false
if (Permissions is not null)
{
Permissions.EditAssignedCollections = false;
Permissions.DeleteAssignedCollections = false;
}
} }
public Guid Id { get; set; } public Guid Id { get; set; }

View File

@ -1,7 +1,6 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using Bit.Core.Entities; using Bit.Core.Entities;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Models.Data;
using Bit.Core.Models.Data.Organizations.OrganizationUsers; using Bit.Core.Models.Data.Organizations.OrganizationUsers;
namespace Bit.Api.AdminConsole.Public.Models; namespace Bit.Api.AdminConsole.Public.Models;
@ -17,7 +16,7 @@ public abstract class MemberBaseModel
throw new ArgumentNullException(nameof(user)); throw new ArgumentNullException(nameof(user));
} }
Type = GetFlexibleCollectionsUserType(user.Type, user.GetPermissions()); Type = user.Type;
ExternalId = user.ExternalId; ExternalId = user.ExternalId;
ResetPasswordEnrolled = user.ResetPasswordKey != null; ResetPasswordEnrolled = user.ResetPasswordKey != null;
@ -34,7 +33,7 @@ public abstract class MemberBaseModel
throw new ArgumentNullException(nameof(user)); throw new ArgumentNullException(nameof(user));
} }
Type = GetFlexibleCollectionsUserType(user.Type, user.GetPermissions()); Type = user.Type;
ExternalId = user.ExternalId; ExternalId = user.ExternalId;
ResetPasswordEnrolled = user.ResetPasswordKey != null; ResetPasswordEnrolled = user.ResetPasswordKey != null;
@ -66,34 +65,4 @@ public abstract class MemberBaseModel
/// default to false. /// default to false.
/// </summary> /// </summary>
public PermissionsModel? Permissions { get; set; } public PermissionsModel? Permissions { get; set; }
// TODO: AC-2188 - Remove this method when the custom users with no other permissions than 'Edit/Delete Assigned Collections' are migrated
private OrganizationUserType GetFlexibleCollectionsUserType(OrganizationUserType type, Permissions permissions)
{
// Downgrade Custom users with no other permissions than 'Edit/Delete Assigned Collections' to User
if (type == OrganizationUserType.Custom)
{
if ((permissions.EditAssignedCollections || permissions.DeleteAssignedCollections) &&
permissions is
{
AccessEventLogs: false,
AccessImportExport: false,
AccessReports: false,
CreateNewCollections: false,
EditAnyCollection: false,
DeleteAnyCollection: false,
ManageGroups: false,
ManagePolicies: false,
ManageSso: false,
ManageUsers: false,
ManageResetPassword: false,
ManageScim: false
})
{
return OrganizationUserType.User;
}
}
return type;
}
} }

View File

@ -1,6 +1,4 @@
using Bit.Core.Models.Data; namespace Bit.Core.Enums;
namespace Bit.Core.Enums;
public enum OrganizationUserType : byte public enum OrganizationUserType : byte
{ {
@ -10,35 +8,3 @@ public enum OrganizationUserType : byte
// Manager = 3 has been intentionally permanently deleted // Manager = 3 has been intentionally permanently deleted
Custom = 4, Custom = 4,
} }
public static class OrganizationUserTypeExtensions
{
public static OrganizationUserType GetFlexibleCollectionsUserType(this OrganizationUserType type, Permissions permissions)
{
// Downgrade Custom users with no other permissions than 'Edit/Delete Assigned Collections' to User
if (type == OrganizationUserType.Custom && permissions is not null)
{
if ((permissions.EditAssignedCollections || permissions.DeleteAssignedCollections) &&
permissions is
{
AccessEventLogs: false,
AccessImportExport: false,
AccessReports: false,
CreateNewCollections: false,
EditAnyCollection: false,
DeleteAnyCollection: false,
ManageGroups: false,
ManagePolicies: false,
ManageSso: false,
ManageUsers: false,
ManageResetPassword: false,
ManageScim: false
})
{
return OrganizationUserType.User;
}
}
return type;
}
}

View File

@ -10,10 +10,6 @@ public class Permissions
public bool CreateNewCollections { get; set; } public bool CreateNewCollections { get; set; }
public bool EditAnyCollection { get; set; } public bool EditAnyCollection { get; set; }
public bool DeleteAnyCollection { get; set; } public bool DeleteAnyCollection { get; set; }
[Obsolete("Pre-Flexible Collections logic.")]
public bool EditAssignedCollections { get; set; }
[Obsolete("Pre-Flexible Collections logic.")]
public bool DeleteAssignedCollections { get; set; }
public bool ManageGroups { get; set; } public bool ManageGroups { get; set; }
public bool ManagePolicies { get; set; } public bool ManagePolicies { get; set; }
public bool ManageSso { get; set; } public bool ManageSso { get; set; }
@ -30,8 +26,6 @@ public class Permissions
(CreateNewCollections, "createnewcollections"), (CreateNewCollections, "createnewcollections"),
(EditAnyCollection, "editanycollection"), (EditAnyCollection, "editanycollection"),
(DeleteAnyCollection, "deleteanycollection"), (DeleteAnyCollection, "deleteanycollection"),
(EditAssignedCollections, "editassignedcollections"),
(DeleteAssignedCollections, "deleteassignedcollections"),
(ManageGroups, "managegroups"), (ManageGroups, "managegroups"),
(ManagePolicies, "managepolicies"), (ManagePolicies, "managepolicies"),
(ManageSso, "managesso"), (ManageSso, "managesso"),

View File

@ -1,5 +1,4 @@
using Bit.Core.Enums; using Bit.Core.Models.Data.Organizations.OrganizationUsers;
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
using Bit.Core.Repositories; using Bit.Core.Repositories;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces; using Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
@ -33,15 +32,6 @@ public class OrganizationUserUserDetailsQuery : IOrganizationUserUserDetailsQuer
{ {
var userPermissions = o.GetPermissions(); var userPermissions = o.GetPermissions();
// Downgrade Custom users with no other permissions than 'Edit/Delete Assigned Collections' to User
o.Type = o.Type.GetFlexibleCollectionsUserType(userPermissions);
if (userPermissions is not null)
{
userPermissions.EditAssignedCollections = false;
userPermissions.DeleteAssignedCollections = false;
}
o.Permissions = CoreHelpers.ClassToJsonData(userPermissions); o.Permissions = CoreHelpers.ClassToJsonData(userPermissions);
return o; return o;

View File

@ -509,8 +509,6 @@ public class CurrentContext : ICurrentContext
CreateNewCollections = hasClaim("createnewcollections"), CreateNewCollections = hasClaim("createnewcollections"),
EditAnyCollection = hasClaim("editanycollection"), EditAnyCollection = hasClaim("editanycollection"),
DeleteAnyCollection = hasClaim("deleteanycollection"), DeleteAnyCollection = hasClaim("deleteanycollection"),
EditAssignedCollections = hasClaim("editassignedcollections"),
DeleteAssignedCollections = hasClaim("deleteassignedcollections"),
ManageGroups = hasClaim("managegroups"), ManageGroups = hasClaim("managegroups"),
ManagePolicies = hasClaim("managepolicies"), ManagePolicies = hasClaim("managepolicies"),
ManageSso = hasClaim("managesso"), ManageSso = hasClaim("managesso"),

View File

@ -10,7 +10,6 @@ using Bit.Core.Billing.Enums;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Models.Data; using Bit.Core.Models.Data;
using Bit.Core.Repositories; using Bit.Core.Repositories;
using Bit.Test.Common.AutoFixture.Attributes;
using Bit.Test.Common.Helpers; using Bit.Test.Common.Helpers;
using Xunit; using Xunit;
@ -109,26 +108,6 @@ public class MembersControllerTests : IClassFixture<ApiApplicationFactory>, IAsy
result.Permissions); result.Permissions);
} }
[Theory]
[BitAutoData(true, true)]
[BitAutoData(false, true)]
[BitAutoData(true, false)]
public async Task Get_CustomMember_WithDeprecatedPermissions_TreatsAsUser(bool editAssignedCollections, bool deleteAssignedCollections)
{
var (email, orgUser) = await OrganizationTestHelpers.CreateNewUserWithAccountAsync(_factory, _organization.Id,
OrganizationUserType.Custom, new Permissions { EditAssignedCollections = editAssignedCollections, DeleteAssignedCollections = deleteAssignedCollections });
var response = await _client.GetAsync($"/public/members/{orgUser.Id}");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var result = await response.Content.ReadFromJsonAsync<MemberResponseModel>();
Assert.NotNull(result);
Assert.Equal(email, result.Email);
Assert.Equal(OrganizationUserType.User, result.Type);
Assert.Null(result.Permissions);
}
[Fact] [Fact]
public async Task Post_CustomMember_Success() public async Task Post_CustomMember_Success()
{ {

View File

@ -1,8 +1,5 @@
using Bit.Core.Enums; using Bit.Core.Models.Data.Organizations.OrganizationUsers;
using Bit.Core.Models.Data;
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
using Bit.Core.Repositories; using Bit.Core.Repositories;
using Bit.Core.Utilities;
using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes; using Bit.Test.Common.AutoFixture.Attributes;
using Core.AdminConsole.OrganizationFeatures.OrganizationUsers; using Core.AdminConsole.OrganizationFeatures.OrganizationUsers;
@ -15,33 +12,6 @@ namespace Api.Test.AdminConsole.Queries;
[SutProviderCustomize] [SutProviderCustomize]
public class OrganizationUserUserDetailsQueryTests public class OrganizationUserUserDetailsQueryTests
{ {
[Theory]
[BitAutoData]
public async Task Get_DowngradesCustomUsersWithDeprecatedPermissions(
ICollection<OrganizationUserUserDetails> organizationUsers,
SutProvider<OrganizationUserUserDetailsQuery> sutProvider,
Guid organizationId)
{
Get_Setup(organizationUsers, sutProvider, organizationId);
var customUser = organizationUsers.First();
customUser.Type = OrganizationUserType.Custom;
customUser.Permissions = CoreHelpers.ClassToJsonData(new Permissions
{
EditAssignedCollections = true,
DeleteAssignedCollections = true,
});
var response = await sutProvider.Sut.GetOrganizationUserUserDetails(new OrganizationUserUserDetailsQueryRequest { OrganizationId = organizationId });
var customUserResponse = response.First(r => r.Id == organizationUsers.First().Id);
Assert.Equal(OrganizationUserType.User, customUserResponse.Type);
var customUserPermissions = customUserResponse.GetPermissions();
Assert.False(customUserPermissions.EditAssignedCollections);
Assert.False(customUserPermissions.DeleteAssignedCollections);
}
[Theory] [Theory]
[BitAutoData] [BitAutoData]
public async Task Get_HandlesNullPermissionsObject( public async Task Get_HandlesNullPermissionsObject(
@ -56,37 +26,6 @@ public class OrganizationUserUserDetailsQueryTests
Assert.True(response.All(r => organizationUsers.Any(ou => ou.Id == r.Id))); Assert.True(response.All(r => organizationUsers.Any(ou => ou.Id == r.Id)));
} }
[Theory]
[BitAutoData]
public async Task Get_SetsDeprecatedCustomPermissionstoFalse(
ICollection<OrganizationUserUserDetails> organizationUsers,
SutProvider<OrganizationUserUserDetailsQuery> sutProvider,
Guid organizationId)
{
Get_Setup(organizationUsers, sutProvider, organizationId);
var customUser = organizationUsers.First();
customUser.Type = OrganizationUserType.Custom;
customUser.Permissions = CoreHelpers.ClassToJsonData(new Permissions
{
AccessReports = true,
EditAssignedCollections = true,
DeleteAssignedCollections = true,
AccessEventLogs = true
});
var response = await sutProvider.Sut.GetOrganizationUserUserDetails(new OrganizationUserUserDetailsQueryRequest { OrganizationId = organizationId });
var customUserResponse = response.First(r => r.Id == organizationUsers.First().Id);
Assert.Equal(OrganizationUserType.Custom, customUserResponse.Type);
var customUserPermissions = customUserResponse.GetPermissions();
Assert.True(customUserPermissions.AccessReports);
Assert.True(customUserPermissions.AccessEventLogs);
Assert.False(customUserPermissions.EditAssignedCollections);
Assert.False(customUserPermissions.DeleteAssignedCollections);
}
[Theory] [Theory]
[BitAutoData] [BitAutoData]
public async Task Get_ReturnsUsers( public async Task Get_ReturnsUsers(

View File

@ -15,8 +15,6 @@ public class PermissionsTests
"\"createNewCollections\": true,", "\"createNewCollections\": true,",
"\"editAnyCollection\": true,", "\"editAnyCollection\": true,",
"\"deleteAnyCollection\": true,", "\"deleteAnyCollection\": true,",
"\"editAssignedCollections\": false,",
"\"deleteAssignedCollections\": false,",
"\"manageGroups\": false,", "\"manageGroups\": false,",
"\"managePolicies\": false,", "\"managePolicies\": false,",
"\"manageSso\": false,", "\"manageSso\": false,",
@ -36,8 +34,6 @@ public class PermissionsTests
CreateNewCollections = true, CreateNewCollections = true,
EditAnyCollection = true, EditAnyCollection = true,
DeleteAnyCollection = true, DeleteAnyCollection = true,
EditAssignedCollections = false,
DeleteAssignedCollections = false,
ManageGroups = false, ManageGroups = false,
ManagePolicies = false, ManagePolicies = false,
ManageSso = false, ManageSso = false,

View File

@ -1,335 +0,0 @@
using System.Text.Json;
using Bit.Core.AdminConsole.Entities;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Models.Data;
using Bit.Core.Repositories;
using Bit.Core.Utilities;
using Bit.Infrastructure.IntegrationTest.Services;
using Newtonsoft.Json.Linq;
using Xunit;
namespace Bit.Infrastructure.IntegrationTest.AdminConsole.Migrations;
public class FinalFlexibleCollectionsDataMigrationsTests
{
private const string _migrationName = "FinalFlexibleCollectionsDataMigrations";
[DatabaseTheory, DatabaseData(MigrationName = _migrationName)]
public async Task RunMigration_WithEditAssignedCollections_WithCustomUserType_MigratesToUserNullPermissions(
IUserRepository userRepository,
IOrganizationRepository organizationRepository,
IOrganizationUserRepository organizationUserRepository,
IMigrationTesterService migrationTester)
{
// Setup data
var orgUser = await SetupData(
userRepository, organizationRepository, organizationUserRepository,
OrganizationUserType.Custom, editAssignedCollections: true, deleteAssignedCollections: false);
// Run data migration
migrationTester.ApplyMigration();
// Assert that the user was migrated to a User type with null permissions
var migratedOrgUser = await organizationUserRepository.GetByIdAsync(orgUser.Id);
Assert.NotNull(migratedOrgUser);
Assert.Equal(orgUser.Id, migratedOrgUser.Id);
Assert.Equal(OrganizationUserType.User, migratedOrgUser.Type);
Assert.Null(migratedOrgUser.Permissions);
}
[DatabaseTheory, DatabaseData(MigrationName = _migrationName)]
public async Task RunMigration_WithDeleteAssignedCollections_WithCustomUserType_MigratesToUserNullPermissions(
IUserRepository userRepository,
IOrganizationRepository organizationRepository,
IOrganizationUserRepository organizationUserRepository,
IMigrationTesterService migrationTester)
{
// Setup data
var orgUser = await SetupData(
userRepository, organizationRepository, organizationUserRepository,
OrganizationUserType.Custom, editAssignedCollections: false, deleteAssignedCollections: true);
// Run data migration
migrationTester.ApplyMigration();
// Assert that the user was migrated to a User type with null permissions
var migratedOrgUser = await organizationUserRepository.GetByIdAsync(orgUser.Id);
Assert.NotNull(migratedOrgUser);
Assert.Equal(orgUser.Id, migratedOrgUser.Id);
Assert.Equal(OrganizationUserType.User, migratedOrgUser.Type);
Assert.Null(migratedOrgUser.Permissions);
}
[DatabaseTheory, DatabaseData(MigrationName = _migrationName)]
public async Task RunMigration_WithEditAndDeleteAssignedCollections_WithCustomUserType_MigratesToUserNullPermissions(
IUserRepository userRepository,
IOrganizationRepository organizationRepository,
IOrganizationUserRepository organizationUserRepository,
IMigrationTesterService migrationTester)
{
// Setup data
var orgUser = await SetupData(
userRepository, organizationRepository, organizationUserRepository,
OrganizationUserType.Custom, editAssignedCollections: true, deleteAssignedCollections: true);
// Run data migration
migrationTester.ApplyMigration();
// Assert that the user was migrated to a User type with null permissions
var migratedOrgUser = await organizationUserRepository.GetByIdAsync(orgUser.Id);
Assert.NotNull(migratedOrgUser);
Assert.Equal(orgUser.Id, migratedOrgUser.Id);
Assert.Equal(OrganizationUserType.User, migratedOrgUser.Type);
Assert.Null(migratedOrgUser.Permissions);
}
[DatabaseTheory, DatabaseData(MigrationName = _migrationName)]
public async Task RunMigration_WithoutAssignedCollectionsPermissions_WithCustomUserType_RemovesAssignedCollectionsPermissions(
IUserRepository userRepository,
IOrganizationRepository organizationRepository,
IOrganizationUserRepository organizationUserRepository,
IMigrationTesterService migrationTester)
{
// Setup data
var orgUser = await SetupData(
userRepository, organizationRepository, organizationUserRepository, OrganizationUserType.Custom,
editAssignedCollections: false, deleteAssignedCollections: false, accessEventLogs: true);
// Run data migration
migrationTester.ApplyMigration();
// Assert that the user kept the accessEventLogs permission and lost the editAssignedCollections and deleteAssignedCollections permissions
var migratedOrgUser = await organizationUserRepository.GetByIdAsync(orgUser.Id);
Assert.NotNull(migratedOrgUser);
Assert.Equal(orgUser.Id, migratedOrgUser.Id);
Assert.Equal(OrganizationUserType.Custom, migratedOrgUser.Type);
Assert.NotEqual(orgUser.Permissions, migratedOrgUser.Permissions);
Assert.NotNull(migratedOrgUser.Permissions);
Assert.Contains("accessEventLogs", orgUser.Permissions);
Assert.Contains("editAssignedCollections", orgUser.Permissions);
Assert.Contains("deleteAssignedCollections", orgUser.Permissions);
Assert.Contains("accessEventLogs", migratedOrgUser.Permissions);
var migratedOrgUserPermissions = migratedOrgUser.GetPermissions();
Assert.NotNull(migratedOrgUserPermissions);
Assert.True(migratedOrgUserPermissions.AccessEventLogs);
Assert.DoesNotContain("editAssignedCollections", migratedOrgUser.Permissions);
Assert.DoesNotContain("deleteAssignedCollections", migratedOrgUser.Permissions);
}
[DatabaseTheory, DatabaseData(MigrationName = _migrationName)]
public async Task RunMigration_WithAdminUserType_RemovesAssignedCollectionsPermissions(
IUserRepository userRepository,
IOrganizationRepository organizationRepository,
IOrganizationUserRepository organizationUserRepository,
IMigrationTesterService migrationTester)
{
// Setup data
var orgUser = await SetupData(
userRepository, organizationRepository, organizationUserRepository, OrganizationUserType.Admin,
editAssignedCollections: false, deleteAssignedCollections: false, accessEventLogs: true);
// Run data migration
migrationTester.ApplyMigration();
// Assert that the user kept the Admin type and lost the editAssignedCollections and deleteAssignedCollections
// permissions but kept the accessEventLogs permission
var migratedOrgUser = await organizationUserRepository.GetByIdAsync(orgUser.Id);
Assert.NotNull(migratedOrgUser);
Assert.Equal(orgUser.Id, migratedOrgUser.Id);
Assert.Equal(OrganizationUserType.Admin, migratedOrgUser.Type);
Assert.NotEqual(orgUser.Permissions, migratedOrgUser.Permissions);
Assert.NotNull(migratedOrgUser.Permissions);
Assert.Contains("accessEventLogs", orgUser.Permissions);
Assert.Contains("editAssignedCollections", orgUser.Permissions);
Assert.Contains("deleteAssignedCollections", orgUser.Permissions);
Assert.Contains("accessEventLogs", migratedOrgUser.Permissions);
Assert.True(migratedOrgUser.GetPermissions().AccessEventLogs);
Assert.DoesNotContain("editAssignedCollections", migratedOrgUser.Permissions);
Assert.DoesNotContain("deleteAssignedCollections", migratedOrgUser.Permissions);
}
[DatabaseTheory, DatabaseData(MigrationName = _migrationName)]
public async Task RunMigration_WithoutAssignedCollectionsPermissions_DoesNothing(
IUserRepository userRepository,
IOrganizationRepository organizationRepository,
IOrganizationUserRepository organizationUserRepository,
IMigrationTesterService migrationTester)
{
// Setup data
var orgUser = await SetupData(
userRepository, organizationRepository, organizationUserRepository, OrganizationUserType.Custom,
editAssignedCollections: false, deleteAssignedCollections: false, accessEventLogs: false);
// Remove the editAssignedCollections and deleteAssignedCollections permissions
orgUser.Permissions = JsonSerializer.Serialize(new
{
AccessEventLogs = false,
AccessImportExport = false,
AccessReports = false,
CreateNewCollections = false,
EditAnyCollection = false,
DeleteAnyCollection = false,
ManageGroups = false,
ManagePolicies = false,
ManageSso = false,
ManageUsers = false,
ManageResetPassword = false,
ManageScim = false
}, JsonHelpers.CamelCase);
await organizationUserRepository.ReplaceAsync(orgUser);
// Run data migration
migrationTester.ApplyMigration();
// Assert that the user remained unchanged
var migratedOrgUser = await organizationUserRepository.GetByIdAsync(orgUser.Id);
Assert.NotNull(migratedOrgUser);
Assert.Equal(orgUser.Id, migratedOrgUser.Id);
Assert.Equal(OrganizationUserType.Custom, orgUser.Type);
Assert.Equal(OrganizationUserType.Custom, migratedOrgUser.Type);
Assert.NotNull(migratedOrgUser.Permissions);
// Assert that the permissions remain unchanged by comparing JSON data, ignoring the order of properties
Assert.True(JToken.DeepEquals(JObject.Parse(orgUser.Permissions), JObject.Parse(migratedOrgUser.Permissions)));
}
[DatabaseTheory, DatabaseData(MigrationName = _migrationName)]
public async Task RunMigration_HandlesNull(
IUserRepository userRepository,
IOrganizationRepository organizationRepository,
IOrganizationUserRepository organizationUserRepository,
IMigrationTesterService migrationTester)
{
// Setup data
var orgUser = await SetupData(
userRepository, organizationRepository, organizationUserRepository, OrganizationUserType.Custom,
editAssignedCollections: false, deleteAssignedCollections: false, accessEventLogs: false);
orgUser.Permissions = null;
await organizationUserRepository.ReplaceAsync(orgUser);
// Run data migration
migrationTester.ApplyMigration();
// Assert no changes
var migratedOrgUser = await organizationUserRepository.GetByIdAsync(orgUser.Id);
Assert.NotNull(migratedOrgUser);
Assert.Equal(orgUser.Id, migratedOrgUser.Id);
Assert.Equal(orgUser.Type, migratedOrgUser.Type);
Assert.Null(migratedOrgUser.Permissions);
}
[DatabaseTheory, DatabaseData(MigrationName = _migrationName)]
public async Task RunMigration_HandlesNullString(
IUserRepository userRepository,
IOrganizationRepository organizationRepository,
IOrganizationUserRepository organizationUserRepository,
IMigrationTesterService migrationTester)
{
// Setup data
var orgUser = await SetupData(
userRepository, organizationRepository, organizationUserRepository, OrganizationUserType.Custom,
editAssignedCollections: false, deleteAssignedCollections: false, accessEventLogs: false);
// We haven't tracked down the source of this yet but it does occur in our cloud database
orgUser.Permissions = "NULL";
await organizationUserRepository.ReplaceAsync(orgUser);
// Run data migration
migrationTester.ApplyMigration();
// Assert no changes
var migratedOrgUser = await organizationUserRepository.GetByIdAsync(orgUser.Id);
Assert.NotNull(migratedOrgUser);
Assert.Equal(orgUser.Id, migratedOrgUser.Id);
Assert.Equal(orgUser.Type, migratedOrgUser.Type);
Assert.Equal("NULL", migratedOrgUser.Permissions);
}
[DatabaseTheory, DatabaseData(MigrationName = _migrationName)]
public async Task RunMigration_HandlesNonJsonValues(
IUserRepository userRepository,
IOrganizationRepository organizationRepository,
IOrganizationUserRepository organizationUserRepository,
IMigrationTesterService migrationTester)
{
// Setup data
var orgUser = await SetupData(
userRepository, organizationRepository, organizationUserRepository, OrganizationUserType.Custom,
editAssignedCollections: false, deleteAssignedCollections: false, accessEventLogs: false);
orgUser.Permissions = "asdfasdfasfd";
await organizationUserRepository.ReplaceAsync(orgUser);
// Run data migration
migrationTester.ApplyMigration();
// Assert no changes
var migratedOrgUser = await organizationUserRepository.GetByIdAsync(orgUser.Id);
Assert.NotNull(migratedOrgUser);
Assert.Equal(orgUser.Id, migratedOrgUser.Id);
Assert.Equal(orgUser.Type, migratedOrgUser.Type);
Assert.Equal("asdfasdfasfd", migratedOrgUser.Permissions);
}
private async Task<OrganizationUser> SetupData(
IUserRepository userRepository,
IOrganizationRepository organizationRepository,
IOrganizationUserRepository organizationUserRepository,
OrganizationUserType organizationUserType,
bool editAssignedCollections,
bool deleteAssignedCollections,
bool accessEventLogs = false)
{
var permissions = new Permissions
{
AccessEventLogs = accessEventLogs,
AccessImportExport = false,
AccessReports = false,
CreateNewCollections = false,
EditAnyCollection = false,
DeleteAnyCollection = false,
EditAssignedCollections = editAssignedCollections,
DeleteAssignedCollections = deleteAssignedCollections,
ManageGroups = false,
ManagePolicies = false,
ManageSso = false,
ManageUsers = false,
ManageResetPassword = false,
ManageScim = false
};
var user = await userRepository.CreateAsync(new User
{
Name = "Test User 1",
Email = $"test+{Guid.NewGuid()}@example.com",
ApiKey = "TEST",
SecurityStamp = "stamp",
Kdf = KdfType.PBKDF2_SHA256,
KdfIterations = 1,
KdfMemory = 2,
KdfParallelism = 3
});
var organization = await organizationRepository.CreateAsync(new Organization
{
Name = "Test Org",
BillingEmail = user.Email, // TODO: EF does not enforce this being NOT NULl
Plan = "Test", // TODO: EF does not enforce this being NOT NULl
PrivateKey = "privatekey",
});
var orgUser = await organizationUserRepository.CreateAsync(new OrganizationUser
{
OrganizationId = organization.Id,
UserId = user.Id,
Status = OrganizationUserStatusType.Confirmed,
ResetPasswordKey = "resetpasswordkey1",
Type = organizationUserType,
Permissions = JsonSerializer.Serialize(permissions, JsonHelpers.CamelCase)
});
return orgUser;
}
}