1
0
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:
Federico Maccaroni
2022-06-06 14:52:50 -03:00
committed by GitHub
parent 16c6b23a27
commit b070e9a387
23 changed files with 3781 additions and 13 deletions

View File

@ -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);

View 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;
}
}
}

View 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; }
}
}

View File

@ -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()
{

View File

@ -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);
}
}

View File

@ -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

View File

@ -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)

View 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");
}
}
}

View File

@ -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

View File

@ -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

View File

@ -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)
);