mirror of
https://github.com/bitwarden/server.git
synced 2025-07-01 08:02:49 -05:00
[PS-589] Fix emergency contact takeover device verification and endpoints for its settings (#2016)
* Added UnknownDeviceVerificationEnabled on User that is turned off when emergency contact takes over the account. Also added endpoints to get and update 2fa device verification settings. And Updated migrations & tests * Applied dotnet format * Fixed method rename call on TwoFactorController * PS-589 Format fixes * PS-589 changed UnknownDeviceVerificationEnabled to be non-nullable
This commit is contained in:

committed by
GitHub

parent
16c6b23a27
commit
b070e9a387
@ -380,6 +380,42 @@ namespace Bit.Api.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("get-device-verification-settings")]
|
||||
public async Task<DeviceVerificationResponseModel> GetDeviceVerificationSettings()
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
|
||||
if (User.Claims.HasSsoIdP())
|
||||
{
|
||||
return new DeviceVerificationResponseModel(false, false);
|
||||
}
|
||||
|
||||
return new DeviceVerificationResponseModel(_userService.CanEditDeviceVerificationSettings(user), user.UnknownDeviceVerificationEnabled);
|
||||
}
|
||||
|
||||
[HttpPut("device-verification-settings")]
|
||||
public async Task<DeviceVerificationResponseModel> PutDeviceVerificationSettings([FromBody] DeviceVerificationRequestModel model)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
if (!_userService.CanEditDeviceVerificationSettings(user)
|
||||
|| User.Claims.HasSsoIdP())
|
||||
{
|
||||
throw new InvalidOperationException("Can't update device verification settings");
|
||||
}
|
||||
|
||||
model.ToUser(user);
|
||||
await _userService.SaveUserAsync(user);
|
||||
return new DeviceVerificationResponseModel(true, user.UnknownDeviceVerificationEnabled);
|
||||
}
|
||||
|
||||
private async Task<User> CheckAsync(SecretVerificationRequestModel model, bool premium)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
|
18
src/Api/Models/Request/DeviceVerificationRequestModel.cs
Normal file
18
src/Api/Models/Request/DeviceVerificationRequestModel.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Bit.Core.Entities;
|
||||
|
||||
namespace Bit.Api.Models.Request
|
||||
{
|
||||
public class DeviceVerificationRequestModel
|
||||
{
|
||||
[Required]
|
||||
public bool UnknownDeviceVerificationEnabled { get; set; }
|
||||
|
||||
public User ToUser(User user)
|
||||
{
|
||||
user.UnknownDeviceVerificationEnabled = UnknownDeviceVerificationEnabled;
|
||||
return user;
|
||||
}
|
||||
}
|
||||
}
|
18
src/Api/Models/Response/DeviceVerificationResponseModel.cs
Normal file
18
src/Api/Models/Response/DeviceVerificationResponseModel.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using Bit.Core.Models.Api;
|
||||
|
||||
namespace Bit.Api.Models.Response
|
||||
{
|
||||
public class DeviceVerificationResponseModel : ResponseModel
|
||||
{
|
||||
public DeviceVerificationResponseModel(bool isDeviceVerificationSectionEnabled, bool unknownDeviceVerificationEnabled)
|
||||
: base("deviceVerificationSettings")
|
||||
{
|
||||
IsDeviceVerificationSectionEnabled = isDeviceVerificationSectionEnabled;
|
||||
UnknownDeviceVerificationEnabled = unknownDeviceVerificationEnabled;
|
||||
}
|
||||
|
||||
public bool IsDeviceVerificationSectionEnabled { get; }
|
||||
public bool UnknownDeviceVerificationEnabled { get; }
|
||||
}
|
||||
}
|
@ -62,6 +62,7 @@ namespace Bit.Core.Entities
|
||||
public bool UsesKeyConnector { get; set; }
|
||||
public int FailedLoginCount { get; set; }
|
||||
public DateTime? LastFailedLoginDate { get; set; }
|
||||
public bool UnknownDeviceVerificationEnabled { get; set; }
|
||||
|
||||
public void SetNewId()
|
||||
{
|
||||
|
@ -79,5 +79,6 @@ namespace Bit.Core.Services
|
||||
Task<bool> VerifyOTPAsync(User user, string token);
|
||||
Task<bool> VerifySecretAsync(User user, string secret);
|
||||
Task<bool> Needs2FABecauseNewDeviceAsync(User user, string deviceIdentifier, string grantType);
|
||||
bool CanEditDeviceVerificationSettings(User user);
|
||||
}
|
||||
}
|
||||
|
@ -327,6 +327,7 @@ namespace Bit.Core.Services
|
||||
grantor.Key = key;
|
||||
// Disable TwoFactor providers since they will otherwise block logins
|
||||
grantor.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>());
|
||||
grantor.UnknownDeviceVerificationEnabled = false;
|
||||
await _userRepository.ReplaceAsync(grantor);
|
||||
|
||||
// Remove grantor from all organizations unless Owner
|
||||
|
@ -1417,12 +1417,20 @@ namespace Bit.Core.Services
|
||||
|
||||
public async Task<bool> Needs2FABecauseNewDeviceAsync(User user, string deviceIdentifier, string grantType)
|
||||
{
|
||||
return _globalSettings.TwoFactorAuth.EmailOnNewDeviceLogin
|
||||
&& user.EmailVerified
|
||||
return CanEditDeviceVerificationSettings(user)
|
||||
&& user.UnknownDeviceVerificationEnabled
|
||||
&& grantType != "authorization_code"
|
||||
&& await IsNewDeviceAndNotTheFirstOneAsync(user, deviceIdentifier);
|
||||
}
|
||||
|
||||
public bool CanEditDeviceVerificationSettings(User user)
|
||||
{
|
||||
return _globalSettings.TwoFactorAuth.EmailOnNewDeviceLogin
|
||||
&& user.EmailVerified
|
||||
&& !user.UsesKeyConnector
|
||||
&& !(user.GetTwoFactorProviders()?.Any() ?? false);
|
||||
}
|
||||
|
||||
private async Task<bool> IsNewDeviceAndNotTheFirstOneAsync(User user, string deviceIdentifier)
|
||||
{
|
||||
if (user == null)
|
||||
|
15
src/Core/Utilities/ClaimsExtensions.cs
Normal file
15
src/Core/Utilities/ClaimsExtensions.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace Bit.Core.Utilities
|
||||
{
|
||||
public static class ClaimsExtensions
|
||||
{
|
||||
public static bool HasSsoIdP(this IEnumerable<Claim> claims)
|
||||
{
|
||||
return claims.Any(c => c.Type == "idp" && c.Value == "sso");
|
||||
}
|
||||
}
|
||||
}
|
@ -33,7 +33,8 @@
|
||||
@ForcePasswordReset BIT = 0,
|
||||
@UsesKeyConnector BIT = 0,
|
||||
@FailedLoginCount INT = 0,
|
||||
@LastFailedLoginDate DATETIME2(7)
|
||||
@LastFailedLoginDate DATETIME2(7),
|
||||
@UnknownDeviceVerificationEnabled BIT NULL
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
@ -74,7 +75,8 @@ BEGIN
|
||||
[ForcePasswordReset],
|
||||
[UsesKeyConnector],
|
||||
[FailedLoginCount],
|
||||
[LastFailedLoginDate]
|
||||
[LastFailedLoginDate],
|
||||
[UnknownDeviceVerificationEnabled]
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
@ -112,6 +114,7 @@ BEGIN
|
||||
@ForcePasswordReset,
|
||||
@UsesKeyConnector,
|
||||
@FailedLoginCount,
|
||||
@LastFailedLoginDate
|
||||
@LastFailedLoginDate,
|
||||
@UnknownDeviceVerificationEnabled
|
||||
)
|
||||
END
|
||||
|
@ -33,7 +33,8 @@
|
||||
@ForcePasswordReset BIT = 0,
|
||||
@UsesKeyConnector BIT = 0,
|
||||
@FailedLoginCount INT,
|
||||
@LastFailedLoginDate DATETIME2(7)
|
||||
@LastFailedLoginDate DATETIME2(7),
|
||||
@UnknownDeviceVerificationEnabled BIT NULL
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
@ -74,7 +75,8 @@ BEGIN
|
||||
[ForcePasswordReset] = @ForcePasswordReset,
|
||||
[UsesKeyConnector] = @UsesKeyConnector,
|
||||
[FailedLoginCount] = @FailedLoginCount,
|
||||
[LastFailedLoginDate] = @LastFailedLoginDate
|
||||
[LastFailedLoginDate] = @LastFailedLoginDate,
|
||||
[UnknownDeviceVerificationEnabled] = @UnknownDeviceVerificationEnabled
|
||||
WHERE
|
||||
[Id] = @Id
|
||||
END
|
||||
|
@ -34,6 +34,7 @@
|
||||
[UsesKeyConnector] BIT NOT NULL,
|
||||
[FailedLoginCount] INT NOT NULL,
|
||||
[LastFailedLoginDate] DATETIME2 (7) NULL,
|
||||
[UnknownDeviceVerificationEnabled] BIT NULL,
|
||||
CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED ([Id] ASC)
|
||||
);
|
||||
|
||||
|
Reference in New Issue
Block a user