1
0
mirror of https://github.com/bitwarden/mobile synced 2025-12-05 23:53:33 +00:00
Files
mobile/src/Core/Services/UsernameGenerationService.cs
cubemike99 f98dfa6581 [PM-4800] Send item domain name to fastmail (#2867)
* Send item domain name to fastmail

- Added a metadata field (forDomain:) to the Fastmail Forwarder API
  request that's set to the domain name of the item being added to the
  vault, or to "" if the username generator is being used in standalone
  mode. This allows the user's Fastmail account to display the domain
  name for the username that was generated.

* Minor changes for readability

* dotnet format

---------

Co-authored-by:  Audrey  <ajensen@bitwarden.com>
2023-11-17 17:17:25 -05:00

197 lines
7.5 KiB
C#

using System.Text;
using System.Threading.Tasks;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Models.Domain;
using Bit.Core.Services.EmailForwarders;
using Bit.Core.Utilities;
namespace Bit.Core.Services
{
public class UsernameGenerationService : IUsernameGenerationService
{
private const string CATCH_ALL_EMAIL_DOMAIN_FORMAT = "{0}@{1}";
private readonly ICryptoService _cryptoService;
private readonly IApiService _apiService;
private readonly IStateService _stateService;
readonly LazyResolve<ILogger> _logger = new LazyResolve<ILogger>();
private UsernameGenerationOptions _optionsCache;
public UsernameGenerationService(
ICryptoService cryptoService,
IApiService apiService,
IStateService stateService)
{
_cryptoService = cryptoService;
_apiService = apiService;
_stateService = stateService;
}
public async Task<string> GenerateAsync(UsernameGenerationOptions options)
{
switch (options.Type)
{
case UsernameType.PlusAddressedEmail:
return await GeneratePlusAddressedEmailAsync(options);
case UsernameType.CatchAllEmail:
return await GenerateCatchAllAsync(options);
case UsernameType.ForwardedEmailAlias:
return await GenerateForwardedEmailAliasAsync(options);
case UsernameType.RandomWord:
return await GenerateRandomWordAsync(options);
default:
_logger.Value.Error($"Error UsernameGenerationService: UsernameType {options.Type} not implemented.");
return string.Empty;
}
}
public async Task<UsernameGenerationOptions> GetOptionsAsync()
{
if (_optionsCache == null)
{
var options = await _stateService.GetUsernameGenerationOptionsAsync();
_optionsCache = options ?? new UsernameGenerationOptions();
}
return _optionsCache;
}
public async Task SaveOptionsAsync(UsernameGenerationOptions options)
{
await _stateService.SetUsernameGenerationOptionsAsync(options);
_optionsCache = options;
}
public void ClearCache()
{
_optionsCache = null;
}
private async Task<string> GenerateRandomWordAsync(UsernameGenerationOptions options)
{
var listLength = EEFLongWordList.Instance.List.Count - 1;
var wordIndex = await _cryptoService.RandomNumberAsync(0, listLength);
var randomWord = EEFLongWordList.Instance.List[wordIndex];
if (string.IsNullOrWhiteSpace(randomWord))
{
_logger.Value.Error($"Error UsernameGenerationService: EEFLongWordList has NullOrWhiteSpace value at {wordIndex} index.");
return Constants.DefaultUsernameGenerated;
}
if (options.CapitalizeRandomWordUsername)
{
randomWord = Capitalize(randomWord);
}
if (options.IncludeNumberRandomWordUsername)
{
randomWord = await AppendRandomNumberToRandomWordAsync(randomWord);
}
return randomWord;
}
private async Task<string> GeneratePlusAddressedEmailAsync(UsernameGenerationOptions options)
{
if (string.IsNullOrWhiteSpace(options.PlusAddressedEmail) || options.PlusAddressedEmail.Length < 3)
{
return Constants.DefaultUsernameGenerated;
}
var atIndex = options.PlusAddressedEmail.IndexOf("@");
if (atIndex < 1 || atIndex >= options.PlusAddressedEmail.Length - 1)
{
return options.PlusAddressedEmail;
}
if (options.PlusAddressedEmailType == UsernameEmailType.Random)
{
var randomString = await _cryptoService.RandomStringAsync(8);
return options.PlusAddressedEmail.Insert(atIndex, $"+{randomString}");
}
else
{
return options.PlusAddressedEmail.Insert(atIndex, $"+{options.EmailWebsite}");
}
}
private async Task<string> GenerateCatchAllAsync(UsernameGenerationOptions options)
{
var catchAllEmailDomain = options.CatchAllEmailDomain;
if (string.IsNullOrWhiteSpace(catchAllEmailDomain))
{
return Constants.DefaultUsernameGenerated;
}
if (options.CatchAllEmailType == UsernameEmailType.Random)
{
var randomString = await _cryptoService.RandomStringAsync(8);
return string.Format(CATCH_ALL_EMAIL_DOMAIN_FORMAT, randomString, catchAllEmailDomain);
}
return string.Format(CATCH_ALL_EMAIL_DOMAIN_FORMAT, options.EmailWebsite, catchAllEmailDomain);
}
private async Task<string> GenerateForwardedEmailAliasAsync(UsernameGenerationOptions options)
{
if (options.ServiceType == ForwardedEmailServiceType.AnonAddy)
{
return await new AnonAddyForwarder()
.GenerateAsync(_apiService, (AnonAddyForwarderOptions)options.GetForwarderOptions());
}
if (options.ServiceType == ForwardedEmailServiceType.ForwardEmail)
{
var forwardedEmailOptions = (ForwardEmailForwarderOptions)options.GetForwarderOptions();
return await new ForwardEmailForwarder(forwardedEmailOptions.DomainName)
.GenerateAsync(_apiService, forwardedEmailOptions);
}
if (options.ServiceType == ForwardedEmailServiceType.Fastmail)
{
var fastmailEmailOptions = (FastmailForwarderOptions)options.GetForwarderOptions();
return await new FastmailForwarder()
.GenerateAsync(_apiService, fastmailEmailOptions);
}
BaseForwarder<ForwarderOptions> simpleForwarder = null;
switch (options.ServiceType)
{
case ForwardedEmailServiceType.FirefoxRelay:
simpleForwarder = new FirefoxRelayForwarder();
break;
case ForwardedEmailServiceType.SimpleLogin:
simpleForwarder = new SimpleLoginForwarder();
break;
case ForwardedEmailServiceType.DuckDuckGo:
simpleForwarder = new DuckDuckGoForwarder();
break;
default:
_logger.Value.Error($"Error UsernameGenerationService: ForwardedEmailServiceType {options.ServiceType} not implemented.");
return Constants.DefaultUsernameGenerated;
}
return await simpleForwarder.GenerateAsync(_apiService, options.GetForwarderOptions());
}
private string Capitalize(string str)
{
return char.ToUpper(str[0]) + str.Substring(1);
}
private async Task<string> AppendRandomNumberToRandomWordAsync(string word)
{
if (string.IsNullOrWhiteSpace(word))
{
return word;
}
var randomNumber = await _cryptoService.RandomNumberAsync(1, 9999);
return word + randomNumber.ToString("0000");
}
}
}