mirror of
https://github.com/bitwarden/mobile.git
synced 2025-02-28 03:12:31 +01:00
[PM-192] Refactor forwarded email providers (#2579)
* PM-192 Refactor Forwarded email providers to use better patterns and code reuse. * PM-192 fix format
This commit is contained in:
parent
3506269811
commit
1014563c75
@ -249,27 +249,25 @@
|
|||||||
ItemDisplayBinding="{Binding ., Converter={StaticResource localizableEnum}}"
|
ItemDisplayBinding="{Binding ., Converter={StaticResource localizableEnum}}"
|
||||||
StyleClass="box-value"
|
StyleClass="box-value"
|
||||||
AutomationId="ServiceTypePicker" />
|
AutomationId="ServiceTypePicker" />
|
||||||
<!--ANONADDY OPTIONS-->
|
<Grid
|
||||||
<Grid IsVisible="{Binding ForwardedEmailServiceSelected, Converter={StaticResource enumToBool}, ConverterParameter={x:Static enums:ForwardedEmailServiceType.AnonAddy}}"
|
Grid.RowDefinitions="Auto,*"
|
||||||
Grid.RowDefinitions="Auto,*"
|
Grid.ColumnDefinitions="*,Auto">
|
||||||
Grid.ColumnDefinitions="*,Auto">
|
|
||||||
<Label
|
<Label
|
||||||
Margin="0,10,0,0"
|
Margin="0,10,0,0"
|
||||||
Text="{u:I18n APIAccessToken}"
|
Text="{Binding ForwardedEmailApiSecretLabel}"
|
||||||
StyleClass="box-label"/>
|
StyleClass="box-label"/>
|
||||||
<Entry
|
<Entry
|
||||||
x:Name="_anonAddyApiAccessTokenEntry"
|
Text="{Binding ForwardedEmailApiSecret}"
|
||||||
Text="{Binding AnonAddyApiAccessToken}"
|
IsPassword="{Binding ShowForwardedEmailApiSecret, Converter={StaticResource inverseBool}}"
|
||||||
IsPassword="{Binding ShowAnonAddyApiAccessToken, Converter={StaticResource inverseBool}}"
|
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
AutomationId="AnonAddyApiAccessTokenEntry" />
|
AutomationId="ForwardedEmailApiSecretEntry" />
|
||||||
<controls:IconButton
|
<controls:IconButton
|
||||||
StyleClass="box-row-button, box-row-button-platform"
|
StyleClass="box-row-button, box-row-button-platform"
|
||||||
Text="{Binding ShowAnonAddyApiAccessToken, Converter={StaticResource inverseBool, iconGlyphConverter}, ConverterParameter={x:Static u:BooleanGlyphType.Eye}}"
|
Text="{Binding ShowForwardedEmailApiSecret, Converter={StaticResource inverseBool, iconGlyphConverter}, ConverterParameter={x:Static u:BooleanGlyphType.Eye}}"
|
||||||
Command="{Binding ToggleForwardedEmailHiddenValueCommand}"
|
Command="{Binding ToggleForwardedEmailHiddenValueCommand}"
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
AutomationId="ShowAnonAddyApiAccessTokenButton" />
|
AutomationId="ShowForwardedEmailApiSecretButton" />
|
||||||
</Grid>
|
</Grid>
|
||||||
<Label IsVisible="{Binding ForwardedEmailServiceSelected, Converter={StaticResource enumToBool}, ConverterParameter={x:Static enums:ForwardedEmailServiceType.AnonAddy}}"
|
<Label IsVisible="{Binding ForwardedEmailServiceSelected, Converter={StaticResource enumToBool}, ConverterParameter={x:Static enums:ForwardedEmailServiceType.AnonAddy}}"
|
||||||
Text="{u:I18n DomainNameRequiredParenthesis}"
|
Text="{u:I18n DomainNameRequiredParenthesis}"
|
||||||
@ -280,98 +278,6 @@
|
|||||||
Text="{Binding AnonAddyDomainName}"
|
Text="{Binding AnonAddyDomainName}"
|
||||||
StyleClass="box-value"
|
StyleClass="box-value"
|
||||||
AutomationId="AnonAddyDomainNameEntry" />
|
AutomationId="AnonAddyDomainNameEntry" />
|
||||||
<!--FIREFOX RELAY OPTIONS-->
|
|
||||||
<Grid StyleClass="box-row, box-row-input"
|
|
||||||
IsVisible="{Binding ForwardedEmailServiceSelected, Converter={StaticResource enumToBool}, ConverterParameter={x:Static enums:ForwardedEmailServiceType.FirefoxRelay}}"
|
|
||||||
Grid.RowDefinitions="Auto,*"
|
|
||||||
Grid.ColumnDefinitions="*,Auto">
|
|
||||||
<Label
|
|
||||||
Text="{u:I18n APIAccessToken}"
|
|
||||||
StyleClass="box-label"/>
|
|
||||||
<Entry
|
|
||||||
x:Name="_firefoxRelayApiAccessTokenEntry"
|
|
||||||
Text="{Binding FirefoxRelayApiAccessToken}"
|
|
||||||
StyleClass="box-value"
|
|
||||||
Grid.Row="1"
|
|
||||||
IsPassword="{Binding ShowFirefoxRelayApiAccessToken, Converter={StaticResource inverseBool}}"
|
|
||||||
AutomationId="FirefoxRelayApiAccessTokenEntry" />
|
|
||||||
<controls:IconButton
|
|
||||||
StyleClass="box-row-button, box-row-button-platform"
|
|
||||||
Text="{Binding ShowFirefoxRelayApiAccessToken, Converter={StaticResource inverseBool, iconGlyphConverter}, ConverterParameter={x:Static u:BooleanGlyphType.Eye}}"
|
|
||||||
Command="{Binding ToggleForwardedEmailHiddenValueCommand}"
|
|
||||||
Grid.Row="1"
|
|
||||||
Grid.Column="1"
|
|
||||||
AutomationId="ShowFirefoxRelayApiAccessTokenButton" />
|
|
||||||
</Grid>
|
|
||||||
<!--SIMPLELOGIN OPTIONS-->
|
|
||||||
<Grid StyleClass="box-row, box-row-input"
|
|
||||||
IsVisible="{Binding ForwardedEmailServiceSelected, Converter={StaticResource enumToBool}, ConverterParameter={x:Static enums:ForwardedEmailServiceType.SimpleLogin}}"
|
|
||||||
Grid.RowDefinitions="Auto,*"
|
|
||||||
Grid.ColumnDefinitions="*,Auto">
|
|
||||||
<Label
|
|
||||||
Text="{u:I18n APIKeyRequiredParenthesis}"
|
|
||||||
StyleClass="box-label"/>
|
|
||||||
<Entry
|
|
||||||
x:Name="_simpleLoginApiKeyEntry"
|
|
||||||
Text="{Binding SimpleLoginApiKey}"
|
|
||||||
StyleClass="box-value"
|
|
||||||
Grid.Row="1"
|
|
||||||
IsPassword="{Binding ShowSimpleLoginApiKey, Converter={StaticResource inverseBool}}"
|
|
||||||
AutomationId="SimpleLoginApiKeyEntry" />
|
|
||||||
<controls:IconButton
|
|
||||||
StyleClass="box-row-button, box-row-button-platform"
|
|
||||||
Text="{Binding ShowSimpleLoginApiKey, Converter={StaticResource inverseBool, iconGlyphConverter}, ConverterParameter={x:Static u:BooleanGlyphType.Eye}}"
|
|
||||||
Command="{Binding ToggleForwardedEmailHiddenValueCommand}"
|
|
||||||
Grid.Row="1"
|
|
||||||
Grid.Column="1"
|
|
||||||
AutomationId="ShowSimpleLoginApiKeyButton" />
|
|
||||||
</Grid>
|
|
||||||
<!--DUCKDUCKGO OPTIONS-->
|
|
||||||
<Grid StyleClass="box-row, box-row-input"
|
|
||||||
IsVisible="{Binding ForwardedEmailServiceSelected, Converter={StaticResource enumToBool}, ConverterParameter={x:Static enums:ForwardedEmailServiceType.DuckDuckGo}}"
|
|
||||||
Grid.RowDefinitions="Auto,*"
|
|
||||||
Grid.ColumnDefinitions="*,Auto">
|
|
||||||
<Label
|
|
||||||
Text="{u:I18n APIKeyRequiredParenthesis}"
|
|
||||||
StyleClass="box-label"/>
|
|
||||||
<Entry
|
|
||||||
x:Name="_duckDuckGoApiAccessTokenEntry"
|
|
||||||
Text="{Binding DuckDuckGoApiKey}"
|
|
||||||
StyleClass="box-value"
|
|
||||||
Grid.Row="1"
|
|
||||||
IsPassword="{Binding ShowDuckDuckGoApiKey, Converter={StaticResource inverseBool}}"
|
|
||||||
AutomationId="DuckDuckGoApiKeyEntry" />
|
|
||||||
<controls:IconButton
|
|
||||||
StyleClass="box-row-button, box-row-button-platform"
|
|
||||||
Text="{Binding ShowDuckDuckGoApiKey, Converter={StaticResource inverseBool, iconGlyphConverter}, ConverterParameter={x:Static u:BooleanGlyphType.Eye}}"
|
|
||||||
Command="{Binding ToggleForwardedEmailHiddenValueCommand}"
|
|
||||||
Grid.Row="1"
|
|
||||||
Grid.Column="1"
|
|
||||||
AutomationId="ShowDuckDuckGoApiKeyButton" />
|
|
||||||
</Grid>
|
|
||||||
<!--FASTMAIL OPTIONS-->
|
|
||||||
<Grid StyleClass="box-row, box-row-input"
|
|
||||||
IsVisible="{Binding ForwardedEmailServiceSelected, Converter={StaticResource enumToBool}, ConverterParameter={x:Static enums:ForwardedEmailServiceType.Fastmail}}"
|
|
||||||
Grid.RowDefinitions="Auto,*"
|
|
||||||
Grid.ColumnDefinitions="*,Auto">
|
|
||||||
<Label
|
|
||||||
Text="{u:I18n APIKeyRequiredParenthesis}"
|
|
||||||
StyleClass="box-label"/>
|
|
||||||
<Entry
|
|
||||||
x:Name="_fastmailApiAccessTokenEntry"
|
|
||||||
Text="{Binding FastmailApiKey}"
|
|
||||||
StyleClass="box-value"
|
|
||||||
Grid.Row="1"
|
|
||||||
IsPassword="{Binding ShowFastmailApiKey, Converter={StaticResource inverseBool}}"
|
|
||||||
AutomationId="FastmailApiKeyEntry" />
|
|
||||||
<controls:IconButton
|
|
||||||
StyleClass="box-row-button, box-row-button-platform"
|
|
||||||
Text="{Binding ShowFastmailApiKey, Converter={StaticResource iconGlyphConverter}, ConverterParameter={x:Static u:BooleanGlyphType.Eye}}"
|
|
||||||
Command="{Binding ToggleForwardedEmailHiddenValueCommand}"
|
|
||||||
Grid.Row="1"
|
|
||||||
Grid.Column="1"
|
|
||||||
AutomationId="ShowFastmailApiKeyButton" />
|
|
||||||
</Grid>
|
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<!--RANDOM WORD OPTIONS-->
|
<!--RANDOM WORD OPTIONS-->
|
||||||
<Grid IsVisible="{Binding UsernameTypeSelected, Converter={StaticResource enumToBool}, ConverterParameter={x:Static enums:UsernameType.RandomWord}}">
|
<Grid IsVisible="{Binding UsernameTypeSelected, Converter={StaticResource enumToBool}, ConverterParameter={x:Static enums:UsernameType.RandomWord}}">
|
||||||
|
@ -8,6 +8,7 @@ using Bit.App.Utilities;
|
|||||||
using Bit.Core;
|
using Bit.Core;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.Domain;
|
using Bit.Core.Models.Domain;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Xamarin.CommunityToolkit.ObjectModel;
|
using Xamarin.CommunityToolkit.ObjectModel;
|
||||||
@ -23,7 +24,7 @@ namespace Bit.App.Pages
|
|||||||
private readonly IUsernameGenerationService _usernameGenerationService;
|
private readonly IUsernameGenerationService _usernameGenerationService;
|
||||||
private readonly ITokenService _tokenService;
|
private readonly ITokenService _tokenService;
|
||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
readonly LazyResolve<ILogger> _logger = new LazyResolve<ILogger>("logger");
|
readonly LazyResolve<ILogger> _logger = new LazyResolve<ILogger>();
|
||||||
|
|
||||||
private PasswordGenerationOptions _options;
|
private PasswordGenerationOptions _options;
|
||||||
private UsernameGenerationOptions _usernameOptions;
|
private UsernameGenerationOptions _usernameOptions;
|
||||||
@ -49,11 +50,7 @@ namespace Bit.App.Pages
|
|||||||
private bool _doneIniting;
|
private bool _doneIniting;
|
||||||
private bool _showTypePicker;
|
private bool _showTypePicker;
|
||||||
private string _emailWebsite;
|
private string _emailWebsite;
|
||||||
private bool _showFirefoxRelayApiAccessToken;
|
private bool _showForwardedEmailApiSecret;
|
||||||
private bool _showAnonAddyApiAccessToken;
|
|
||||||
private bool _showSimpleLoginApiKey;
|
|
||||||
private bool _showDuckDuckGoApiKey;
|
|
||||||
private bool _showFastmailApiKey;
|
|
||||||
private bool _editMode;
|
private bool _editMode;
|
||||||
|
|
||||||
public GeneratorPageViewModel()
|
public GeneratorPageViewModel()
|
||||||
@ -96,7 +93,7 @@ namespace Bit.App.Pages
|
|||||||
UsernameTypePromptHelpCommand = new Command(UsernameTypePromptHelp);
|
UsernameTypePromptHelpCommand = new Command(UsernameTypePromptHelp);
|
||||||
RegenerateCommand = new AsyncCommand(RegenerateAsync, onException: ex => OnSubmitException(ex), allowsMultipleExecutions: false);
|
RegenerateCommand = new AsyncCommand(RegenerateAsync, onException: ex => OnSubmitException(ex), allowsMultipleExecutions: false);
|
||||||
RegenerateUsernameCommand = new AsyncCommand(RegenerateUsernameAsync, onException: ex => OnSubmitException(ex), allowsMultipleExecutions: false);
|
RegenerateUsernameCommand = new AsyncCommand(RegenerateUsernameAsync, onException: ex => OnSubmitException(ex), allowsMultipleExecutions: false);
|
||||||
ToggleForwardedEmailHiddenValueCommand = new AsyncCommand(ToggleForwardedEmailHiddenValueAsync, onException: ex => _logger.Value.Exception(ex), allowsMultipleExecutions: false);
|
ToggleForwardedEmailHiddenValueCommand = new Command(() => ShowForwardedEmailApiSecret = !ShowForwardedEmailApiSecret);
|
||||||
CopyCommand = new AsyncCommand(CopyAsync, onException: ex => _logger.Value.Exception(ex), allowsMultipleExecutions: false);
|
CopyCommand = new AsyncCommand(CopyAsync, onException: ex => _logger.Value.Exception(ex), allowsMultipleExecutions: false);
|
||||||
CloseCommand = new AsyncCommand(CloseAsync, onException: ex => _logger.Value.Exception(ex), allowsMultipleExecutions: false);
|
CloseCommand = new AsyncCommand(CloseAsync, onException: ex => _logger.Value.Exception(ex), allowsMultipleExecutions: false);
|
||||||
}
|
}
|
||||||
@ -415,7 +412,6 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public string UsernameTypeDescriptionLabel => GetUsernameTypeLabelDescription(UsernameTypeSelected);
|
public string UsernameTypeDescriptionLabel => GetUsernameTypeLabelDescription(UsernameTypeSelected);
|
||||||
|
|
||||||
|
|
||||||
public ForwardedEmailServiceType ForwardedEmailServiceSelected
|
public ForwardedEmailServiceType ForwardedEmailServiceSelected
|
||||||
{
|
{
|
||||||
get => _usernameOptions.ServiceType;
|
get => _usernameOptions.ServiceType;
|
||||||
@ -425,7 +421,11 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
_usernameOptions.ServiceType = value;
|
_usernameOptions.ServiceType = value;
|
||||||
Username = Constants.DefaultUsernameGenerated;
|
Username = Constants.DefaultUsernameGenerated;
|
||||||
TriggerPropertyChanged(nameof(ForwardedEmailServiceSelected));
|
TriggerPropertyChanged(nameof(ForwardedEmailServiceSelected), new string[]
|
||||||
|
{
|
||||||
|
nameof(ForwardedEmailApiSecret),
|
||||||
|
nameof(ForwardedEmailApiSecretLabel)
|
||||||
|
});
|
||||||
SaveUsernameOptionsAsync(false).FireAndForget();
|
SaveUsernameOptionsAsync(false).FireAndForget();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -445,27 +445,104 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string AnonAddyApiAccessToken
|
public string ForwardedEmailApiSecret
|
||||||
{
|
{
|
||||||
get => _usernameOptions.AnonAddyApiAccessToken;
|
get
|
||||||
|
{
|
||||||
|
switch (ForwardedEmailServiceSelected)
|
||||||
|
{
|
||||||
|
case ForwardedEmailServiceType.AnonAddy:
|
||||||
|
return _usernameOptions.AnonAddyApiAccessToken;
|
||||||
|
case ForwardedEmailServiceType.DuckDuckGo:
|
||||||
|
return _usernameOptions.DuckDuckGoApiKey;
|
||||||
|
case ForwardedEmailServiceType.Fastmail:
|
||||||
|
return _usernameOptions.FastMailApiKey;
|
||||||
|
case ForwardedEmailServiceType.FirefoxRelay:
|
||||||
|
return _usernameOptions.FirefoxRelayApiAccessToken;
|
||||||
|
case ForwardedEmailServiceType.SimpleLogin:
|
||||||
|
return _usernameOptions.SimpleLoginApiKey;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_usernameOptions.AnonAddyApiAccessToken != value)
|
bool changed = false;
|
||||||
|
switch (ForwardedEmailServiceSelected)
|
||||||
{
|
{
|
||||||
_usernameOptions.AnonAddyApiAccessToken = value;
|
case ForwardedEmailServiceType.AnonAddy:
|
||||||
TriggerPropertyChanged(nameof(AnonAddyApiAccessToken));
|
if (_usernameOptions.AnonAddyApiAccessToken != value)
|
||||||
|
{
|
||||||
|
_usernameOptions.AnonAddyApiAccessToken = value;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ForwardedEmailServiceType.DuckDuckGo:
|
||||||
|
if (_usernameOptions.DuckDuckGoApiKey != value)
|
||||||
|
{
|
||||||
|
_usernameOptions.DuckDuckGoApiKey = value;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ForwardedEmailServiceType.Fastmail:
|
||||||
|
if (_usernameOptions.FastMailApiKey != value)
|
||||||
|
{
|
||||||
|
_usernameOptions.FastMailApiKey = value;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ForwardedEmailServiceType.FirefoxRelay:
|
||||||
|
if (_usernameOptions.FirefoxRelayApiAccessToken != value)
|
||||||
|
{
|
||||||
|
_usernameOptions.FirefoxRelayApiAccessToken = value;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ForwardedEmailServiceType.SimpleLogin:
|
||||||
|
if (_usernameOptions.SimpleLoginApiKey != value)
|
||||||
|
{
|
||||||
|
_usernameOptions.SimpleLoginApiKey = value;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed)
|
||||||
|
{
|
||||||
|
TriggerPropertyChanged(nameof(ForwardedEmailApiSecret));
|
||||||
SaveUsernameOptionsAsync(false).FireAndForget();
|
SaveUsernameOptionsAsync(false).FireAndForget();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ShowAnonAddyApiAccessToken
|
public string ForwardedEmailApiSecretLabel
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return _showAnonAddyApiAccessToken;
|
switch (ForwardedEmailServiceSelected)
|
||||||
|
{
|
||||||
|
case ForwardedEmailServiceType.AnonAddy:
|
||||||
|
case ForwardedEmailServiceType.FirefoxRelay:
|
||||||
|
return AppResources.APIAccessToken;
|
||||||
|
case ForwardedEmailServiceType.DuckDuckGo:
|
||||||
|
case ForwardedEmailServiceType.Fastmail:
|
||||||
|
case ForwardedEmailServiceType.SimpleLogin:
|
||||||
|
return AppResources.APIKeyRequiredParenthesis;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
set => SetProperty(ref _showAnonAddyApiAccessToken, value);
|
}
|
||||||
|
|
||||||
|
public bool ShowForwardedEmailApiSecret
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _showForwardedEmailApiSecret;
|
||||||
|
}
|
||||||
|
set => SetProperty(ref _showForwardedEmailApiSecret, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string AnonAddyDomainName
|
public string AnonAddyDomainName
|
||||||
@ -482,99 +559,6 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string FirefoxRelayApiAccessToken
|
|
||||||
{
|
|
||||||
get => _usernameOptions.FirefoxRelayApiAccessToken;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_usernameOptions.FirefoxRelayApiAccessToken != value)
|
|
||||||
{
|
|
||||||
_usernameOptions.FirefoxRelayApiAccessToken = value;
|
|
||||||
TriggerPropertyChanged(nameof(FirefoxRelayApiAccessToken));
|
|
||||||
SaveUsernameOptionsAsync(false).FireAndForget();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ShowFirefoxRelayApiAccessToken
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _showFirefoxRelayApiAccessToken;
|
|
||||||
}
|
|
||||||
set => SetProperty(ref _showFirefoxRelayApiAccessToken, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string SimpleLoginApiKey
|
|
||||||
{
|
|
||||||
get => _usernameOptions.SimpleLoginApiKey;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_usernameOptions.SimpleLoginApiKey != value)
|
|
||||||
{
|
|
||||||
_usernameOptions.SimpleLoginApiKey = value;
|
|
||||||
TriggerPropertyChanged(nameof(SimpleLoginApiKey));
|
|
||||||
SaveUsernameOptionsAsync(false).FireAndForget();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ShowSimpleLoginApiKey
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _showSimpleLoginApiKey;
|
|
||||||
}
|
|
||||||
set => SetProperty(ref _showSimpleLoginApiKey, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string DuckDuckGoApiKey
|
|
||||||
{
|
|
||||||
get => _usernameOptions.DuckDuckGoApiKey;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_usernameOptions.DuckDuckGoApiKey != value)
|
|
||||||
{
|
|
||||||
_usernameOptions.DuckDuckGoApiKey = value;
|
|
||||||
TriggerPropertyChanged(nameof(DuckDuckGoApiKey));
|
|
||||||
SaveUsernameOptionsAsync(false).FireAndForget();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ShowDuckDuckGoApiKey
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _showDuckDuckGoApiKey;
|
|
||||||
}
|
|
||||||
set => SetProperty(ref _showDuckDuckGoApiKey, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public string FastmailApiKey
|
|
||||||
{
|
|
||||||
get => _usernameOptions.FastMailApiKey;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_usernameOptions.FastMailApiKey != value)
|
|
||||||
{
|
|
||||||
_usernameOptions.FastMailApiKey = value;
|
|
||||||
TriggerPropertyChanged(nameof(FastmailApiKey));
|
|
||||||
SaveUsernameOptionsAsync(false).FireAndForget();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ShowFastmailApiKey
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _showFastmailApiKey;
|
|
||||||
}
|
|
||||||
set => SetProperty(ref _showFastmailApiKey, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool CapitalizeRandomWordUsername
|
public bool CapitalizeRandomWordUsername
|
||||||
{
|
{
|
||||||
get => _usernameOptions.CapitalizeRandomWordUsername;
|
get => _usernameOptions.CapitalizeRandomWordUsername;
|
||||||
@ -807,12 +791,9 @@ namespace Bit.App.Pages
|
|||||||
TriggerPropertyChanged(nameof(PlusAddressedEmailTypeSelected));
|
TriggerPropertyChanged(nameof(PlusAddressedEmailTypeSelected));
|
||||||
TriggerPropertyChanged(nameof(IncludeNumberRandomWordUsername));
|
TriggerPropertyChanged(nameof(IncludeNumberRandomWordUsername));
|
||||||
TriggerPropertyChanged(nameof(CapitalizeRandomWordUsername));
|
TriggerPropertyChanged(nameof(CapitalizeRandomWordUsername));
|
||||||
TriggerPropertyChanged(nameof(SimpleLoginApiKey));
|
TriggerPropertyChanged(nameof(ForwardedEmailApiSecret));
|
||||||
TriggerPropertyChanged(nameof(FirefoxRelayApiAccessToken));
|
TriggerPropertyChanged(nameof(ForwardedEmailApiSecretLabel));
|
||||||
TriggerPropertyChanged(nameof(AnonAddyDomainName));
|
TriggerPropertyChanged(nameof(AnonAddyDomainName));
|
||||||
TriggerPropertyChanged(nameof(AnonAddyApiAccessToken));
|
|
||||||
TriggerPropertyChanged(nameof(DuckDuckGoApiKey));
|
|
||||||
TriggerPropertyChanged(nameof(FastmailApiKey));
|
|
||||||
TriggerPropertyChanged(nameof(CatchAllEmailDomain));
|
TriggerPropertyChanged(nameof(CatchAllEmailDomain));
|
||||||
TriggerPropertyChanged(nameof(ForwardedEmailServiceSelected));
|
TriggerPropertyChanged(nameof(ForwardedEmailServiceSelected));
|
||||||
TriggerPropertyChanged(nameof(UsernameTypeSelected));
|
TriggerPropertyChanged(nameof(UsernameTypeSelected));
|
||||||
@ -845,15 +826,23 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
_logger.Value.Exception(ex);
|
_logger.Value.Exception(ex);
|
||||||
|
|
||||||
|
string message = AppResources.GenericErrorMessage;
|
||||||
|
|
||||||
if (IsUsername && UsernameTypeSelected == UsernameType.ForwardedEmailAlias)
|
if (IsUsername && UsernameTypeSelected == UsernameType.ForwardedEmailAlias)
|
||||||
{
|
{
|
||||||
await Device.InvokeOnMainThreadAsync(() => Page.DisplayAlert(
|
if (ex is ForwardedEmailInvalidSecretException)
|
||||||
AppResources.AnErrorHasOccurred, string.Format(AppResources.UnknownXErrorMessage, ForwardedEmailServiceSelected), AppResources.Ok));
|
{
|
||||||
}
|
message = ForwardedEmailServiceSelected == ForwardedEmailServiceType.AnonAddy || ForwardedEmailServiceSelected == ForwardedEmailServiceType.FirefoxRelay
|
||||||
else
|
? AppResources.InvalidAPIToken
|
||||||
{
|
: AppResources.InvalidAPIKey;
|
||||||
await Device.InvokeOnMainThreadAsync(() => Page.DisplayAlert(AppResources.AnErrorHasOccurred, AppResources.GenericErrorMessage, AppResources.Ok));
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
message = string.Format(AppResources.UnknownXErrorMessage, ForwardedEmailServiceSelected);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await Device.InvokeOnMainThreadAsync(() => Page.DisplayAlert(AppResources.AnErrorHasOccurred, message, AppResources.Ok));
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetUsernameTypeLabelDescription(UsernameType value)
|
private string GetUsernameTypeLabelDescription(UsernameType value)
|
||||||
@ -870,27 +859,5 @@ namespace Bit.App.Pages
|
|||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ToggleForwardedEmailHiddenValueAsync()
|
|
||||||
{
|
|
||||||
switch (ForwardedEmailServiceSelected)
|
|
||||||
{
|
|
||||||
case ForwardedEmailServiceType.AnonAddy:
|
|
||||||
ShowAnonAddyApiAccessToken = !ShowAnonAddyApiAccessToken;
|
|
||||||
break;
|
|
||||||
case ForwardedEmailServiceType.FirefoxRelay:
|
|
||||||
ShowFirefoxRelayApiAccessToken = !ShowFirefoxRelayApiAccessToken;
|
|
||||||
break;
|
|
||||||
case ForwardedEmailServiceType.SimpleLogin:
|
|
||||||
ShowSimpleLoginApiKey = !ShowSimpleLoginApiKey;
|
|
||||||
break;
|
|
||||||
case ForwardedEmailServiceType.DuckDuckGo:
|
|
||||||
ShowDuckDuckGoApiKey = !ShowDuckDuckGoApiKey;
|
|
||||||
break;
|
|
||||||
case ForwardedEmailServiceType.Fastmail:
|
|
||||||
ShowFastmailApiKey = !ShowFastmailApiKey;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
18
src/App/Resources/AppResources.Designer.cs
generated
18
src/App/Resources/AppResources.Designer.cs
generated
@ -3262,6 +3262,24 @@ namespace Bit.App.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Invalid API key.
|
||||||
|
/// </summary>
|
||||||
|
public static string InvalidAPIKey {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("InvalidAPIKey", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Invalid API token.
|
||||||
|
/// </summary>
|
||||||
|
public static string InvalidAPIToken {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("InvalidAPIToken", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Invalid email address..
|
/// Looks up a localized string similar to Invalid email address..
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -2637,4 +2637,10 @@ Do you want to switch to this account?</value>
|
|||||||
<data name="UnlockingMayFailDueToInsufficientMemoryDecreaseYourKDFMemorySettingsToResolve" xml:space="preserve">
|
<data name="UnlockingMayFailDueToInsufficientMemoryDecreaseYourKDFMemorySettingsToResolve" xml:space="preserve">
|
||||||
<value>Unlocking may fail due to insufficient memory. Decrease your KDF memory settings to resolve.</value>
|
<value>Unlocking may fail due to insufficient memory. Decrease your KDF memory settings to resolve.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="InvalidAPIKey" xml:space="preserve">
|
||||||
|
<value>Invalid API key</value>
|
||||||
|
</data>
|
||||||
|
<data name="InvalidAPIToken" xml:space="preserve">
|
||||||
|
<value>Invalid API token</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.Core.Enums;
|
|
||||||
using Bit.Core.Models.Domain;
|
using Bit.Core.Models.Domain;
|
||||||
using Bit.Core.Models.Request;
|
using Bit.Core.Models.Request;
|
||||||
using Bit.Core.Models.Response;
|
using Bit.Core.Models.Response;
|
||||||
@ -48,6 +49,7 @@ namespace Bit.Core.Abstractions
|
|||||||
Task<SsoPrevalidateResponse> PreValidateSso(string identifier);
|
Task<SsoPrevalidateResponse> PreValidateSso(string identifier);
|
||||||
Task<TResponse> SendAsync<TRequest, TResponse>(HttpMethod method, string path,
|
Task<TResponse> SendAsync<TRequest, TResponse>(HttpMethod method, string path,
|
||||||
TRequest body, bool authed, bool hasResponse, Action<HttpRequestMessage> alterRequest, bool logoutOnUnauthorized = true);
|
TRequest body, bool authed, bool hasResponse, Action<HttpRequestMessage> alterRequest, bool logoutOnUnauthorized = true);
|
||||||
|
Task<HttpResponseMessage> SendAsync(HttpRequestMessage requestMessage, CancellationToken cancellationToken = default);
|
||||||
void SetUrls(EnvironmentUrls urls);
|
void SetUrls(EnvironmentUrls urls);
|
||||||
[Obsolete("Mar 25 2021: This method has been deprecated in favor of direct uploads. This method still exists for backward compatibility with old server versions.")]
|
[Obsolete("Mar 25 2021: This method has been deprecated in favor of direct uploads. This method still exists for backward compatibility with old server versions.")]
|
||||||
Task<CipherResponse> PostCipherAttachmentLegacyAsync(string id, MultipartFormDataContent data);
|
Task<CipherResponse> PostCipherAttachmentLegacyAsync(string id, MultipartFormDataContent data);
|
||||||
@ -89,9 +91,9 @@ namespace Bit.Core.Abstractions
|
|||||||
Task<PasswordlessLoginResponse> GetAuthResponseAsync(string id, string accessCode);
|
Task<PasswordlessLoginResponse> GetAuthResponseAsync(string id, string accessCode);
|
||||||
Task<PasswordlessLoginResponse> PutAuthRequestAsync(string id, string key, string masterPasswordHash, string deviceIdentifier, bool requestApproved);
|
Task<PasswordlessLoginResponse> PutAuthRequestAsync(string id, string key, string masterPasswordHash, string deviceIdentifier, bool requestApproved);
|
||||||
Task<PasswordlessLoginResponse> PostCreateRequestAsync(PasswordlessCreateLoginRequest passwordlessCreateLoginRequest);
|
Task<PasswordlessLoginResponse> PostCreateRequestAsync(PasswordlessCreateLoginRequest passwordlessCreateLoginRequest);
|
||||||
Task<string> GetUsernameFromAsync(ForwardedEmailServiceType service, UsernameGeneratorConfig config);
|
|
||||||
Task<bool> GetKnownDeviceAsync(string email, string deviceIdentifier);
|
Task<bool> GetKnownDeviceAsync(string email, string deviceIdentifier);
|
||||||
Task<OrganizationDomainSsoDetailsResponse> GetOrgDomainSsoDetailsAsync(string email);
|
Task<OrganizationDomainSsoDetailsResponse> GetOrgDomainSsoDetailsAsync(string email);
|
||||||
Task<ConfigResponse> GetConfigsAsync();
|
Task<ConfigResponse> GetConfigsAsync();
|
||||||
|
Task<string> GetFastmailAccountIdAsync(string apiKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,7 @@ namespace Bit.Core.Abstractions
|
|||||||
Task<Tuple<EncString, SymmetricCryptoKey>> MakeShareKeyAsync();
|
Task<Tuple<EncString, SymmetricCryptoKey>> MakeShareKeyAsync();
|
||||||
Task<SymmetricCryptoKey> MakeSendKeyAsync(byte[] keyMaterial);
|
Task<SymmetricCryptoKey> MakeSendKeyAsync(byte[] keyMaterial);
|
||||||
Task<int> RandomNumberAsync(int min, int max);
|
Task<int> RandomNumberAsync(int min, int max);
|
||||||
|
Task<string> RandomStringAsync(int length);
|
||||||
Task<Tuple<SymmetricCryptoKey, EncString>> RemakeEncKeyAsync(SymmetricCryptoKey key);
|
Task<Tuple<SymmetricCryptoKey, EncString>> RemakeEncKeyAsync(SymmetricCryptoKey key);
|
||||||
Task<EncString> RsaEncryptAsync(byte[] data, byte[] publicKey = null);
|
Task<EncString> RsaEncryptAsync(byte[] data, byte[] publicKey = null);
|
||||||
Task<byte[]> RsaDecryptAsync(string encValue, byte[] privateKey = null);
|
Task<byte[]> RsaDecryptAsync(string encValue, byte[] privateKey = null);
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
<None Remove="Attributes\" />
|
<None Remove="Attributes\" />
|
||||||
<None Remove="MessagePack" />
|
<None Remove="MessagePack" />
|
||||||
<None Remove="MessagePack.MSBuild.Tasks" />
|
<None Remove="MessagePack.MSBuild.Tasks" />
|
||||||
|
<None Remove="Services\EmailForwarders\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@ -44,5 +45,6 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="Services\Logging\" />
|
<Folder Include="Services\Logging\" />
|
||||||
<Folder Include="Attributes\" />
|
<Folder Include="Attributes\" />
|
||||||
|
<Folder Include="Services\EmailForwarders\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
11
src/Core/Exceptions/ForwardedEmailInvalidSecretException.cs
Normal file
11
src/Core/Exceptions/ForwardedEmailInvalidSecretException.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
namespace Bit.Core.Exceptions
|
||||||
|
{
|
||||||
|
public class ForwardedEmailInvalidSecretException : Exception
|
||||||
|
{
|
||||||
|
public ForwardedEmailInvalidSecretException(Exception innerEx)
|
||||||
|
: base("Invalid API Secret", innerEx)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Services.EmailForwarders;
|
||||||
|
|
||||||
namespace Bit.Core.Models.Domain
|
namespace Bit.Core.Models.Domain
|
||||||
{
|
{
|
||||||
@ -24,5 +25,33 @@ namespace Bit.Core.Models.Domain
|
|||||||
public string AnonAddyApiAccessToken { get; set; }
|
public string AnonAddyApiAccessToken { get; set; }
|
||||||
public string AnonAddyDomainName { get; set; }
|
public string AnonAddyDomainName { get; set; }
|
||||||
public string EmailWebsite { get; set; }
|
public string EmailWebsite { get; set; }
|
||||||
|
|
||||||
|
public ForwarderOptions GetForwarderOptions()
|
||||||
|
{
|
||||||
|
if (Type != UsernameType.ForwardedEmailAlias)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (ServiceType)
|
||||||
|
{
|
||||||
|
case ForwardedEmailServiceType.AnonAddy:
|
||||||
|
return new AnonAddyForwarderOptions
|
||||||
|
{
|
||||||
|
ApiKey = AnonAddyApiAccessToken,
|
||||||
|
DomainName = AnonAddyDomainName
|
||||||
|
};
|
||||||
|
case ForwardedEmailServiceType.DuckDuckGo:
|
||||||
|
return new ForwarderOptions { ApiKey = DuckDuckGoApiKey };
|
||||||
|
case ForwardedEmailServiceType.Fastmail:
|
||||||
|
return new ForwarderOptions { ApiKey = FastMailApiKey };
|
||||||
|
case ForwardedEmailServiceType.FirefoxRelay:
|
||||||
|
return new ForwarderOptions { ApiKey = FirefoxRelayApiAccessToken };
|
||||||
|
case ForwardedEmailServiceType.SimpleLogin:
|
||||||
|
return new ForwarderOptions { ApiKey = SimpleLoginApiKey };
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
namespace Bit.Core.Models.Domain
|
|
||||||
{
|
|
||||||
public class UsernameGeneratorConfig
|
|
||||||
{
|
|
||||||
public string ApiToken { get; set; }
|
|
||||||
public string Domain { get; set; }
|
|
||||||
public string Url { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,6 +4,7 @@ using System.Linq;
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
@ -763,111 +764,29 @@ namespace Bit.Core.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> GetUsernameFromAsync(ForwardedEmailServiceType service, UsernameGeneratorConfig config)
|
public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage requestMessage, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
using (var requestMessage = new HttpRequestMessage())
|
HttpResponseMessage response;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
requestMessage.Version = new Version(1, 0);
|
response = await _httpClient.SendAsync(requestMessage, cancellationToken);
|
||||||
requestMessage.Method = HttpMethod.Post;
|
|
||||||
requestMessage.RequestUri = new Uri(config.Url);
|
|
||||||
requestMessage.Headers.Add("Accept", "application/json");
|
|
||||||
|
|
||||||
switch (service)
|
|
||||||
{
|
|
||||||
case ForwardedEmailServiceType.AnonAddy:
|
|
||||||
requestMessage.Headers.Add("Authorization", $"Bearer {config.ApiToken}");
|
|
||||||
requestMessage.Content = new FormUrlEncodedContent(new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
["domain"] = config.Domain
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case ForwardedEmailServiceType.FirefoxRelay:
|
|
||||||
requestMessage.Headers.Add("Authorization", $"Token {config.ApiToken}");
|
|
||||||
requestMessage.Content = new StringContent(JsonConvert.SerializeObject(
|
|
||||||
new
|
|
||||||
{
|
|
||||||
enabled = true,
|
|
||||||
description = "Generated by Bitwarden."
|
|
||||||
}), Encoding.UTF8, "application/json");
|
|
||||||
break;
|
|
||||||
case ForwardedEmailServiceType.SimpleLogin:
|
|
||||||
requestMessage.Headers.Add("Authentication", config.ApiToken);
|
|
||||||
break;
|
|
||||||
case ForwardedEmailServiceType.DuckDuckGo:
|
|
||||||
requestMessage.Headers.Add("Authorization", $"Bearer {config.ApiToken}");
|
|
||||||
break;
|
|
||||||
case ForwardedEmailServiceType.Fastmail:
|
|
||||||
requestMessage.Headers.Add("Authorization", $"Bearer {config.ApiToken}");
|
|
||||||
requestMessage.Content = new StringContent(await CreateFastmailRequest(config.ApiToken),
|
|
||||||
Encoding.UTF8, "application/json");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpResponseMessage response;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
response = await _httpClient.SendAsync(requestMessage);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
throw new ApiException(HandleWebError(e));
|
|
||||||
}
|
|
||||||
if (!response.IsSuccessStatusCode)
|
|
||||||
{
|
|
||||||
throw new ApiException(new ErrorResponse
|
|
||||||
{
|
|
||||||
StatusCode = response.StatusCode,
|
|
||||||
Message = $"{service} error: {(int)response.StatusCode} {response.ReasonPhrase}."
|
|
||||||
});
|
|
||||||
}
|
|
||||||
var responseJsonString = await response.Content.ReadAsStringAsync();
|
|
||||||
var result = JObject.Parse(responseJsonString);
|
|
||||||
|
|
||||||
switch (service)
|
|
||||||
{
|
|
||||||
case ForwardedEmailServiceType.AnonAddy:
|
|
||||||
return result["data"]?["email"]?.ToString();
|
|
||||||
case ForwardedEmailServiceType.FirefoxRelay:
|
|
||||||
return result["full_address"]?.ToString();
|
|
||||||
case ForwardedEmailServiceType.SimpleLogin:
|
|
||||||
return result["alias"]?.ToString();
|
|
||||||
case ForwardedEmailServiceType.DuckDuckGo:
|
|
||||||
return $"{result["address"]?.ToString()}@duck.com";
|
|
||||||
case ForwardedEmailServiceType.Fastmail:
|
|
||||||
return HandleFastMailResponse(result);
|
|
||||||
default:
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new ApiException(HandleWebError(e));
|
||||||
|
}
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
throw new ApiException(new ErrorResponse
|
||||||
|
{
|
||||||
|
StatusCode = response.StatusCode,
|
||||||
|
Message = $"{requestMessage.RequestUri} error: {(int)response.StatusCode} {response.ReasonPhrase}."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string HandleFastMailResponse(JObject result)
|
public async Task<string> GetFastmailAccountIdAsync(string apiKey)
|
||||||
{
|
|
||||||
if (result["methodResponses"] == null || !result["methodResponses"].HasValues ||
|
|
||||||
!result["methodResponses"][0].HasValues)
|
|
||||||
{
|
|
||||||
throw new Exception("Fastmail error: could not parse response.");
|
|
||||||
}
|
|
||||||
if (result["methodResponses"][0][0].ToString() == "MaskedEmail/set")
|
|
||||||
{
|
|
||||||
if (result["methodResponses"][0][1]?["created"]?["new-masked-email"] != null)
|
|
||||||
{
|
|
||||||
return result["methodResponses"][0][1]?["created"]?["new-masked-email"]?["email"].ToString();
|
|
||||||
}
|
|
||||||
if (result["methodResponses"][0][1]?["notCreated"]?["new-masked-email"] != null)
|
|
||||||
{
|
|
||||||
throw new Exception("Fastmail error: " +
|
|
||||||
result["methodResponses"][0][1]?["created"]?["new-masked-email"]?["description"].ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (result["methodResponses"][0][0].ToString() == "error")
|
|
||||||
{
|
|
||||||
throw new Exception("Fastmail error: " + result["methodResponses"][0][1]?["description"].ToString());
|
|
||||||
}
|
|
||||||
throw new Exception("Fastmail error: could not parse response.");
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<string> CreateFastmailRequest(string apiKey)
|
|
||||||
{
|
{
|
||||||
using (var httpclient = new HttpClient())
|
using (var httpclient = new HttpClient())
|
||||||
{
|
{
|
||||||
@ -891,36 +810,7 @@ namespace Bit.Core.Services
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
var result = JObject.Parse(await response.Content.ReadAsStringAsync());
|
var result = JObject.Parse(await response.Content.ReadAsStringAsync());
|
||||||
var accountId = result["primaryAccounts"]?["https://www.fastmail.com/dev/maskedemail"]?.ToString();
|
return result["primaryAccounts"]?["https://www.fastmail.com/dev/maskedemail"]?.ToString();
|
||||||
var requestJObj = new JObject
|
|
||||||
{
|
|
||||||
new JProperty("using",
|
|
||||||
new JArray { "https://www.fastmail.com/dev/maskedemail", "urn:ietf:params:jmap:core" }),
|
|
||||||
new JProperty("methodCalls",
|
|
||||||
new JArray
|
|
||||||
{
|
|
||||||
new JArray
|
|
||||||
{
|
|
||||||
"MaskedEmail/set",
|
|
||||||
new JObject
|
|
||||||
{
|
|
||||||
["accountId"] = accountId,
|
|
||||||
["create"] = new JObject
|
|
||||||
{
|
|
||||||
["new-masked-email"] = new JObject
|
|
||||||
{
|
|
||||||
["state"] = "enabled",
|
|
||||||
["description"] = "",
|
|
||||||
["url"] = "",
|
|
||||||
["emailPrefix"] = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"0"
|
|
||||||
}
|
|
||||||
})
|
|
||||||
};
|
|
||||||
return requestJObj.ToString();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,8 @@ namespace Bit.Core.Services
|
|||||||
{
|
{
|
||||||
public class CryptoService : ICryptoService
|
public class CryptoService : ICryptoService
|
||||||
{
|
{
|
||||||
|
private const string RANDOM_STRING_CHARSET = "abcdefghijklmnopqrstuvwxyz1234567890";
|
||||||
|
|
||||||
private readonly IStateService _stateService;
|
private readonly IStateService _stateService;
|
||||||
private readonly ICryptoFunctionService _cryptoFunctionService;
|
private readonly ICryptoFunctionService _cryptoFunctionService;
|
||||||
|
|
||||||
@ -633,6 +635,22 @@ namespace Bit.Core.Services
|
|||||||
return (int)(min + (ui % diff));
|
return (int)(min + (ui % diff));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Makes random string with length <paramref name="length"/> based on the charset <see cref="RANDOM_STRING_CHARSET"/>
|
||||||
|
/// </summary>
|
||||||
|
public async Task<string> RandomStringAsync(int length)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
|
||||||
|
for (var i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
var randomCharIndex = await RandomNumberAsync(0, RANDOM_STRING_CHARSET.Length - 1);
|
||||||
|
sb.Append(RANDOM_STRING_CHARSET[randomCharIndex]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
|
|
||||||
private async Task<EncryptedObject> AesEncryptAsync(byte[] data, SymmetricCryptoKey key)
|
private async Task<EncryptedObject> AesEncryptAsync(byte[] data, SymmetricCryptoKey key)
|
||||||
|
42
src/Core/Services/EmailForwarders/AnonAddyForwarder.cs
Normal file
42
src/Core/Services/EmailForwarders/AnonAddyForwarder.cs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace Bit.Core.Services.EmailForwarders
|
||||||
|
{
|
||||||
|
public class AnonAddyForwarderOptions : ForwarderOptions
|
||||||
|
{
|
||||||
|
public string DomainName { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AnonAddyForwarder : BaseForwarder<AnonAddyForwarderOptions>
|
||||||
|
{
|
||||||
|
protected override string RequestUri => "https://app.anonaddy.com/api/v1/aliases";
|
||||||
|
|
||||||
|
protected override bool CanGenerate(AnonAddyForwarderOptions options)
|
||||||
|
{
|
||||||
|
return !string.IsNullOrWhiteSpace(options.ApiKey) && !string.IsNullOrWhiteSpace(options.DomainName);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ConfigureHeaders(HttpRequestHeaders headers, AnonAddyForwarderOptions options)
|
||||||
|
{
|
||||||
|
headers.Add("Authorization", $"Bearer {options.ApiKey}");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Task<HttpContent> GetContentAsync(IApiService apiService, AnonAddyForwarderOptions options)
|
||||||
|
{
|
||||||
|
return Task.FromResult<HttpContent>(new FormUrlEncodedContent(new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
["domain"] = options.DomainName
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override string HandleResponse(JObject result)
|
||||||
|
{
|
||||||
|
return result["data"]?["email"]?.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
63
src/Core/Services/EmailForwarders/BaseForwarder.cs
Normal file
63
src/Core/Services/EmailForwarders/BaseForwarder.cs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
using System;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace Bit.Core.Services.EmailForwarders
|
||||||
|
{
|
||||||
|
public abstract class BaseForwarder<T>
|
||||||
|
where T : ForwarderOptions
|
||||||
|
{
|
||||||
|
protected abstract string RequestUri { get; }
|
||||||
|
|
||||||
|
public async Task<string> GenerateAsync(IApiService apiService, T options)
|
||||||
|
{
|
||||||
|
if (!CanGenerate(options))
|
||||||
|
{
|
||||||
|
return Constants.DefaultUsernameGenerated;
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var requestMessage = new HttpRequestMessage())
|
||||||
|
{
|
||||||
|
requestMessage.Version = new Version(1, 0);
|
||||||
|
requestMessage.Method = HttpMethod.Post;
|
||||||
|
requestMessage.RequestUri = new Uri(RequestUri);
|
||||||
|
requestMessage.Headers.Add("Accept", "application/json");
|
||||||
|
|
||||||
|
ConfigureHeaders(requestMessage.Headers, options);
|
||||||
|
requestMessage.Content = await GetContentAsync(apiService, options);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await apiService.SendAsync(requestMessage);
|
||||||
|
|
||||||
|
var responseJsonString = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
return HandleResponse(JObject.Parse(responseJsonString));
|
||||||
|
}
|
||||||
|
catch (ApiException ex)
|
||||||
|
{
|
||||||
|
if (IsRequestSecretInvalid(ex))
|
||||||
|
{
|
||||||
|
throw new ForwardedEmailInvalidSecretException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual bool CanGenerate(T options) => !string.IsNullOrWhiteSpace(options.ApiKey);
|
||||||
|
|
||||||
|
protected abstract void ConfigureHeaders(HttpRequestHeaders headers, T options);
|
||||||
|
|
||||||
|
protected abstract Task<HttpContent> GetContentAsync(IApiService apiService, T options);
|
||||||
|
|
||||||
|
protected abstract string HandleResponse(JObject result);
|
||||||
|
|
||||||
|
protected virtual bool IsRequestSecretInvalid(ApiException ex) => ex.Error?.StatusCode == System.Net.HttpStatusCode.Unauthorized;
|
||||||
|
}
|
||||||
|
}
|
25
src/Core/Services/EmailForwarders/DuckDuckGoForwarder.cs
Normal file
25
src/Core/Services/EmailForwarders/DuckDuckGoForwarder.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace Bit.Core.Services.EmailForwarders
|
||||||
|
{
|
||||||
|
public class DuckDuckGoForwarder : BaseForwarder<ForwarderOptions>
|
||||||
|
{
|
||||||
|
protected override string RequestUri => "https://quack.duckduckgo.com/api/email/addresses";
|
||||||
|
|
||||||
|
protected override void ConfigureHeaders(HttpRequestHeaders headers, ForwarderOptions options)
|
||||||
|
{
|
||||||
|
headers.Add("Authorization", $"Bearer {options.ApiKey}");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Task<HttpContent> GetContentAsync(IApiService apiService, ForwarderOptions options) => Task.FromResult<HttpContent>(null);
|
||||||
|
|
||||||
|
protected override string HandleResponse(JObject result)
|
||||||
|
{
|
||||||
|
return $"{result["address"]?.ToString()}@duck.com";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
98
src/Core/Services/EmailForwarders/FastmailForwarder.cs
Normal file
98
src/Core/Services/EmailForwarders/FastmailForwarder.cs
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
using System;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace Bit.Core.Services.EmailForwarders
|
||||||
|
{
|
||||||
|
public class FastmailForwarder : BaseForwarder<ForwarderOptions>
|
||||||
|
{
|
||||||
|
protected override string RequestUri => "https://api.fastmail.com/jmap/api/";
|
||||||
|
|
||||||
|
protected override void ConfigureHeaders(HttpRequestHeaders headers, ForwarderOptions options)
|
||||||
|
{
|
||||||
|
headers.Add("Authorization", $"Bearer {options.ApiKey}");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task<HttpContent> GetContentAsync(IApiService apiService, ForwarderOptions options)
|
||||||
|
{
|
||||||
|
string accountId = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
accountId = await apiService.GetFastmailAccountIdAsync(options.ApiKey);
|
||||||
|
}
|
||||||
|
catch (ApiException ex)
|
||||||
|
{
|
||||||
|
if (IsRequestSecretInvalid(ex))
|
||||||
|
{
|
||||||
|
throw new ForwardedEmailInvalidSecretException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
var requestJObj = new JObject
|
||||||
|
{
|
||||||
|
new JProperty("using",
|
||||||
|
new JArray { "https://www.fastmail.com/dev/maskedemail", "urn:ietf:params:jmap:core" }),
|
||||||
|
new JProperty("methodCalls",
|
||||||
|
new JArray
|
||||||
|
{
|
||||||
|
new JArray
|
||||||
|
{
|
||||||
|
"MaskedEmail/set",
|
||||||
|
new JObject
|
||||||
|
{
|
||||||
|
["accountId"] = accountId,
|
||||||
|
["create"] = new JObject
|
||||||
|
{
|
||||||
|
["new-masked-email"] = new JObject
|
||||||
|
{
|
||||||
|
["state"] = "enabled",
|
||||||
|
["description"] = "",
|
||||||
|
["url"] = "",
|
||||||
|
["emailPrefix"] = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"0"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
return new StringContent(requestJObj.ToString(), Encoding.UTF8, "application/json");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override string HandleResponse(JObject result)
|
||||||
|
{
|
||||||
|
if (result["methodResponses"] == null || !result["methodResponses"].HasValues ||
|
||||||
|
!result["methodResponses"][0].HasValues)
|
||||||
|
{
|
||||||
|
throw new Exception("Fastmail error: could not parse response.");
|
||||||
|
}
|
||||||
|
if (result["methodResponses"][0][0].ToString() == "MaskedEmail/set")
|
||||||
|
{
|
||||||
|
if (result["methodResponses"][0][1]?["created"]?["new-masked-email"] != null)
|
||||||
|
{
|
||||||
|
return result["methodResponses"][0][1]?["created"]?["new-masked-email"]?["email"].ToString();
|
||||||
|
}
|
||||||
|
if (result["methodResponses"][0][1]?["notCreated"]?["new-masked-email"] != null)
|
||||||
|
{
|
||||||
|
throw new Exception("Fastmail error: " +
|
||||||
|
result["methodResponses"][0][1]?["created"]?["new-masked-email"]?["description"].ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (result["methodResponses"][0][0].ToString() == "error")
|
||||||
|
{
|
||||||
|
throw new Exception("Fastmail error: " + result["methodResponses"][0][1]?["description"].ToString());
|
||||||
|
}
|
||||||
|
throw new Exception("Fastmail error: could not parse response.");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool IsRequestSecretInvalid(ApiException ex) => base.IsRequestSecretInvalid(ex) || ex.Error?.StatusCode == System.Net.HttpStatusCode.Forbidden;
|
||||||
|
}
|
||||||
|
}
|
36
src/Core/Services/EmailForwarders/FirefoxRelayForwarder.cs
Normal file
36
src/Core/Services/EmailForwarders/FirefoxRelayForwarder.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace Bit.Core.Services.EmailForwarders
|
||||||
|
{
|
||||||
|
public class FirefoxRelayForwarder : BaseForwarder<ForwarderOptions>
|
||||||
|
{
|
||||||
|
protected override string RequestUri => "https://relay.firefox.com/api/v1/relayaddresses/";
|
||||||
|
|
||||||
|
protected override void ConfigureHeaders(HttpRequestHeaders headers, ForwarderOptions options)
|
||||||
|
{
|
||||||
|
headers.Add("Authorization", $"Token {options.ApiKey}");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Task<HttpContent> GetContentAsync(IApiService apiService, ForwarderOptions options)
|
||||||
|
{
|
||||||
|
return Task.FromResult<HttpContent>(new StringContent(
|
||||||
|
JsonConvert.SerializeObject(
|
||||||
|
new
|
||||||
|
{
|
||||||
|
enabled = true,
|
||||||
|
description = "Generated by Bitwarden."
|
||||||
|
}), Encoding.UTF8, "application/json"));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override string HandleResponse(JObject result)
|
||||||
|
{
|
||||||
|
return result["full_address"]?.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
src/Core/Services/EmailForwarders/ForwarderOptions.cs
Normal file
7
src/Core/Services/EmailForwarders/ForwarderOptions.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace Bit.Core.Services.EmailForwarders
|
||||||
|
{
|
||||||
|
public class ForwarderOptions
|
||||||
|
{
|
||||||
|
public string ApiKey { get; set; }
|
||||||
|
}
|
||||||
|
}
|
25
src/Core/Services/EmailForwarders/SimpleLoginForwarder.cs
Normal file
25
src/Core/Services/EmailForwarders/SimpleLoginForwarder.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace Bit.Core.Services.EmailForwarders
|
||||||
|
{
|
||||||
|
public class SimpleLoginForwarder : BaseForwarder<ForwarderOptions>
|
||||||
|
{
|
||||||
|
protected override string RequestUri => "https://app.simplelogin.io/api/alias/random/new";
|
||||||
|
|
||||||
|
protected override void ConfigureHeaders(HttpRequestHeaders headers, ForwarderOptions options)
|
||||||
|
{
|
||||||
|
headers.Add("Authentication", options.ApiKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Task<HttpContent> GetContentAsync(IApiService apiService, ForwarderOptions options) => Task.FromResult<HttpContent>(null);
|
||||||
|
|
||||||
|
protected override string HandleResponse(JObject result)
|
||||||
|
{
|
||||||
|
return result["alias"]?.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,9 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models.Domain;
|
using Bit.Core.Models.Domain;
|
||||||
|
using Bit.Core.Services.EmailForwarders;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
namespace Bit.Core.Services
|
namespace Bit.Core.Services
|
||||||
@ -12,7 +14,7 @@ namespace Bit.Core.Services
|
|||||||
private readonly ICryptoService _cryptoService;
|
private readonly ICryptoService _cryptoService;
|
||||||
private readonly IApiService _apiService;
|
private readonly IApiService _apiService;
|
||||||
private readonly IStateService _stateService;
|
private readonly IStateService _stateService;
|
||||||
readonly LazyResolve<ILogger> _logger = new LazyResolve<ILogger>("logger");
|
readonly LazyResolve<ILogger> _logger = new LazyResolve<ILogger>();
|
||||||
private UsernameGenerationOptions _optionsCache;
|
private UsernameGenerationOptions _optionsCache;
|
||||||
|
|
||||||
public UsernameGenerationService(
|
public UsernameGenerationService(
|
||||||
@ -104,7 +106,7 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
if (options.PlusAddressedEmailType == UsernameEmailType.Random)
|
if (options.PlusAddressedEmailType == UsernameEmailType.Random)
|
||||||
{
|
{
|
||||||
var randomString = await RandomStringAsync(8);
|
var randomString = await _cryptoService.RandomStringAsync(8);
|
||||||
return options.PlusAddressedEmail.Insert(atIndex, $"+{randomString}");
|
return options.PlusAddressedEmail.Insert(atIndex, $"+{randomString}");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -124,7 +126,7 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
if (options.CatchAllEmailType == UsernameEmailType.Random)
|
if (options.CatchAllEmailType == UsernameEmailType.Random)
|
||||||
{
|
{
|
||||||
var randomString = await RandomStringAsync(8);
|
var randomString = await _cryptoService.RandomStringAsync(8);
|
||||||
return string.Format(CATCH_ALL_EMAIL_DOMAIN_FORMAT, randomString, catchAllEmailDomain);
|
return string.Format(CATCH_ALL_EMAIL_DOMAIN_FORMAT, randomString, catchAllEmailDomain);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,85 +135,34 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
private async Task<string> GenerateForwardedEmailAliasAsync(UsernameGenerationOptions options)
|
private async Task<string> GenerateForwardedEmailAliasAsync(UsernameGenerationOptions options)
|
||||||
{
|
{
|
||||||
|
if (options.ServiceType == ForwardedEmailServiceType.AnonAddy)
|
||||||
|
{
|
||||||
|
return await new AnonAddyForwarder()
|
||||||
|
.GenerateAsync(_apiService, (AnonAddyForwarderOptions)options.GetForwarderOptions());
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseForwarder<ForwarderOptions> simpleForwarder = null;
|
||||||
|
|
||||||
switch (options.ServiceType)
|
switch (options.ServiceType)
|
||||||
{
|
{
|
||||||
case ForwardedEmailServiceType.AnonAddy:
|
|
||||||
if (string.IsNullOrWhiteSpace(options.AnonAddyApiAccessToken) || string.IsNullOrWhiteSpace(options.AnonAddyDomainName))
|
|
||||||
{
|
|
||||||
return Constants.DefaultUsernameGenerated;
|
|
||||||
}
|
|
||||||
return await _apiService.GetUsernameFromAsync(ForwardedEmailServiceType.AnonAddy,
|
|
||||||
new UsernameGeneratorConfig()
|
|
||||||
{
|
|
||||||
ApiToken = options.AnonAddyApiAccessToken,
|
|
||||||
Domain = options.AnonAddyDomainName,
|
|
||||||
Url = "https://app.anonaddy.com/api/v1/aliases"
|
|
||||||
});
|
|
||||||
|
|
||||||
case ForwardedEmailServiceType.FirefoxRelay:
|
case ForwardedEmailServiceType.FirefoxRelay:
|
||||||
if (string.IsNullOrWhiteSpace(options.FirefoxRelayApiAccessToken))
|
simpleForwarder = new FirefoxRelayForwarder();
|
||||||
{
|
break;
|
||||||
return Constants.DefaultUsernameGenerated;
|
|
||||||
}
|
|
||||||
return await _apiService.GetUsernameFromAsync(ForwardedEmailServiceType.FirefoxRelay,
|
|
||||||
new UsernameGeneratorConfig()
|
|
||||||
{
|
|
||||||
ApiToken = options.FirefoxRelayApiAccessToken,
|
|
||||||
Url = "https://relay.firefox.com/api/v1/relayaddresses/"
|
|
||||||
});
|
|
||||||
|
|
||||||
case ForwardedEmailServiceType.SimpleLogin:
|
case ForwardedEmailServiceType.SimpleLogin:
|
||||||
if (string.IsNullOrWhiteSpace(options.SimpleLoginApiKey))
|
simpleForwarder = new SimpleLoginForwarder();
|
||||||
{
|
break;
|
||||||
return Constants.DefaultUsernameGenerated;
|
|
||||||
}
|
|
||||||
return await _apiService.GetUsernameFromAsync(ForwardedEmailServiceType.SimpleLogin,
|
|
||||||
new UsernameGeneratorConfig()
|
|
||||||
{
|
|
||||||
ApiToken = options.SimpleLoginApiKey,
|
|
||||||
Url = "https://app.simplelogin.io/api/alias/random/new"
|
|
||||||
});
|
|
||||||
case ForwardedEmailServiceType.DuckDuckGo:
|
case ForwardedEmailServiceType.DuckDuckGo:
|
||||||
if (string.IsNullOrWhiteSpace(options.DuckDuckGoApiKey))
|
simpleForwarder = new DuckDuckGoForwarder();
|
||||||
{
|
break;
|
||||||
return Constants.DefaultUsernameGenerated;
|
|
||||||
}
|
|
||||||
return await _apiService.GetUsernameFromAsync(ForwardedEmailServiceType.DuckDuckGo,
|
|
||||||
new UsernameGeneratorConfig()
|
|
||||||
{
|
|
||||||
ApiToken = options.DuckDuckGoApiKey,
|
|
||||||
Url = "https://quack.duckduckgo.com/api/email/addresses"
|
|
||||||
});
|
|
||||||
case ForwardedEmailServiceType.Fastmail:
|
case ForwardedEmailServiceType.Fastmail:
|
||||||
if (string.IsNullOrWhiteSpace(options.FastMailApiKey))
|
simpleForwarder = new FastmailForwarder();
|
||||||
{
|
break;
|
||||||
return Constants.DefaultUsernameGenerated;
|
|
||||||
}
|
|
||||||
|
|
||||||
return await _apiService.GetUsernameFromAsync(ForwardedEmailServiceType.Fastmail,
|
|
||||||
new UsernameGeneratorConfig()
|
|
||||||
{
|
|
||||||
ApiToken = options.FastMailApiKey,
|
|
||||||
Url = "https://api.fastmail.com/jmap/api/"
|
|
||||||
});
|
|
||||||
default:
|
default:
|
||||||
_logger.Value.Error($"Error UsernameGenerationService: ForwardedEmailServiceType {options.ServiceType} not implemented.");
|
_logger.Value.Error($"Error UsernameGenerationService: ForwardedEmailServiceType {options.ServiceType} not implemented.");
|
||||||
return Constants.DefaultUsernameGenerated;
|
return Constants.DefaultUsernameGenerated;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<string> RandomStringAsync(int length)
|
return await simpleForwarder.GenerateAsync(_apiService, options.GetForwarderOptions());
|
||||||
{
|
|
||||||
var str = "";
|
|
||||||
var charSet = "abcdefghijklmnopqrstuvwxyz1234567890";
|
|
||||||
|
|
||||||
for (var i = 0; i < length; i++)
|
|
||||||
{
|
|
||||||
var randomCharIndex = await _cryptoService.RandomNumberAsync(0, charSet.Length - 1);
|
|
||||||
str += charSet[randomCharIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
return str;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private string Capitalize(string str)
|
private string Capitalize(string str)
|
||||||
|
Loading…
Reference in New Issue
Block a user