mirror of
https://github.com/bitwarden/server.git
synced 2025-04-06 13:38:13 -05:00
new device logged in email notification
This commit is contained in:
parent
43967ebbc1
commit
b19628c6f8
@ -11,6 +11,11 @@
|
|||||||
<EmbeddedResource Include="MailTemplates\Handlebars\**\*.hbs" />
|
<EmbeddedResource Include="MailTemplates\Handlebars\**\*.hbs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="MailTemplates\Handlebars\NewDeviceLoggedIn.html.hbs" />
|
||||||
|
<None Remove="MailTemplates\Handlebars\NewDeviceLoggedIn.text.hbs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Handlebars.Net" Version="1.9.5" />
|
<PackageReference Include="Handlebars.Net" Version="1.9.5" />
|
||||||
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="2.6.0" />
|
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="2.6.0" />
|
||||||
|
@ -1,27 +1,50 @@
|
|||||||
namespace Bit.Core.Enums
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Bit.Core.Enums
|
||||||
{
|
{
|
||||||
public enum DeviceType : byte
|
public enum DeviceType : byte
|
||||||
{
|
{
|
||||||
|
[Display(Name = "Android")]
|
||||||
Android = 0,
|
Android = 0,
|
||||||
|
[Display(Name = "iOS")]
|
||||||
iOS = 1,
|
iOS = 1,
|
||||||
|
[Display(Name = "Chrome Extension")]
|
||||||
ChromeExtension = 2,
|
ChromeExtension = 2,
|
||||||
|
[Display(Name = "Firefox Extension")]
|
||||||
FirefoxExtension = 3,
|
FirefoxExtension = 3,
|
||||||
|
[Display(Name = "Opera Extension")]
|
||||||
OperaExtension = 4,
|
OperaExtension = 4,
|
||||||
|
[Display(Name = "Edge Extension")]
|
||||||
EdgeExtension = 5,
|
EdgeExtension = 5,
|
||||||
|
[Display(Name = "Windows")]
|
||||||
WindowsDesktop = 6,
|
WindowsDesktop = 6,
|
||||||
|
[Display(Name = "macOS")]
|
||||||
MacOsDesktop = 7,
|
MacOsDesktop = 7,
|
||||||
|
[Display(Name = "Linux")]
|
||||||
LinuxDesktop = 8,
|
LinuxDesktop = 8,
|
||||||
|
[Display(Name = "Chrome")]
|
||||||
ChromeBrowser = 9,
|
ChromeBrowser = 9,
|
||||||
|
[Display(Name = "Firefox")]
|
||||||
FirefoxBrowser = 10,
|
FirefoxBrowser = 10,
|
||||||
|
[Display(Name = "Opera")]
|
||||||
OperaBrowser = 11,
|
OperaBrowser = 11,
|
||||||
|
[Display(Name = "Edge")]
|
||||||
EdgeBrowser = 12,
|
EdgeBrowser = 12,
|
||||||
|
[Display(Name = "Internet Explorer")]
|
||||||
IEBrowser = 13,
|
IEBrowser = 13,
|
||||||
|
[Display(Name = "Unknown Browser")]
|
||||||
UnknownBrowser = 14,
|
UnknownBrowser = 14,
|
||||||
|
[Display(Name = "Android")]
|
||||||
AndroidAmazon = 15,
|
AndroidAmazon = 15,
|
||||||
|
[Display(Name = "UWP")]
|
||||||
UWP = 16,
|
UWP = 16,
|
||||||
|
[Display(Name = "Safari")]
|
||||||
SafariBrowser = 17,
|
SafariBrowser = 17,
|
||||||
|
[Display(Name = "Vivaldi")]
|
||||||
VivaldiBrowser = 18,
|
VivaldiBrowser = 18,
|
||||||
|
[Display(Name = "Vivaldi Extension")]
|
||||||
VivaldiExtension = 19,
|
VivaldiExtension = 19,
|
||||||
|
[Display(Name = "Safari Extension")]
|
||||||
SafariExtension = 20
|
SafariExtension = 20
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,8 @@ using Bit.Core.Models;
|
|||||||
using Bit.Core.Identity;
|
using Bit.Core.Identity;
|
||||||
using Bit.Core.Models.Data;
|
using Bit.Core.Models.Data;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
namespace Bit.Core.IdentityServer
|
namespace Bit.Core.IdentityServer
|
||||||
{
|
{
|
||||||
@ -29,6 +31,7 @@ namespace Bit.Core.IdentityServer
|
|||||||
private readonly IOrganizationRepository _organizationRepository;
|
private readonly IOrganizationRepository _organizationRepository;
|
||||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||||
private readonly IApplicationCacheService _applicationCacheService;
|
private readonly IApplicationCacheService _applicationCacheService;
|
||||||
|
private readonly IMailService _mailService;
|
||||||
private readonly CurrentContext _currentContext;
|
private readonly CurrentContext _currentContext;
|
||||||
|
|
||||||
public ResourceOwnerPasswordValidator(
|
public ResourceOwnerPasswordValidator(
|
||||||
@ -41,6 +44,7 @@ namespace Bit.Core.IdentityServer
|
|||||||
IOrganizationRepository organizationRepository,
|
IOrganizationRepository organizationRepository,
|
||||||
IOrganizationUserRepository organizationUserRepository,
|
IOrganizationUserRepository organizationUserRepository,
|
||||||
IApplicationCacheService applicationCacheService,
|
IApplicationCacheService applicationCacheService,
|
||||||
|
IMailService mailService,
|
||||||
CurrentContext currentContext)
|
CurrentContext currentContext)
|
||||||
{
|
{
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
@ -52,6 +56,7 @@ namespace Bit.Core.IdentityServer
|
|||||||
_organizationRepository = organizationRepository;
|
_organizationRepository = organizationRepository;
|
||||||
_organizationUserRepository = organizationUserRepository;
|
_organizationUserRepository = organizationUserRepository;
|
||||||
_applicationCacheService = applicationCacheService;
|
_applicationCacheService = applicationCacheService;
|
||||||
|
_mailService = mailService;
|
||||||
_currentContext = currentContext;
|
_currentContext = currentContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -373,6 +378,16 @@ namespace Bit.Core.IdentityServer
|
|||||||
{
|
{
|
||||||
device.UserId = user.Id;
|
device.UserId = user.Id;
|
||||||
await _deviceService.SaveAsync(device);
|
await _deviceService.SaveAsync(device);
|
||||||
|
|
||||||
|
var now = DateTime.UtcNow;
|
||||||
|
if(now - user.CreationDate > TimeSpan.FromMinutes(10))
|
||||||
|
{
|
||||||
|
var deviceType = device.Type.GetType().GetMember(device.Type.ToString())
|
||||||
|
.FirstOrDefault()?.GetCustomAttribute<DisplayAttribute>()?.GetName();
|
||||||
|
await _mailService.SendNewDeviceLoggedInEmail(user.Email, deviceType, now,
|
||||||
|
_currentContext.IpAddress);
|
||||||
|
}
|
||||||
|
|
||||||
return device;
|
return device;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
21
src/Core/MailTemplates/Handlebars/NewDeviceLoggedIn.html.hbs
Normal file
21
src/Core/MailTemplates/Handlebars/NewDeviceLoggedIn.html.hbs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{{#>FullHtmlLayout}}
|
||||||
|
<table width="100%" cellpadding="0" cellspacing="0" style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
||||||
|
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
||||||
|
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none;" valign="top">
|
||||||
|
Your Bitwarden account was just logged into from a new device.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
||||||
|
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none;" valign="top">
|
||||||
|
<b style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">Date:</b> {{TheDate}} at {{TheTime}} {{TimeZone}}<br style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;" />
|
||||||
|
<b style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">IP Address:</b> {{IpAddress}}<br style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;" />
|
||||||
|
<b style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">Device Type:</b> {{DeviceType}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
||||||
|
<td class="content-block last" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0; -webkit-text-size-adjust: none;" valign="top">
|
||||||
|
You can deauthorize all devices that have access to your account from the <a target="_blank" clicktracking=off href="{{{WebVaultUrl}}}" style="-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; box-sizing: border-box; color: #3c8dbc; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 25px; margin: 0; text-decoration: underline;">web vault</a> under Settings → My Account → Deauthorize Sessions.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
{{/FullHtmlLayout}}
|
10
src/Core/MailTemplates/Handlebars/NewDeviceLoggedIn.text.hbs
Normal file
10
src/Core/MailTemplates/Handlebars/NewDeviceLoggedIn.text.hbs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{{#>BasicTextLayout}}
|
||||||
|
Your Bitwarden account was just logged into from a new device.
|
||||||
|
|
||||||
|
Date: {{TheDate}} at {{TheTime}} {{TimeZone}}
|
||||||
|
IP Address: {{IpAddress}}
|
||||||
|
Device Type: {{DeviceType}}
|
||||||
|
|
||||||
|
You can deauthorize all devices that have access to your account from the
|
||||||
|
web vault under Settings > My Account > Deauthorize Sessions.
|
||||||
|
{{/BasicTextLayout}}
|
11
src/Core/Models/Mail/NewDeviceLoggedInModel.cs
Normal file
11
src/Core/Models/Mail/NewDeviceLoggedInModel.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
namespace Bit.Core.Models.Mail
|
||||||
|
{
|
||||||
|
public class NewDeviceLoggedInModel : BaseMailModel
|
||||||
|
{
|
||||||
|
public string TheDate { get; set; }
|
||||||
|
public string TheTime { get; set; }
|
||||||
|
public string TimeZone { get; set; }
|
||||||
|
public string IpAddress { get; set; }
|
||||||
|
public string DeviceType { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -20,5 +20,6 @@ namespace Bit.Core.Services
|
|||||||
Task SendOrganizationConfirmedEmailAsync(string organizationName, string email);
|
Task SendOrganizationConfirmedEmailAsync(string organizationName, string email);
|
||||||
Task SendPasswordlessSignInAsync(string returnUrl, string token, string email);
|
Task SendPasswordlessSignInAsync(string returnUrl, string token, string email);
|
||||||
Task SendInvoiceUpcomingAsync(string email, decimal amount, DateTime dueDate, List<string> items, bool mentionInvoices);
|
Task SendInvoiceUpcomingAsync(string email, decimal amount, DateTime dueDate, List<string> items, bool mentionInvoices);
|
||||||
|
Task SendNewDeviceLoggedInEmail(string email, string deviceType, DateTime timestamp, string ip);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -236,6 +236,24 @@ namespace Bit.Core.Services
|
|||||||
await _mailDeliveryService.SendEmailAsync(message);
|
await _mailDeliveryService.SendEmailAsync(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task SendNewDeviceLoggedInEmail(string email, string deviceType, DateTime timestamp, string ip)
|
||||||
|
{
|
||||||
|
var message = CreateDefaultMessage($"New Device Logged In From {deviceType}", email);
|
||||||
|
var model = new NewDeviceLoggedInModel
|
||||||
|
{
|
||||||
|
WebVaultUrl = _globalSettings.BaseServiceUri.VaultWithHash,
|
||||||
|
SiteName = _globalSettings.SiteName,
|
||||||
|
DeviceType = deviceType,
|
||||||
|
TheDate = timestamp.ToLongDateString(),
|
||||||
|
TheTime = timestamp.ToShortTimeString(),
|
||||||
|
TimeZone = "UTC",
|
||||||
|
IpAddress = ip
|
||||||
|
};
|
||||||
|
await AddMessageContentAsync(message, "NewDeviceLoggedIn", model);
|
||||||
|
message.MetaData.Add("SendGridCategories", new List<string> { "NewDeviceLoggedIn" });
|
||||||
|
await _mailDeliveryService.SendEmailAsync(message);
|
||||||
|
}
|
||||||
|
|
||||||
private MailMessage CreateDefaultMessage(string subject, string toEmail)
|
private MailMessage CreateDefaultMessage(string subject, string toEmail)
|
||||||
{
|
{
|
||||||
return CreateDefaultMessage(subject, new List<string> { toEmail });
|
return CreateDefaultMessage(subject, new List<string> { toEmail });
|
||||||
|
@ -72,5 +72,10 @@ namespace Bit.Core.Services
|
|||||||
{
|
{
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task SendNewDeviceLoggedInEmail(string email, string deviceType, DateTime timestamp, string ip)
|
||||||
|
{
|
||||||
|
return Task.FromResult(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user