mirror of
https://github.com/bitwarden/server.git
synced 2025-05-18 01:55:37 -05:00
Auth/pm 17111/add browser to list of approving clients (#5825)
* refactor(update-auth-approving-clients): [PM-17111] Add Browser to List of Approving Clients - Refactored how it works to fit different priorities.
This commit is contained in:
parent
67f745ebc4
commit
8d2629fe58
@ -115,6 +115,7 @@ public static class FeatureFlagKeys
|
|||||||
public const string TwoFactorExtensionDataPersistence = "pm-9115-two-factor-extension-data-persistence";
|
public const string TwoFactorExtensionDataPersistence = "pm-9115-two-factor-extension-data-persistence";
|
||||||
public const string EmailVerification = "email-verification";
|
public const string EmailVerification = "email-verification";
|
||||||
public const string UnauthenticatedExtensionUIRefresh = "unauth-ui-refresh";
|
public const string UnauthenticatedExtensionUIRefresh = "unauth-ui-refresh";
|
||||||
|
public const string BrowserExtensionLoginApproval = "pm-14938-browser-extension-login-approvals";
|
||||||
public const string SetInitialPasswordRefactor = "pm-16117-set-initial-password-refactor";
|
public const string SetInitialPasswordRefactor = "pm-16117-set-initial-password-refactor";
|
||||||
public const string ChangeExistingPasswordRefactor = "pm-16117-change-existing-password-refactor";
|
public const string ChangeExistingPasswordRefactor = "pm-16117-change-existing-password-refactor";
|
||||||
public const string RecoveryCodeLogin = "pm-17128-recovery-code-login";
|
public const string RecoveryCodeLogin = "pm-17128-recovery-code-login";
|
||||||
|
@ -22,6 +22,7 @@ public class UserDecryptionOptionsBuilder : IUserDecryptionOptionsBuilder
|
|||||||
private readonly ICurrentContext _currentContext;
|
private readonly ICurrentContext _currentContext;
|
||||||
private readonly IDeviceRepository _deviceRepository;
|
private readonly IDeviceRepository _deviceRepository;
|
||||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||||
|
private readonly ILoginApprovingClientTypes _loginApprovingClientTypes;
|
||||||
|
|
||||||
private UserDecryptionOptions _options = new UserDecryptionOptions();
|
private UserDecryptionOptions _options = new UserDecryptionOptions();
|
||||||
private User? _user;
|
private User? _user;
|
||||||
@ -31,12 +32,14 @@ public class UserDecryptionOptionsBuilder : IUserDecryptionOptionsBuilder
|
|||||||
public UserDecryptionOptionsBuilder(
|
public UserDecryptionOptionsBuilder(
|
||||||
ICurrentContext currentContext,
|
ICurrentContext currentContext,
|
||||||
IDeviceRepository deviceRepository,
|
IDeviceRepository deviceRepository,
|
||||||
IOrganizationUserRepository organizationUserRepository
|
IOrganizationUserRepository organizationUserRepository,
|
||||||
|
ILoginApprovingClientTypes loginApprovingClientTypes
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
_currentContext = currentContext;
|
_currentContext = currentContext;
|
||||||
_deviceRepository = deviceRepository;
|
_deviceRepository = deviceRepository;
|
||||||
_organizationUserRepository = organizationUserRepository;
|
_organizationUserRepository = organizationUserRepository;
|
||||||
|
_loginApprovingClientTypes = loginApprovingClientTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IUserDecryptionOptionsBuilder ForUser(User user)
|
public IUserDecryptionOptionsBuilder ForUser(User user)
|
||||||
@ -119,8 +122,7 @@ public class UserDecryptionOptionsBuilder : IUserDecryptionOptionsBuilder
|
|||||||
// Checks if the current user has any devices that are capable of approving login with device requests except for
|
// Checks if the current user has any devices that are capable of approving login with device requests except for
|
||||||
// their current device.
|
// their current device.
|
||||||
// NOTE: this doesn't check for if the users have configured the devices to be capable of approving requests as that is a client side setting.
|
// NOTE: this doesn't check for if the users have configured the devices to be capable of approving requests as that is a client side setting.
|
||||||
hasLoginApprovingDevice = allDevices
|
hasLoginApprovingDevice = allDevices.Any(d => d.Identifier != _device.Identifier && _loginApprovingClientTypes.TypesThatCanApprove.Contains(DeviceTypes.ToClientType(d.Type)));
|
||||||
.Any(d => d.Identifier != _device.Identifier && LoginApprovingClientTypes.TypesThatCanApprove.Contains(DeviceTypes.ToClientType(d.Type)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine if user has manage reset password permission as post sso logic requires it for forcing users with this permission to set a MP
|
// Determine if user has manage reset password permission as post sso logic requires it for forcing users with this permission to set a MP
|
||||||
|
@ -1,22 +1,39 @@
|
|||||||
using Bit.Core.Enums;
|
using Bit.Core;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Services;
|
||||||
|
|
||||||
namespace Bit.Identity.Utilities;
|
namespace Bit.Identity.Utilities;
|
||||||
|
|
||||||
public static class LoginApprovingClientTypes
|
public interface ILoginApprovingClientTypes
|
||||||
{
|
{
|
||||||
private static readonly IReadOnlyCollection<ClientType> _clientTypesThatCanApprove;
|
IReadOnlyCollection<ClientType> TypesThatCanApprove { get; }
|
||||||
|
}
|
||||||
|
|
||||||
static LoginApprovingClientTypes()
|
public class LoginApprovingClientTypes : ILoginApprovingClientTypes
|
||||||
{
|
{
|
||||||
var clientTypes = new List<ClientType>
|
public LoginApprovingClientTypes(
|
||||||
|
IFeatureService featureService)
|
||||||
|
{
|
||||||
|
if (featureService.IsEnabled(FeatureFlagKeys.BrowserExtensionLoginApproval))
|
||||||
|
{
|
||||||
|
TypesThatCanApprove = new List<ClientType>
|
||||||
{
|
{
|
||||||
ClientType.Desktop,
|
ClientType.Desktop,
|
||||||
ClientType.Mobile,
|
ClientType.Mobile,
|
||||||
ClientType.Web,
|
ClientType.Web,
|
||||||
ClientType.Browser,
|
ClientType.Browser,
|
||||||
};
|
};
|
||||||
_clientTypesThatCanApprove = clientTypes.AsReadOnly();
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TypesThatCanApprove = new List<ClientType>
|
||||||
|
{
|
||||||
|
ClientType.Desktop,
|
||||||
|
ClientType.Mobile,
|
||||||
|
ClientType.Web,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IReadOnlyCollection<ClientType> TypesThatCanApprove => _clientTypesThatCanApprove;
|
public IReadOnlyCollection<ClientType> TypesThatCanApprove { get; }
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ public static class ServiceCollectionExtensions
|
|||||||
services.AddTransient<IUserDecryptionOptionsBuilder, UserDecryptionOptionsBuilder>();
|
services.AddTransient<IUserDecryptionOptionsBuilder, UserDecryptionOptionsBuilder>();
|
||||||
services.AddTransient<IDeviceValidator, DeviceValidator>();
|
services.AddTransient<IDeviceValidator, DeviceValidator>();
|
||||||
services.AddTransient<ITwoFactorAuthenticationValidator, TwoFactorAuthenticationValidator>();
|
services.AddTransient<ITwoFactorAuthenticationValidator, TwoFactorAuthenticationValidator>();
|
||||||
|
services.AddTransient<ILoginApprovingClientTypes, LoginApprovingClientTypes>();
|
||||||
|
|
||||||
var issuerUri = new Uri(globalSettings.BaseServiceUri.InternalIdentity);
|
var issuerUri = new Uri(globalSettings.BaseServiceUri.InternalIdentity);
|
||||||
var identityServerBuilder = services
|
var identityServerBuilder = services
|
||||||
|
@ -6,6 +6,7 @@ using Bit.Core.Entities;
|
|||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Identity.IdentityServer;
|
using Bit.Identity.IdentityServer;
|
||||||
|
using Bit.Identity.Utilities;
|
||||||
using Bit.Test.Common.AutoFixture.Attributes;
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
@ -17,6 +18,7 @@ public class UserDecryptionOptionsBuilderTests
|
|||||||
private readonly ICurrentContext _currentContext;
|
private readonly ICurrentContext _currentContext;
|
||||||
private readonly IDeviceRepository _deviceRepository;
|
private readonly IDeviceRepository _deviceRepository;
|
||||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||||
|
private readonly ILoginApprovingClientTypes _loginApprovingClientTypes;
|
||||||
private readonly UserDecryptionOptionsBuilder _builder;
|
private readonly UserDecryptionOptionsBuilder _builder;
|
||||||
|
|
||||||
public UserDecryptionOptionsBuilderTests()
|
public UserDecryptionOptionsBuilderTests()
|
||||||
@ -24,7 +26,8 @@ public class UserDecryptionOptionsBuilderTests
|
|||||||
_currentContext = Substitute.For<ICurrentContext>();
|
_currentContext = Substitute.For<ICurrentContext>();
|
||||||
_deviceRepository = Substitute.For<IDeviceRepository>();
|
_deviceRepository = Substitute.For<IDeviceRepository>();
|
||||||
_organizationUserRepository = Substitute.For<IOrganizationUserRepository>();
|
_organizationUserRepository = Substitute.For<IOrganizationUserRepository>();
|
||||||
_builder = new UserDecryptionOptionsBuilder(_currentContext, _deviceRepository, _organizationUserRepository);
|
_loginApprovingClientTypes = Substitute.For<ILoginApprovingClientTypes>();
|
||||||
|
_builder = new UserDecryptionOptionsBuilder(_currentContext, _deviceRepository, _organizationUserRepository, _loginApprovingClientTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
@ -120,17 +123,17 @@ public class UserDecryptionOptionsBuilderTests
|
|||||||
[BitAutoData(DeviceType.SafariBrowser)]
|
[BitAutoData(DeviceType.SafariBrowser)]
|
||||||
[BitAutoData(DeviceType.VivaldiBrowser)]
|
[BitAutoData(DeviceType.VivaldiBrowser)]
|
||||||
[BitAutoData(DeviceType.UnknownBrowser)]
|
[BitAutoData(DeviceType.UnknownBrowser)]
|
||||||
// Extension
|
|
||||||
[BitAutoData(DeviceType.ChromeExtension)]
|
|
||||||
[BitAutoData(DeviceType.FirefoxExtension)]
|
|
||||||
[BitAutoData(DeviceType.OperaExtension)]
|
|
||||||
[BitAutoData(DeviceType.EdgeExtension)]
|
|
||||||
[BitAutoData(DeviceType.VivaldiExtension)]
|
|
||||||
[BitAutoData(DeviceType.SafariExtension)]
|
|
||||||
public async Task Build_WhenHasLoginApprovingDevice_ShouldApprovingDeviceTrue(
|
public async Task Build_WhenHasLoginApprovingDevice_ShouldApprovingDeviceTrue(
|
||||||
DeviceType deviceType,
|
DeviceType deviceType,
|
||||||
SsoConfig ssoConfig, SsoConfigurationData configurationData, User user, Device device, Device approvingDevice)
|
SsoConfig ssoConfig, SsoConfigurationData configurationData, User user, Device device, Device approvingDevice)
|
||||||
{
|
{
|
||||||
|
_loginApprovingClientTypes.TypesThatCanApprove.Returns(new List<ClientType>
|
||||||
|
{
|
||||||
|
ClientType.Desktop,
|
||||||
|
ClientType.Mobile,
|
||||||
|
ClientType.Web,
|
||||||
|
});
|
||||||
|
|
||||||
configurationData.MemberDecryptionType = MemberDecryptionType.TrustedDeviceEncryption;
|
configurationData.MemberDecryptionType = MemberDecryptionType.TrustedDeviceEncryption;
|
||||||
ssoConfig.Data = configurationData.Serialize();
|
ssoConfig.Data = configurationData.Serialize();
|
||||||
approvingDevice.Type = deviceType;
|
approvingDevice.Type = deviceType;
|
||||||
@ -142,9 +145,65 @@ public class UserDecryptionOptionsBuilderTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
// Desktop
|
||||||
|
[BitAutoData(DeviceType.LinuxDesktop)]
|
||||||
|
[BitAutoData(DeviceType.MacOsDesktop)]
|
||||||
|
[BitAutoData(DeviceType.WindowsDesktop)]
|
||||||
|
[BitAutoData(DeviceType.UWP)]
|
||||||
|
// Mobile
|
||||||
|
[BitAutoData(DeviceType.Android)]
|
||||||
|
[BitAutoData(DeviceType.iOS)]
|
||||||
|
[BitAutoData(DeviceType.AndroidAmazon)]
|
||||||
|
// Web
|
||||||
|
[BitAutoData(DeviceType.ChromeBrowser)]
|
||||||
|
[BitAutoData(DeviceType.FirefoxBrowser)]
|
||||||
|
[BitAutoData(DeviceType.OperaBrowser)]
|
||||||
|
[BitAutoData(DeviceType.EdgeBrowser)]
|
||||||
|
[BitAutoData(DeviceType.IEBrowser)]
|
||||||
|
[BitAutoData(DeviceType.SafariBrowser)]
|
||||||
|
[BitAutoData(DeviceType.VivaldiBrowser)]
|
||||||
|
[BitAutoData(DeviceType.UnknownBrowser)]
|
||||||
|
// Extension
|
||||||
|
[BitAutoData(DeviceType.ChromeExtension)]
|
||||||
|
[BitAutoData(DeviceType.FirefoxExtension)]
|
||||||
|
[BitAutoData(DeviceType.OperaExtension)]
|
||||||
|
[BitAutoData(DeviceType.EdgeExtension)]
|
||||||
|
[BitAutoData(DeviceType.VivaldiExtension)]
|
||||||
|
[BitAutoData(DeviceType.SafariExtension)]
|
||||||
|
public async Task Build_WhenHasLoginApprovingDeviceFeatureFlag_ShouldApprovingDeviceTrue(
|
||||||
|
DeviceType deviceType,
|
||||||
|
SsoConfig ssoConfig, SsoConfigurationData configurationData, User user, Device device, Device approvingDevice)
|
||||||
|
{
|
||||||
|
_loginApprovingClientTypes.TypesThatCanApprove.Returns(new List<ClientType>
|
||||||
|
{
|
||||||
|
ClientType.Desktop,
|
||||||
|
ClientType.Mobile,
|
||||||
|
ClientType.Web,
|
||||||
|
ClientType.Browser,
|
||||||
|
});
|
||||||
|
|
||||||
|
configurationData.MemberDecryptionType = MemberDecryptionType.TrustedDeviceEncryption;
|
||||||
|
ssoConfig.Data = configurationData.Serialize();
|
||||||
|
approvingDevice.Type = deviceType;
|
||||||
|
_deviceRepository.GetManyByUserIdAsync(user.Id).Returns(new Device[] { approvingDevice });
|
||||||
|
|
||||||
|
var result = await _builder.ForUser(user).WithSso(ssoConfig).WithDevice(device).BuildAsync();
|
||||||
|
|
||||||
|
Assert.True(result.TrustedDeviceOption?.HasLoginApprovingDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
// CLI
|
||||||
[BitAutoData(DeviceType.WindowsCLI)]
|
[BitAutoData(DeviceType.WindowsCLI)]
|
||||||
[BitAutoData(DeviceType.MacOsCLI)]
|
[BitAutoData(DeviceType.MacOsCLI)]
|
||||||
[BitAutoData(DeviceType.LinuxCLI)]
|
[BitAutoData(DeviceType.LinuxCLI)]
|
||||||
|
// Extension
|
||||||
|
[BitAutoData(DeviceType.ChromeExtension)]
|
||||||
|
[BitAutoData(DeviceType.FirefoxExtension)]
|
||||||
|
[BitAutoData(DeviceType.OperaExtension)]
|
||||||
|
[BitAutoData(DeviceType.EdgeExtension)]
|
||||||
|
[BitAutoData(DeviceType.VivaldiExtension)]
|
||||||
|
[BitAutoData(DeviceType.SafariExtension)]
|
||||||
public async Task Build_WhenHasLoginApprovingDevice_ShouldApprovingDeviceFalse(
|
public async Task Build_WhenHasLoginApprovingDevice_ShouldApprovingDeviceFalse(
|
||||||
DeviceType deviceType,
|
DeviceType deviceType,
|
||||||
SsoConfig ssoConfig, SsoConfigurationData configurationData, User user, Device device, Device approvingDevice)
|
SsoConfig ssoConfig, SsoConfigurationData configurationData, User user, Device device, Device approvingDevice)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user