mirror of
https://github.com/bitwarden/server.git
synced 2025-07-01 16:12:49 -05:00
Merge branch 'master' into feature/flexible-collections
This commit is contained in:
@ -1,6 +1,4 @@
|
||||
using Bit.Core.OrganizationFeatures.OrganizationUsers;
|
||||
using Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||
using Bit.Scim.Groups;
|
||||
using Bit.Scim.Groups;
|
||||
using Bit.Scim.Groups.Interfaces;
|
||||
using Bit.Scim.Users;
|
||||
using Bit.Scim.Users.Interfaces;
|
||||
@ -23,7 +21,6 @@ public static class ScimServiceCollectionExtensions
|
||||
|
||||
public static void AddScimUserCommands(this IServiceCollection services)
|
||||
{
|
||||
services.AddScoped<IDeleteOrganizationUserCommand, DeleteOrganizationUserCommand>();
|
||||
services.AddScoped<IPatchUserCommand, PatchUserCommand>();
|
||||
services.AddScoped<IPostUserCommand, PostUserCommand>();
|
||||
}
|
||||
|
@ -44,7 +44,7 @@
|
||||
<div class="float-right">
|
||||
@if (org.Status == OrganizationStatusType.Pending)
|
||||
{
|
||||
<a href="#" class="float-right" onclick="return resendOwnerInvite('@org.OrganizationId', '@org.OrganizationName');">
|
||||
<a href="#" class="float-right" onclick="return resendOwnerInvite('@org.OrganizationId');">
|
||||
<i class="fa fa-envelope-o fa-lg" title="Resend Setup Invite"></i>
|
||||
</a>
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<script>
|
||||
function resendOwnerInvite(orgId, orgName) {
|
||||
if (confirm('Resend invite to "' + orgName + '"?')) {
|
||||
function resendOwnerInvite(orgId) {
|
||||
if (confirm('Resend invite to organization?')) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: '@Url.Action("ResendOwnerInvite", "Organizations")' + '?id=' + orgId,
|
||||
|
@ -236,7 +236,7 @@ public class TwoFactorController : Controller
|
||||
[HttpPost("get-webauthn")]
|
||||
public async Task<TwoFactorWebAuthnResponseModel> GetWebAuthn([FromBody] SecretVerificationRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model, true);
|
||||
var user = await CheckAsync(model, false);
|
||||
var response = new TwoFactorWebAuthnResponseModel(user);
|
||||
return response;
|
||||
}
|
||||
@ -245,7 +245,7 @@ public class TwoFactorController : Controller
|
||||
[ApiExplorerSettings(IgnoreApi = true)] // Disable Swagger due to CredentialCreateOptions not converting properly
|
||||
public async Task<CredentialCreateOptions> GetWebAuthnChallenge([FromBody] SecretVerificationRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model, true);
|
||||
var user = await CheckAsync(model, false);
|
||||
var reg = await _userService.StartWebAuthnRegistrationAsync(user);
|
||||
return reg;
|
||||
}
|
||||
@ -254,7 +254,7 @@ public class TwoFactorController : Controller
|
||||
[HttpPost("webauthn")]
|
||||
public async Task<TwoFactorWebAuthnResponseModel> PutWebAuthn([FromBody] TwoFactorWebAuthnRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model, true);
|
||||
var user = await CheckAsync(model, false);
|
||||
|
||||
var success = await _userService.CompleteWebAuthRegistrationAsync(
|
||||
user, model.Id.Value, model.Name, model.DeviceResponse);
|
||||
@ -271,7 +271,7 @@ public class TwoFactorController : Controller
|
||||
public async Task<TwoFactorWebAuthnResponseModel> DeleteWebAuthn(
|
||||
[FromBody] TwoFactorWebAuthnDeleteRequestModel model)
|
||||
{
|
||||
var user = await CheckAsync(model, true);
|
||||
var user = await CheckAsync(model, false);
|
||||
await _userService.DeleteWebAuthnKeyAsync(user, model.Id.Value);
|
||||
var response = new TwoFactorWebAuthnResponseModel(user);
|
||||
return response;
|
||||
|
@ -30,6 +30,7 @@ public class OrganizationUsersController : Controller
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly ICountNewSmSeatsRequiredQuery _countNewSmSeatsRequiredQuery;
|
||||
private readonly IUpdateSecretsManagerSubscriptionCommand _updateSecretsManagerSubscriptionCommand;
|
||||
private readonly IUpdateOrganizationUserGroupsCommand _updateOrganizationUserGroupsCommand;
|
||||
|
||||
public OrganizationUsersController(
|
||||
IOrganizationRepository organizationRepository,
|
||||
@ -41,7 +42,8 @@ public class OrganizationUsersController : Controller
|
||||
IPolicyRepository policyRepository,
|
||||
ICurrentContext currentContext,
|
||||
ICountNewSmSeatsRequiredQuery countNewSmSeatsRequiredQuery,
|
||||
IUpdateSecretsManagerSubscriptionCommand updateSecretsManagerSubscriptionCommand)
|
||||
IUpdateSecretsManagerSubscriptionCommand updateSecretsManagerSubscriptionCommand,
|
||||
IUpdateOrganizationUserGroupsCommand updateOrganizationUserGroupsCommand)
|
||||
{
|
||||
_organizationRepository = organizationRepository;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
@ -53,6 +55,7 @@ public class OrganizationUsersController : Controller
|
||||
_currentContext = currentContext;
|
||||
_countNewSmSeatsRequiredQuery = countNewSmSeatsRequiredQuery;
|
||||
_updateSecretsManagerSubscriptionCommand = updateSecretsManagerSubscriptionCommand;
|
||||
_updateOrganizationUserGroupsCommand = updateOrganizationUserGroupsCommand;
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
@ -308,7 +311,7 @@ public class OrganizationUsersController : Controller
|
||||
}
|
||||
|
||||
var loggedInUserId = _userService.GetProperUserId(User);
|
||||
await _organizationService.UpdateUserGroupsAsync(organizationUser, model.GroupIds.Select(g => new Guid(g)), loggedInUserId);
|
||||
await _updateOrganizationUserGroupsCommand.UpdateUserGroupsAsync(organizationUser, model.GroupIds.Select(g => new Guid(g)), loggedInUserId);
|
||||
}
|
||||
|
||||
[HttpPut("{userId}/reset-password-enrollment")]
|
||||
|
@ -3,6 +3,7 @@ using Bit.Api.Models.Public.Request;
|
||||
using Bit.Api.Models.Public.Response;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Models.Business;
|
||||
using Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
@ -19,19 +20,22 @@ public class MembersController : Controller
|
||||
private readonly IOrganizationService _organizationService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly IUpdateOrganizationUserGroupsCommand _updateOrganizationUserGroupsCommand;
|
||||
|
||||
public MembersController(
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
IGroupRepository groupRepository,
|
||||
IOrganizationService organizationService,
|
||||
IUserService userService,
|
||||
ICurrentContext currentContext)
|
||||
ICurrentContext currentContext,
|
||||
IUpdateOrganizationUserGroupsCommand updateOrganizationUserGroupsCommand)
|
||||
{
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
_groupRepository = groupRepository;
|
||||
_organizationService = organizationService;
|
||||
_userService = userService;
|
||||
_currentContext = currentContext;
|
||||
_updateOrganizationUserGroupsCommand = updateOrganizationUserGroupsCommand;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -183,7 +187,7 @@ public class MembersController : Controller
|
||||
{
|
||||
return new NotFoundResult();
|
||||
}
|
||||
await _organizationService.UpdateUserGroupsAsync(existingUser, model.GroupIds, null);
|
||||
await _updateOrganizationUserGroupsCommand.UpdateUserGroupsAsync(existingUser, model.GroupIds, null);
|
||||
return new OkResult();
|
||||
}
|
||||
|
||||
|
@ -28,10 +28,6 @@ public class WebAuthnTokenProvider : IUserTwoFactorTokenProvider<User>
|
||||
public async Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<User> manager, User user)
|
||||
{
|
||||
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
||||
if (!(await userService.CanAccessPremium(user)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var webAuthnProvider = user.GetTwoFactorProvider(TwoFactorProviderType.WebAuthn);
|
||||
if (!HasProperMetaData(webAuthnProvider))
|
||||
@ -45,10 +41,6 @@ public class WebAuthnTokenProvider : IUserTwoFactorTokenProvider<User>
|
||||
public async Task<string> GenerateAsync(string purpose, UserManager<User> manager, User user)
|
||||
{
|
||||
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
||||
if (!(await userService.CanAccessPremium(user)))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.WebAuthn);
|
||||
var keys = LoadKeys(provider);
|
||||
@ -81,7 +73,7 @@ public class WebAuthnTokenProvider : IUserTwoFactorTokenProvider<User>
|
||||
public async Task<bool> ValidateAsync(string purpose, string token, UserManager<User> manager, User user)
|
||||
{
|
||||
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
||||
if (!(await userService.CanAccessPremium(user)) || string.IsNullOrWhiteSpace(token))
|
||||
if (string.IsNullOrWhiteSpace(token))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -57,7 +57,6 @@ public class TwoFactorProvider
|
||||
case TwoFactorProviderType.Duo:
|
||||
case TwoFactorProviderType.YubiKey:
|
||||
case TwoFactorProviderType.U2f: // Keep to ensure old U2f keys are considered premium
|
||||
case TwoFactorProviderType.WebAuthn:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
@ -46,4 +46,13 @@ public static class FeatureFlagKeys
|
||||
.Select(x => (string)x.GetRawConstantValue())
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public static Dictionary<string, string> GetLocalOverrideFlagValues()
|
||||
{
|
||||
// place overriding values when needed locally (offline), or return null
|
||||
return new Dictionary<string, string>()
|
||||
{
|
||||
{ TrustedDeviceEncryption, "true" }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -45,6 +45,7 @@ public static class OrganizationServiceCollectionExtensions
|
||||
services.AddOrganizationLicenseCommandsQueries();
|
||||
services.AddOrganizationDomainCommandsQueries();
|
||||
services.AddOrganizationAuthCommands();
|
||||
services.AddOrganizationUserCommands();
|
||||
services.AddOrganizationUserCommandsQueries();
|
||||
services.AddBaseOrganizationSubscriptionCommandsQueries();
|
||||
}
|
||||
@ -81,6 +82,12 @@ public static class OrganizationServiceCollectionExtensions
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddOrganizationUserCommands(this IServiceCollection services)
|
||||
{
|
||||
services.AddScoped<IDeleteOrganizationUserCommand, DeleteOrganizationUserCommand>();
|
||||
services.AddScoped<IUpdateOrganizationUserGroupsCommand, UpdateOrganizationUserGroupsCommand>();
|
||||
}
|
||||
|
||||
private static void AddOrganizationApiKeyCommandsQueries(this IServiceCollection services)
|
||||
{
|
||||
services.AddScoped<IGetOrganizationApiKeyQuery, GetOrganizationApiKeyQuery>();
|
||||
|
@ -0,0 +1,8 @@
|
||||
using Bit.Core.Entities;
|
||||
|
||||
namespace Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||
|
||||
public interface IUpdateOrganizationUserGroupsCommand
|
||||
{
|
||||
Task UpdateUserGroupsAsync(OrganizationUser organizationUser, IEnumerable<Guid> groupIds, Guid? loggedInUserId);
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
|
||||
namespace Bit.Core.OrganizationFeatures.OrganizationUsers;
|
||||
|
||||
public class UpdateOrganizationUserGroupsCommand : IUpdateOrganizationUserGroupsCommand
|
||||
{
|
||||
private readonly IEventService _eventService;
|
||||
private readonly IOrganizationService _organizationService;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
|
||||
public UpdateOrganizationUserGroupsCommand(
|
||||
IEventService eventService,
|
||||
IOrganizationService organizationService,
|
||||
IOrganizationUserRepository organizationUserRepository)
|
||||
{
|
||||
_eventService = eventService;
|
||||
_organizationService = organizationService;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
}
|
||||
|
||||
public async Task UpdateUserGroupsAsync(OrganizationUser organizationUser, IEnumerable<Guid> groupIds, Guid? loggedInUserId)
|
||||
{
|
||||
if (loggedInUserId.HasValue)
|
||||
{
|
||||
await _organizationService.ValidateOrganizationUserUpdatePermissions(organizationUser.OrganizationId, organizationUser.Type, null, organizationUser.GetPermissions());
|
||||
}
|
||||
await _organizationUserRepository.UpdateGroupsAsync(organizationUser.Id, groupIds);
|
||||
await _eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_UpdatedGroups);
|
||||
}
|
||||
}
|
@ -54,7 +54,6 @@ public interface IOrganizationService
|
||||
Task DeleteUserAsync(Guid organizationId, Guid userId);
|
||||
Task<List<Tuple<OrganizationUser, string>>> DeleteUsersAsync(Guid organizationId,
|
||||
IEnumerable<Guid> organizationUserIds, Guid? deletingUserId);
|
||||
Task UpdateUserGroupsAsync(OrganizationUser organizationUser, IEnumerable<Guid> groupIds, Guid? loggedInUserId);
|
||||
Task UpdateUserResetPasswordEnrollmentAsync(Guid organizationId, Guid userId, string resetPasswordKey, Guid? callingUserId);
|
||||
Task ImportAsync(Guid organizationId, Guid? importingUserId, IEnumerable<ImportedGroup> groups,
|
||||
IEnumerable<ImportedOrganizationUser> newUsers, IEnumerable<string> removeUserExternalIds,
|
||||
@ -82,4 +81,6 @@ public interface IOrganizationService
|
||||
|
||||
void ValidatePasswordManagerPlan(Models.StaticStore.Plan plan, OrganizationUpgrade upgrade);
|
||||
void ValidateSecretsManagerPlan(Models.StaticStore.Plan plan, OrganizationUpgrade upgrade);
|
||||
Task ValidateOrganizationUserUpdatePermissions(Guid organizationId, OrganizationUserType newType,
|
||||
OrganizationUserType? oldType, Permissions permissions);
|
||||
}
|
||||
|
@ -29,24 +29,12 @@ public class LaunchDarklyFeatureService : IFeatureService, IDisposable
|
||||
// support configuration directly from settings
|
||||
else if (globalSettings.LaunchDarkly?.FlagValues?.Any() is true)
|
||||
{
|
||||
var source = TestData.DataSource();
|
||||
foreach (var kvp in globalSettings.LaunchDarkly.FlagValues)
|
||||
{
|
||||
if (bool.TryParse(kvp.Value, out bool boolValue))
|
||||
{
|
||||
source.Update(source.Flag(kvp.Key).ValueForAll(LaunchDarkly.Sdk.LdValue.Of(boolValue)));
|
||||
}
|
||||
else if (int.TryParse(kvp.Value, out int intValue))
|
||||
{
|
||||
source.Update(source.Flag(kvp.Key).ValueForAll(LaunchDarkly.Sdk.LdValue.Of(intValue)));
|
||||
}
|
||||
else
|
||||
{
|
||||
source.Update(source.Flag(kvp.Key).ValueForAll(LaunchDarkly.Sdk.LdValue.Of(kvp.Value)));
|
||||
}
|
||||
}
|
||||
|
||||
ldConfig.DataSource(source);
|
||||
ldConfig.DataSource(BuildDataSource(globalSettings.LaunchDarkly.FlagValues));
|
||||
}
|
||||
// support local overrides
|
||||
else if (FeatureFlagKeys.GetLocalOverrideFlagValues()?.Any() is true)
|
||||
{
|
||||
ldConfig.DataSource(BuildDataSource(FeatureFlagKeys.GetLocalOverrideFlagValues()));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -187,4 +175,26 @@ public class LaunchDarklyFeatureService : IFeatureService, IDisposable
|
||||
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
private TestData BuildDataSource(Dictionary<string, string> values)
|
||||
{
|
||||
var source = TestData.DataSource();
|
||||
foreach (var kvp in values)
|
||||
{
|
||||
if (bool.TryParse(kvp.Value, out bool boolValue))
|
||||
{
|
||||
source.Update(source.Flag(kvp.Key).ValueForAll(LaunchDarkly.Sdk.LdValue.Of(boolValue)));
|
||||
}
|
||||
else if (int.TryParse(kvp.Value, out int intValue))
|
||||
{
|
||||
source.Update(source.Flag(kvp.Key).ValueForAll(LaunchDarkly.Sdk.LdValue.Of(intValue)));
|
||||
}
|
||||
else
|
||||
{
|
||||
source.Update(source.Flag(kvp.Key).ValueForAll(LaunchDarkly.Sdk.LdValue.Of(kvp.Value)));
|
||||
}
|
||||
}
|
||||
|
||||
return source;
|
||||
}
|
||||
}
|
||||
|
@ -1582,17 +1582,6 @@ public class OrganizationService : IOrganizationService
|
||||
return hasOtherOwner;
|
||||
}
|
||||
|
||||
public async Task UpdateUserGroupsAsync(OrganizationUser organizationUser, IEnumerable<Guid> groupIds, Guid? loggedInUserId)
|
||||
{
|
||||
if (loggedInUserId.HasValue)
|
||||
{
|
||||
await ValidateOrganizationUserUpdatePermissions(organizationUser.OrganizationId, organizationUser.Type, null, organizationUser.GetPermissions());
|
||||
}
|
||||
await _organizationUserRepository.UpdateGroupsAsync(organizationUser.Id, groupIds);
|
||||
await _eventService.LogOrganizationUserEventAsync(organizationUser,
|
||||
EventType.OrganizationUser_UpdatedGroups);
|
||||
}
|
||||
|
||||
public async Task UpdateUserResetPasswordEnrollmentAsync(Guid organizationId, Guid userId, string resetPasswordKey, Guid? callingUserId)
|
||||
{
|
||||
// Org User must be the same as the calling user and the organization ID associated with the user must match passed org ID
|
||||
@ -2032,7 +2021,7 @@ public class OrganizationService : IOrganizationService
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ValidateOrganizationUserUpdatePermissions(Guid organizationId, OrganizationUserType newType, OrganizationUserType? oldType, Permissions permissions)
|
||||
public async Task ValidateOrganizationUserUpdatePermissions(Guid organizationId, OrganizationUserType newType, OrganizationUserType? oldType, Permissions permissions)
|
||||
{
|
||||
if (await _currentContext.OrganizationOwner(organizationId))
|
||||
{
|
||||
|
@ -0,0 +1,50 @@
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.OrganizationFeatures.OrganizationUsers;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationUsers;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class UpdateOrganizationUserGroupsCommandTests
|
||||
{
|
||||
[Theory, BitAutoData]
|
||||
public async Task UpdateUserGroups_Passes(
|
||||
OrganizationUser organizationUser,
|
||||
IEnumerable<Guid> groupIds,
|
||||
SutProvider<UpdateOrganizationUserGroupsCommand> sutProvider)
|
||||
{
|
||||
await sutProvider.Sut.UpdateUserGroupsAsync(organizationUser, groupIds, null);
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationService>().DidNotReceiveWithAnyArgs()
|
||||
.ValidateOrganizationUserUpdatePermissions(default, default, default, default);
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1)
|
||||
.UpdateGroupsAsync(organizationUser.Id, groupIds);
|
||||
await sutProvider.GetDependency<IEventService>().Received(1)
|
||||
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_UpdatedGroups);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task UpdateUserGroups_WithSavingUserId_Passes(
|
||||
OrganizationUser organizationUser,
|
||||
IEnumerable<Guid> groupIds,
|
||||
Guid savingUserId,
|
||||
SutProvider<UpdateOrganizationUserGroupsCommand> sutProvider)
|
||||
{
|
||||
organizationUser.Permissions = null;
|
||||
|
||||
await sutProvider.Sut.UpdateUserGroupsAsync(organizationUser, groupIds, savingUserId);
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationService>().Received(1)
|
||||
.ValidateOrganizationUserUpdatePermissions(organizationUser.OrganizationId, organizationUser.Type, null, organizationUser.GetPermissions());
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1)
|
||||
.UpdateGroupsAsync(organizationUser.Id, groupIds);
|
||||
await sutProvider.GetDependency<IEventService>().Received(1)
|
||||
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_UpdatedGroups);
|
||||
}
|
||||
}
|
@ -20,14 +20,6 @@ public class LaunchDarklyFeatureServiceTests
|
||||
.Create();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Offline_WhenSelfHost()
|
||||
{
|
||||
var sutProvider = GetSutProvider(new Core.Settings.GlobalSettings() { SelfHosted = true });
|
||||
|
||||
Assert.False(sutProvider.Sut.IsOnline());
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public void DefaultFeatureValue_WhenSelfHost(string key)
|
||||
{
|
||||
|
@ -1832,4 +1832,74 @@ public class OrganizationServiceTests
|
||||
|
||||
sutProvider.Sut.ValidateSecretsManagerPlan(plan, signup);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[OrganizationInviteCustomize(
|
||||
InviteeUserType = OrganizationUserType.Owner,
|
||||
InvitorUserType = OrganizationUserType.Admin
|
||||
), BitAutoData]
|
||||
public async Task ValidateOrganizationUserUpdatePermissions_WithAdminAddingOwner_Throws(
|
||||
Guid organizationId,
|
||||
OrganizationUserInvite organizationUserInvite,
|
||||
SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => sutProvider.Sut.ValidateOrganizationUserUpdatePermissions(organizationId, organizationUserInvite.Type.Value, null, organizationUserInvite.Permissions));
|
||||
|
||||
Assert.Contains("only an owner can configure another owner's account.", exception.Message.ToLowerInvariant());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[OrganizationInviteCustomize(
|
||||
InviteeUserType = OrganizationUserType.Admin,
|
||||
InvitorUserType = OrganizationUserType.Owner
|
||||
), BitAutoData]
|
||||
public async Task ValidateOrganizationUserUpdatePermissions_WithoutManageUsersPermission_Throws(
|
||||
Guid organizationId,
|
||||
OrganizationUserInvite organizationUserInvite,
|
||||
SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => sutProvider.Sut.ValidateOrganizationUserUpdatePermissions(organizationId, organizationUserInvite.Type.Value, null, organizationUserInvite.Permissions));
|
||||
|
||||
Assert.Contains("your account does not have permission to manage users.", exception.Message.ToLowerInvariant());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[OrganizationInviteCustomize(
|
||||
InviteeUserType = OrganizationUserType.Admin,
|
||||
InvitorUserType = OrganizationUserType.Custom
|
||||
), BitAutoData]
|
||||
public async Task ValidateOrganizationUserUpdatePermissions_WithCustomAddingAdmin_Throws(
|
||||
Guid organizationId,
|
||||
OrganizationUserInvite organizationUserInvite,
|
||||
SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<ICurrentContext>().ManageUsers(organizationId).Returns(true);
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => sutProvider.Sut.ValidateOrganizationUserUpdatePermissions(organizationId, organizationUserInvite.Type.Value, null, organizationUserInvite.Permissions));
|
||||
|
||||
Assert.Contains("custom users can not manage admins or owners.", exception.Message.ToLowerInvariant());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[OrganizationInviteCustomize(
|
||||
InviteeUserType = OrganizationUserType.Custom,
|
||||
InvitorUserType = OrganizationUserType.Custom
|
||||
), BitAutoData]
|
||||
public async Task ValidateOrganizationUserUpdatePermissions_WithCustomAddingUser_WithoutPermissions_Throws(
|
||||
Guid organizationId,
|
||||
OrganizationUserInvite organizationUserInvite,
|
||||
SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
var invitePermissions = new Permissions { AccessReports = true };
|
||||
sutProvider.GetDependency<ICurrentContext>().ManageUsers(organizationId).Returns(true);
|
||||
sutProvider.GetDependency<ICurrentContext>().AccessReports(organizationId).Returns(false);
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => sutProvider.Sut.ValidateOrganizationUserUpdatePermissions(organizationId, organizationUserInvite.Type.Value, null, invitePermissions));
|
||||
|
||||
Assert.Contains("custom users can only grant the same custom permissions that they have.", exception.Message.ToLowerInvariant());
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user