1
0
mirror of https://github.com/bitwarden/server.git synced 2025-04-24 14:26:38 -05:00

Removed more captcha logic.

This commit is contained in:
Todd Martin 2025-04-20 14:23:29 -04:00
parent 1b11496d33
commit be4e782370
No known key found for this signature in database
GPG Key ID: 663E7AF5C839BC8F
24 changed files with 9 additions and 431 deletions

View File

@ -1,6 +0,0 @@
namespace Bit.Core.Auth.Models.Api;
public interface ICaptchaProtectedModel
{
string CaptchaResponse { get; set; }
}

View File

@ -1,9 +0,0 @@
namespace Bit.Core.Auth.Models.Business;
public class CaptchaResponse
{
public bool Success { get; set; }
public bool MaybeBot { get; set; }
public bool IsBot { get; set; }
public double Score { get; set; }
}

View File

@ -1,43 +0,0 @@
using System.Text.Json.Serialization;
using Bit.Core.Entities;
using Bit.Core.Tokens;
namespace Bit.Core.Auth.Models.Business.Tokenables;
public class HCaptchaTokenable : ExpiringTokenable
{
private const double _tokenLifetimeInHours = (double)5 / 60; // 5 minutes
public const string ClearTextPrefix = "BWCaptchaBypass_";
public const string DataProtectorPurpose = "CaptchaServiceDataProtector";
public const string TokenIdentifier = "CaptchaBypassToken";
public string Identifier { get; set; } = TokenIdentifier;
public Guid Id { get; set; }
public string Email { get; set; }
[JsonConstructor]
public HCaptchaTokenable()
{
ExpirationDate = DateTime.UtcNow.AddHours(_tokenLifetimeInHours);
}
public HCaptchaTokenable(User user) : this()
{
Id = user?.Id ?? default;
Email = user?.Email;
}
public bool TokenIsValid(User user)
{
if (Id == default || Email == default || user == null)
{
return false;
}
return Id == user.Id &&
Email.Equals(user.Email, StringComparison.InvariantCultureIgnoreCase);
}
// Validates deserialized
protected override bool TokenIsValid() => Identifier == TokenIdentifier && Id != default && !string.IsNullOrWhiteSpace(Email);
}

View File

@ -1,15 +0,0 @@
using Bit.Core.Auth.Models.Business;
using Bit.Core.Context;
using Bit.Core.Entities;
namespace Bit.Core.Auth.Services;
public interface ICaptchaValidationService
{
string SiteKey { get; }
string SiteKeyResponseKeyName { get; }
bool RequireCaptchaValidation(ICurrentContext currentContext, User user = null);
Task<CaptchaResponse> ValidateCaptchaResponseAsync(string captchResponse, string clientIpAddress,
User user = null);
string GenerateCaptchaBypassToken(User user);
}

View File

@ -1,132 +0,0 @@
using System.Net.Http.Json;
using System.Text.Json.Serialization;
using Bit.Core.Auth.Models.Business;
using Bit.Core.Auth.Models.Business.Tokenables;
using Bit.Core.Context;
using Bit.Core.Entities;
using Bit.Core.Settings;
using Bit.Core.Tokens;
using Microsoft.Extensions.Logging;
namespace Bit.Core.Auth.Services;
public class HCaptchaValidationService : ICaptchaValidationService
{
private readonly ILogger<HCaptchaValidationService> _logger;
private readonly IHttpClientFactory _httpClientFactory;
private readonly GlobalSettings _globalSettings;
private readonly IDataProtectorTokenFactory<HCaptchaTokenable> _tokenizer;
public HCaptchaValidationService(
ILogger<HCaptchaValidationService> logger,
IHttpClientFactory httpClientFactory,
IDataProtectorTokenFactory<HCaptchaTokenable> tokenizer,
GlobalSettings globalSettings)
{
_logger = logger;
_httpClientFactory = httpClientFactory;
_globalSettings = globalSettings;
_tokenizer = tokenizer;
}
public string SiteKeyResponseKeyName => "HCaptcha_SiteKey";
public string SiteKey => _globalSettings.Captcha.HCaptchaSiteKey;
public string GenerateCaptchaBypassToken(User user) => _tokenizer.Protect(new HCaptchaTokenable(user));
public async Task<CaptchaResponse> ValidateCaptchaResponseAsync(string captchaResponse, string clientIpAddress,
User user = null)
{
var response = new CaptchaResponse { Success = false };
if (string.IsNullOrWhiteSpace(captchaResponse))
{
return response;
}
if (user != null && ValidateCaptchaBypassToken(captchaResponse, user))
{
response.Success = true;
return response;
}
var httpClient = _httpClientFactory.CreateClient("HCaptchaValidationService");
var requestMessage = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri("https://hcaptcha.com/siteverify"),
Content = new FormUrlEncodedContent(new Dictionary<string, string>
{
{ "response", captchaResponse.TrimStart("hcaptcha|".ToCharArray()) },
{ "secret", _globalSettings.Captcha.HCaptchaSecretKey },
{ "sitekey", SiteKey },
{ "remoteip", clientIpAddress }
})
};
HttpResponseMessage responseMessage;
try
{
responseMessage = await httpClient.SendAsync(requestMessage);
}
catch (Exception e)
{
_logger.LogError(11389, e, "Unable to verify with HCaptcha.");
return response;
}
if (!responseMessage.IsSuccessStatusCode)
{
return response;
}
using var hcaptchaResponse = await responseMessage.Content.ReadFromJsonAsync<HCaptchaResponse>();
response.Success = hcaptchaResponse.Success;
var score = hcaptchaResponse.Score.GetValueOrDefault();
response.MaybeBot = score >= _globalSettings.Captcha.MaybeBotScoreThreshold;
response.IsBot = score >= _globalSettings.Captcha.IsBotScoreThreshold;
response.Score = score;
return response;
}
public bool RequireCaptchaValidation(ICurrentContext currentContext, User user = null)
{
if (user == null)
{
return currentContext.IsBot || _globalSettings.Captcha.ForceCaptchaRequired;
}
var failedLoginCeiling = _globalSettings.Captcha.MaximumFailedLoginAttempts;
var failedLoginCount = user?.FailedLoginCount ?? 0;
var requireOnCloud = !_globalSettings.SelfHosted && !user.EmailVerified &&
user.CreationDate < DateTime.UtcNow.AddHours(-24);
return currentContext.IsBot ||
_globalSettings.Captcha.ForceCaptchaRequired ||
requireOnCloud ||
failedLoginCeiling > 0 && failedLoginCount >= failedLoginCeiling;
}
private static bool TokenIsValidApiKey(string bypassToken, User user) =>
!string.IsNullOrWhiteSpace(bypassToken) && user != null && user.ApiKey == bypassToken;
private bool TokenIsValidCaptchaBypassToken(string encryptedToken, User user)
{
return _tokenizer.TryUnprotect(encryptedToken, out var data) &&
data.Valid && data.TokenIsValid(user);
}
private bool ValidateCaptchaBypassToken(string bypassToken, User user) =>
TokenIsValidApiKey(bypassToken, user) || TokenIsValidCaptchaBypassToken(bypassToken, user);
public class HCaptchaResponse : IDisposable
{
[JsonPropertyName("success")]
public bool Success { get; set; }
[JsonPropertyName("score")]
public double? Score { get; set; }
[JsonPropertyName("score_reason")]
public List<string> ScoreReason { get; set; }
public void Dispose() { }
}
}

View File

@ -1,18 +0,0 @@
using Bit.Core.Auth.Models.Business;
using Bit.Core.Context;
using Bit.Core.Entities;
namespace Bit.Core.Auth.Services;
public class NoopCaptchaValidationService : ICaptchaValidationService
{
public string SiteKeyResponseKeyName => null;
public string SiteKey => null;
public bool RequireCaptchaValidation(ICurrentContext currentContext, User user = null) => false;
public string GenerateCaptchaBypassToken(User user) => "";
public Task<CaptchaResponse> ValidateCaptchaResponseAsync(string captchaResponse, string clientIpAddress,
User user = null)
{
return Task.FromResult(new CaptchaResponse { Success = true });
}
}

View File

@ -1,36 +0,0 @@
using Bit.Core.Auth.Models.Api;
using Bit.Core.Auth.Services;
using Bit.Core.Context;
using Bit.Core.Exceptions;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
namespace Bit.Core.Auth.Utilities;
public class CaptchaProtectedAttribute : ActionFilterAttribute
{
public string ModelParameterName { get; set; } = "model";
public override void OnActionExecuting(ActionExecutingContext context)
{
var currentContext = context.HttpContext.RequestServices.GetRequiredService<ICurrentContext>();
var captchaValidationService = context.HttpContext.RequestServices.GetRequiredService<ICaptchaValidationService>();
if (captchaValidationService.RequireCaptchaValidation(currentContext, null))
{
var captchaResponse = (context.ActionArguments[ModelParameterName] as ICaptchaProtectedModel)?.CaptchaResponse;
if (string.IsNullOrWhiteSpace(captchaResponse))
{
throw new BadRequestException(captchaValidationService.SiteKeyResponseKeyName, captchaValidationService.SiteKey);
}
var captchaValidationResponse = captchaValidationService.ValidateCaptchaResponseAsync(captchaResponse,
currentContext.IpAddress, null).GetAwaiter().GetResult();
if (!captchaValidationResponse.Success || captchaValidationResponse.IsBot)
{
throw new BadRequestException("Captcha is invalid. Please refresh and try again");
}
}
}
}

View File

@ -45,7 +45,6 @@ public class GlobalSettings : IGlobalSettings
public virtual bool EnableCloudCommunication { get; set; } = false; public virtual bool EnableCloudCommunication { get; set; } = false;
public virtual int OrganizationInviteExpirationHours { get; set; } = 120; // 5 days public virtual int OrganizationInviteExpirationHours { get; set; } = 120; // 5 days
public virtual string EventGridKey { get; set; } public virtual string EventGridKey { get; set; }
public virtual CaptchaSettings Captcha { get; set; } = new CaptchaSettings();
public virtual IInstallationSettings Installation { get; set; } = new InstallationSettings(); public virtual IInstallationSettings Installation { get; set; } = new InstallationSettings();
public virtual IBaseServiceUriSettings BaseServiceUri { get; set; } public virtual IBaseServiceUriSettings BaseServiceUri { get; set; }
public virtual string DatabaseProvider { get; set; } public virtual string DatabaseProvider { get; set; }
@ -618,16 +617,6 @@ public class GlobalSettings : IGlobalSettings
public bool EnforceSsoPolicyForAllUsers { get; set; } public bool EnforceSsoPolicyForAllUsers { get; set; }
} }
public class CaptchaSettings
{
public bool ForceCaptchaRequired { get; set; } = false;
public string HCaptchaSecretKey { get; set; }
public string HCaptchaSiteKey { get; set; }
public int MaximumFailedLoginAttempts { get; set; }
public double MaybeBotScoreThreshold { get; set; } = double.MaxValue;
public double IsBotScoreThreshold { get; set; } = double.MaxValue;
}
public class StripeSettings public class StripeSettings
{ {
public string ApiKey { get; set; } public string ApiKey { get; set; }

View File

@ -5,7 +5,6 @@ using Bit.Core.Auth.Enums;
using Bit.Core.Auth.Models.Api.Request.Accounts; using Bit.Core.Auth.Models.Api.Request.Accounts;
using Bit.Core.Auth.Models.Api.Response.Accounts; using Bit.Core.Auth.Models.Api.Response.Accounts;
using Bit.Core.Auth.Models.Business.Tokenables; using Bit.Core.Auth.Models.Business.Tokenables;
using Bit.Core.Auth.Services;
using Bit.Core.Auth.UserFeatures.Registration; using Bit.Core.Auth.UserFeatures.Registration;
using Bit.Core.Auth.UserFeatures.WebAuthnLogin; using Bit.Core.Auth.UserFeatures.WebAuthnLogin;
using Bit.Core.Context; using Bit.Core.Context;
@ -37,7 +36,6 @@ public class AccountsController : Controller
private readonly ILogger<AccountsController> _logger; private readonly ILogger<AccountsController> _logger;
private readonly IUserRepository _userRepository; private readonly IUserRepository _userRepository;
private readonly IRegisterUserCommand _registerUserCommand; private readonly IRegisterUserCommand _registerUserCommand;
private readonly ICaptchaValidationService _captchaValidationService;
private readonly IDataProtectorTokenFactory<WebAuthnLoginAssertionOptionsTokenable> _assertionOptionsDataProtector; private readonly IDataProtectorTokenFactory<WebAuthnLoginAssertionOptionsTokenable> _assertionOptionsDataProtector;
private readonly IGetWebAuthnLoginCredentialAssertionOptionsCommand _getWebAuthnLoginCredentialAssertionOptionsCommand; private readonly IGetWebAuthnLoginCredentialAssertionOptionsCommand _getWebAuthnLoginCredentialAssertionOptionsCommand;
private readonly ISendVerificationEmailForRegistrationCommand _sendVerificationEmailForRegistrationCommand; private readonly ISendVerificationEmailForRegistrationCommand _sendVerificationEmailForRegistrationCommand;
@ -85,7 +83,6 @@ public class AccountsController : Controller
ILogger<AccountsController> logger, ILogger<AccountsController> logger,
IUserRepository userRepository, IUserRepository userRepository,
IRegisterUserCommand registerUserCommand, IRegisterUserCommand registerUserCommand,
ICaptchaValidationService captchaValidationService,
IDataProtectorTokenFactory<WebAuthnLoginAssertionOptionsTokenable> assertionOptionsDataProtector, IDataProtectorTokenFactory<WebAuthnLoginAssertionOptionsTokenable> assertionOptionsDataProtector,
IGetWebAuthnLoginCredentialAssertionOptionsCommand getWebAuthnLoginCredentialAssertionOptionsCommand, IGetWebAuthnLoginCredentialAssertionOptionsCommand getWebAuthnLoginCredentialAssertionOptionsCommand,
ISendVerificationEmailForRegistrationCommand sendVerificationEmailForRegistrationCommand, ISendVerificationEmailForRegistrationCommand sendVerificationEmailForRegistrationCommand,
@ -99,7 +96,6 @@ public class AccountsController : Controller
_logger = logger; _logger = logger;
_userRepository = userRepository; _userRepository = userRepository;
_registerUserCommand = registerUserCommand; _registerUserCommand = registerUserCommand;
_captchaValidationService = captchaValidationService;
_assertionOptionsDataProtector = assertionOptionsDataProtector; _assertionOptionsDataProtector = assertionOptionsDataProtector;
_getWebAuthnLoginCredentialAssertionOptionsCommand = getWebAuthnLoginCredentialAssertionOptionsCommand; _getWebAuthnLoginCredentialAssertionOptionsCommand = getWebAuthnLoginCredentialAssertionOptionsCommand;
_sendVerificationEmailForRegistrationCommand = sendVerificationEmailForRegistrationCommand; _sendVerificationEmailForRegistrationCommand = sendVerificationEmailForRegistrationCommand;
@ -212,8 +208,7 @@ public class AccountsController : Controller
{ {
if (result.Succeeded) if (result.Succeeded)
{ {
var captchaBypassToken = _captchaValidationService.GenerateCaptchaBypassToken(user); return new RegisterResponseModel();
return new RegisterResponseModel(captchaBypassToken);
} }
foreach (var error in result.Errors.Where(e => e.Code != "DuplicateUserName")) foreach (var error in result.Errors.Where(e => e.Code != "DuplicateUserName"))

View File

@ -2,7 +2,6 @@
using Bit.Core; using Bit.Core;
using Bit.Core.AdminConsole.Services; using Bit.Core.AdminConsole.Services;
using Bit.Core.Auth.Repositories; using Bit.Core.Auth.Repositories;
using Bit.Core.Auth.Services;
using Bit.Core.Context; using Bit.Core.Context;
using Bit.Core.Entities; using Bit.Core.Entities;
using Bit.Core.Repositories; using Bit.Core.Repositories;
@ -20,7 +19,6 @@ public class ResourceOwnerPasswordValidator : BaseRequestValidator<ResourceOwner
{ {
private UserManager<User> _userManager; private UserManager<User> _userManager;
private readonly ICurrentContext _currentContext; private readonly ICurrentContext _currentContext;
private readonly ICaptchaValidationService _captchaValidationService;
private readonly IAuthRequestRepository _authRequestRepository; private readonly IAuthRequestRepository _authRequestRepository;
private readonly IDeviceValidator _deviceValidator; private readonly IDeviceValidator _deviceValidator;
public ResourceOwnerPasswordValidator( public ResourceOwnerPasswordValidator(
@ -34,7 +32,6 @@ public class ResourceOwnerPasswordValidator : BaseRequestValidator<ResourceOwner
ILogger<ResourceOwnerPasswordValidator> logger, ILogger<ResourceOwnerPasswordValidator> logger,
ICurrentContext currentContext, ICurrentContext currentContext,
GlobalSettings globalSettings, GlobalSettings globalSettings,
ICaptchaValidationService captchaValidationService,
IAuthRequestRepository authRequestRepository, IAuthRequestRepository authRequestRepository,
IUserRepository userRepository, IUserRepository userRepository,
IPolicyService policyService, IPolicyService policyService,
@ -60,7 +57,6 @@ public class ResourceOwnerPasswordValidator : BaseRequestValidator<ResourceOwner
{ {
_userManager = userManager; _userManager = userManager;
_currentContext = currentContext; _currentContext = currentContext;
_captchaValidationService = captchaValidationService;
_authRequestRepository = authRequestRepository; _authRequestRepository = authRequestRepository;
_deviceValidator = deviceValidator; _deviceValidator = deviceValidator;
} }

View File

@ -1,7 +1,6 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Text.Json; using System.Text.Json;
using Bit.Core; using Bit.Core;
using Bit.Core.Auth.Models.Api;
using Bit.Core.Auth.Models.Api.Request.Accounts; using Bit.Core.Auth.Models.Api.Request.Accounts;
using Bit.Core.Entities; using Bit.Core.Entities;
using Bit.Core.Enums; using Bit.Core.Enums;
@ -9,7 +8,7 @@ using Bit.Core.Utilities;
namespace Bit.Identity.Models.Request.Accounts; namespace Bit.Identity.Models.Request.Accounts;
public class RegisterRequestModel : IValidatableObject, ICaptchaProtectedModel public class RegisterRequestModel : IValidatableObject
{ {
[StringLength(50)] [StringLength(50)]
public string Name { get; set; } public string Name { get; set; }
@ -22,7 +21,6 @@ public class RegisterRequestModel : IValidatableObject, ICaptchaProtectedModel
public string MasterPasswordHash { get; set; } public string MasterPasswordHash { get; set; }
[StringLength(50)] [StringLength(50)]
public string MasterPasswordHint { get; set; } public string MasterPasswordHint { get; set; }
public string CaptchaResponse { get; set; }
public string Key { get; set; } public string Key { get; set; }
public KeysRequestModel Keys { get; set; } public KeysRequestModel Keys { get; set; }
public string Token { get; set; } public string Token { get; set; }

View File

@ -1,5 +0,0 @@
namespace Bit.Identity.Models.Response.Accounts;
public interface ICaptchaProtectedResponseModel
{
public string CaptchaBypassToken { get; set; }
}

View File

@ -2,13 +2,9 @@
namespace Bit.Identity.Models.Response.Accounts; namespace Bit.Identity.Models.Response.Accounts;
public class RegisterResponseModel : ResponseModel, ICaptchaProtectedResponseModel public class RegisterResponseModel : ResponseModel
{ {
public RegisterResponseModel(string captchaBypassToken) public RegisterResponseModel()
: base("register") : base("register")
{ {}
CaptchaBypassToken = captchaBypassToken;
}
public string CaptchaBypassToken { get; set; }
} }

View File

@ -17,9 +17,6 @@
}, },
"braintree": { "braintree": {
"production": true "production": true
},
"captcha": {
"maximumFailedLoginAttempts": 5
} }
}, },
"Logging": { "Logging": {

View File

@ -14,9 +14,6 @@
"internalVault": null, "internalVault": null,
"internalSso": null, "internalSso": null,
"internalScim": null "internalScim": null
},
"captcha": {
"maximumFailedLoginAttempts": 0
} }
} }
} }

View File

@ -151,14 +151,6 @@ public static class ServiceCollectionExtensions
serviceProvider.GetRequiredService<ILogger<DataProtectorTokenFactory<EmergencyAccessInviteTokenable>>>()) serviceProvider.GetRequiredService<ILogger<DataProtectorTokenFactory<EmergencyAccessInviteTokenable>>>())
); );
services.AddSingleton<IDataProtectorTokenFactory<HCaptchaTokenable>>(serviceProvider =>
new DataProtectorTokenFactory<HCaptchaTokenable>(
HCaptchaTokenable.ClearTextPrefix,
HCaptchaTokenable.DataProtectorPurpose,
serviceProvider.GetDataProtectionProvider(),
serviceProvider.GetRequiredService<ILogger<DataProtectorTokenFactory<HCaptchaTokenable>>>())
);
services.AddSingleton<IDataProtectorTokenFactory<SsoTokenable>>(serviceProvider => services.AddSingleton<IDataProtectorTokenFactory<SsoTokenable>>(serviceProvider =>
new DataProtectorTokenFactory<SsoTokenable>( new DataProtectorTokenFactory<SsoTokenable>(
SsoTokenable.ClearTextPrefix, SsoTokenable.ClearTextPrefix,
@ -395,16 +387,6 @@ public static class ServiceCollectionExtensions
{ {
services.AddSingleton<IReferenceEventService, AzureQueueReferenceEventService>(); services.AddSingleton<IReferenceEventService, AzureQueueReferenceEventService>();
} }
if (CoreHelpers.SettingHasValue(globalSettings.Captcha?.HCaptchaSecretKey) &&
CoreHelpers.SettingHasValue(globalSettings.Captcha?.HCaptchaSiteKey))
{
services.AddSingleton<ICaptchaValidationService, HCaptchaValidationService>();
}
else
{
services.AddSingleton<ICaptchaValidationService, NoopCaptchaValidationService>();
}
} }
public static void AddOosServices(this IServiceCollection services) public static void AddOosServices(this IServiceCollection services)

View File

@ -1,87 +0,0 @@
using AutoFixture.Xunit2;
using Bit.Core.Auth.Models.Business.Tokenables;
using Bit.Core.Entities;
using Bit.Core.Tokens;
using Bit.Test.Common.AutoFixture.Attributes;
using Xunit;
namespace Bit.Core.Test.Auth.Models.Business.Tokenables;
public class HCaptchaTokenableTests
{
[Fact]
public void CanHandleNullUser()
{
var token = new HCaptchaTokenable(null);
Assert.Equal(default, token.Id);
Assert.Equal(default, token.Email);
}
[Fact]
public void TokenWithNullUserIsInvalid()
{
var token = new HCaptchaTokenable(null)
{
ExpirationDate = DateTime.UtcNow + TimeSpan.FromDays(1)
};
Assert.False(token.Valid);
}
[Theory, BitAutoData]
public void TokenValidityCheckNullUserIdIsInvalid(User user)
{
var token = new HCaptchaTokenable(user)
{
ExpirationDate = DateTime.UtcNow + TimeSpan.FromDays(1)
};
Assert.False(token.TokenIsValid(null));
}
[Theory, AutoData]
public void CanUpdateExpirationToNonStandard(User user)
{
var token = new HCaptchaTokenable(user)
{
ExpirationDate = DateTime.MinValue
};
Assert.Equal(DateTime.MinValue, token.ExpirationDate, TimeSpan.FromMilliseconds(10));
}
[Theory, AutoData]
public void SetsDataFromUser(User user)
{
var token = new HCaptchaTokenable(user);
Assert.Equal(user.Id, token.Id);
Assert.Equal(user.Email, token.Email);
}
[Theory, AutoData]
public void SerializationSetsCorrectDateTime(User user)
{
var expectedDateTime = DateTime.UtcNow.AddHours(-5);
var token = new HCaptchaTokenable(user)
{
ExpirationDate = expectedDateTime
};
var result = Tokenable.FromToken<HCaptchaTokenable>(token.ToToken());
Assert.Equal(expectedDateTime, result.ExpirationDate, TimeSpan.FromMilliseconds(10));
}
[Theory, AutoData]
public void IsInvalidIfIdentifierIsWrong(User user)
{
var token = new HCaptchaTokenable(user)
{
Identifier = "not correct"
};
Assert.False(token.Valid);
}
}

View File

@ -1,4 +1,5 @@
using AutoFixture.Xunit2; using Amazon.Runtime.Credentials.Internal;
using AutoFixture.Xunit2;
using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Entities;
using Bit.Core.Auth.Models.Business.Tokenables; using Bit.Core.Auth.Models.Business.Tokenables;
using Bit.Core.Tokens; using Bit.Core.Tokens;
@ -67,7 +68,7 @@ public class SsoTokenableTests
ExpirationDate = expectedDateTime ExpirationDate = expectedDateTime
}; };
var result = Tokenable.FromToken<HCaptchaTokenable>(token.ToToken()); var result = Tokenable.FromToken<SsoTokenable>(token.ToToken());
Assert.Equal(expectedDateTime, result.ExpirationDate, TimeSpan.FromMilliseconds(10)); Assert.Equal(expectedDateTime, result.ExpirationDate, TimeSpan.FromMilliseconds(10));
} }

View File

@ -3,7 +3,6 @@ using System.Text;
using Bit.Core; using Bit.Core;
using Bit.Core.Auth.Models.Api.Request.Accounts; using Bit.Core.Auth.Models.Api.Request.Accounts;
using Bit.Core.Auth.Models.Business.Tokenables; using Bit.Core.Auth.Models.Business.Tokenables;
using Bit.Core.Auth.Services;
using Bit.Core.Auth.UserFeatures.Registration; using Bit.Core.Auth.UserFeatures.Registration;
using Bit.Core.Auth.UserFeatures.WebAuthnLogin; using Bit.Core.Auth.UserFeatures.WebAuthnLogin;
using Bit.Core.Context; using Bit.Core.Context;
@ -38,7 +37,6 @@ public class AccountsControllerTests : IDisposable
private readonly ILogger<AccountsController> _logger; private readonly ILogger<AccountsController> _logger;
private readonly IUserRepository _userRepository; private readonly IUserRepository _userRepository;
private readonly IRegisterUserCommand _registerUserCommand; private readonly IRegisterUserCommand _registerUserCommand;
private readonly ICaptchaValidationService _captchaValidationService;
private readonly IDataProtectorTokenFactory<WebAuthnLoginAssertionOptionsTokenable> _assertionOptionsDataProtector; private readonly IDataProtectorTokenFactory<WebAuthnLoginAssertionOptionsTokenable> _assertionOptionsDataProtector;
private readonly IGetWebAuthnLoginCredentialAssertionOptionsCommand _getWebAuthnLoginCredentialAssertionOptionsCommand; private readonly IGetWebAuthnLoginCredentialAssertionOptionsCommand _getWebAuthnLoginCredentialAssertionOptionsCommand;
private readonly ISendVerificationEmailForRegistrationCommand _sendVerificationEmailForRegistrationCommand; private readonly ISendVerificationEmailForRegistrationCommand _sendVerificationEmailForRegistrationCommand;
@ -54,7 +52,6 @@ public class AccountsControllerTests : IDisposable
_logger = Substitute.For<ILogger<AccountsController>>(); _logger = Substitute.For<ILogger<AccountsController>>();
_userRepository = Substitute.For<IUserRepository>(); _userRepository = Substitute.For<IUserRepository>();
_registerUserCommand = Substitute.For<IRegisterUserCommand>(); _registerUserCommand = Substitute.For<IRegisterUserCommand>();
_captchaValidationService = Substitute.For<ICaptchaValidationService>();
_assertionOptionsDataProtector = Substitute.For<IDataProtectorTokenFactory<WebAuthnLoginAssertionOptionsTokenable>>(); _assertionOptionsDataProtector = Substitute.For<IDataProtectorTokenFactory<WebAuthnLoginAssertionOptionsTokenable>>();
_getWebAuthnLoginCredentialAssertionOptionsCommand = Substitute.For<IGetWebAuthnLoginCredentialAssertionOptionsCommand>(); _getWebAuthnLoginCredentialAssertionOptionsCommand = Substitute.For<IGetWebAuthnLoginCredentialAssertionOptionsCommand>();
_sendVerificationEmailForRegistrationCommand = Substitute.For<ISendVerificationEmailForRegistrationCommand>(); _sendVerificationEmailForRegistrationCommand = Substitute.For<ISendVerificationEmailForRegistrationCommand>();
@ -68,7 +65,6 @@ public class AccountsControllerTests : IDisposable
_logger, _logger,
_userRepository, _userRepository,
_registerUserCommand, _registerUserCommand,
_captchaValidationService,
_assertionOptionsDataProtector, _assertionOptionsDataProtector,
_getWebAuthnLoginCredentialAssertionOptionsCommand, _getWebAuthnLoginCredentialAssertionOptionsCommand,
_sendVerificationEmailForRegistrationCommand, _sendVerificationEmailForRegistrationCommand,

View File

@ -94,7 +94,6 @@ public class BaseRequestValidatorTests
{ {
// Arrange // Arrange
var context = CreateContext(tokenRequest, requestContext, grantResult); var context = CreateContext(tokenRequest, requestContext, grantResult);
_globalSettings.Captcha.Returns(new GlobalSettings.CaptchaSettings());
_globalSettings.SelfHosted = true; _globalSettings.SelfHosted = true;
_sut.isValid = false; _sut.isValid = false;

View File

@ -206,9 +206,7 @@ public abstract class WebApplicationFactoryBase<T> : WebApplicationFactory<T>
Replace<IEventRepository, EventRepository>(services); Replace<IEventRepository, EventRepository>(services);
Replace<IMailDeliveryService, NoopMailDeliveryService>(services); Replace<IMailDeliveryService, NoopMailDeliveryService>(services);
Replace<ICaptchaValidationService, NoopCaptchaValidationService>(services);
// TODO: Install and use azurite in CI pipeline // TODO: Install and use azurite in CI pipeline
Replace<IInstallationDeviceRepository, NoopRepos.InstallationDeviceRepository>(services); Replace<IInstallationDeviceRepository, NoopRepos.InstallationDeviceRepository>(services);

View File

@ -31,9 +31,6 @@ public class Configuration
"Learn more: https://docs.docker.com/compose/compose-file/#ports")] "Learn more: https://docs.docker.com/compose/compose-file/#ports")]
public string HttpsPort { get; set; } = "443"; public string HttpsPort { get; set; } = "443";
[Description("Configure Nginx for Captcha.")]
public bool Captcha { get; set; } = false;
[Description("Configure Nginx for SSL.")] [Description("Configure Nginx for SSL.")]
public bool Ssl { get; set; } = true; public bool Ssl { get; set; } = true;

View File

@ -73,7 +73,6 @@ public class NginxConfigBuilder
public TemplateModel(Context context) public TemplateModel(Context context)
{ {
Captcha = context.Config.Captcha;
Ssl = context.Config.Ssl; Ssl = context.Config.Ssl;
EnableKeyConnector = context.Config.EnableKeyConnector; EnableKeyConnector = context.Config.EnableKeyConnector;
EnableScim = context.Config.EnableScim; EnableScim = context.Config.EnableScim;
@ -127,7 +126,6 @@ public class NginxConfigBuilder
} }
} }
public bool Captcha { get; set; }
public bool Ssl { get; set; } public bool Ssl { get; set; }
public bool EnableKeyConnector { get; set; } public bool EnableKeyConnector { get; set; }
public bool EnableScim { get; set; } public bool EnableScim { get; set; }

View File

@ -100,16 +100,6 @@ server {
proxy_pass http://web:5000/sso-connector.html; proxy_pass http://web:5000/sso-connector.html;
} }
{{#if Captcha}}
location = /captcha-connector.html {
proxy_pass http://web:5000/captcha-connector.html;
}
location = /captcha-mobile-connector.html {
proxy_pass http://web:5000/captcha-mobile-connector.html;
}
{{/if}}
location /attachments/ { location /attachments/ {
proxy_pass http://attachments:5000/; proxy_pass http://attachments:5000/;
} }