mirror of
https://github.com/bitwarden/server.git
synced 2025-02-12 01:11:22 +01:00
Add Expiration Date To Organization Invite Emails (#1466)
* Added an expiration date to the organization user invite email * Added a period * moved property assignment around * fixed date offset
This commit is contained in:
parent
5ec37b96b4
commit
745068686b
@ -2,7 +2,7 @@
|
||||
<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; text-align: center;" valign="top" align="center">
|
||||
You have been invited to join the <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;">{{OrganizationName}}</b> organization.
|
||||
You have been invited to join the <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;">{{OrganizationName}}</b> organization. This link expires on <b>{{ExpirationDate}}.</b>
|
||||
</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;">
|
||||
|
@ -3,5 +3,7 @@ You have been invited to join the {{OrganizationName}} organization. To accept t
|
||||
|
||||
{{{Url}}}
|
||||
|
||||
This link expires on {{ExpirationDate}}.
|
||||
|
||||
If you do not wish to join this organization, you can safely ignore this email.
|
||||
{{/BasicTextLayout}}
|
||||
{{/BasicTextLayout}}
|
||||
|
16
src/Core/Models/Business/ExpiringToken.cs
Normal file
16
src/Core/Models/Business/ExpiringToken.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using System;
|
||||
|
||||
namespace Bit.Core.Models.Business
|
||||
{
|
||||
public class ExpiringToken
|
||||
{
|
||||
public readonly string Token;
|
||||
public readonly DateTime ExpirationDate;
|
||||
|
||||
public ExpiringToken(string token, DateTime expirationDate)
|
||||
{
|
||||
Token = token;
|
||||
ExpirationDate = expirationDate;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
namespace Bit.Core.Models.Mail
|
||||
using System;
|
||||
|
||||
namespace Bit.Core.Models.Mail
|
||||
{
|
||||
public class OrganizationUserInvitedViewModel : BaseMailModel
|
||||
{
|
||||
@ -8,6 +10,7 @@
|
||||
public string Email { get; set; }
|
||||
public string OrganizationNameUrlEncoded { get; set; }
|
||||
public string Token { get; set; }
|
||||
public string ExpirationDate { get; set; }
|
||||
public string Url => string.Format("{0}/accept-organization?organizationId={1}&" +
|
||||
"organizationUserId={2}&email={3}&organizationName={4}&token={5}",
|
||||
WebVaultUrl,
|
||||
|
@ -4,6 +4,7 @@ using System.Collections.Generic;
|
||||
using System;
|
||||
using Bit.Core.Models.Mail;
|
||||
using Bit.Core.Models.Table.Provider;
|
||||
using Bit.Core.Models.Business;
|
||||
|
||||
namespace Bit.Core.Services
|
||||
{
|
||||
@ -17,10 +18,9 @@ namespace Bit.Core.Services
|
||||
Task SendTwoFactorEmailAsync(string email, string token);
|
||||
Task SendNoMasterPasswordHintEmailAsync(string email);
|
||||
Task SendMasterPasswordHintEmailAsync(string email, string hint);
|
||||
Task SendOrganizationInviteEmailAsync(string organizationName, OrganizationUser orgUser, string token);
|
||||
Task BulkSendOrganizationInviteEmailAsync(string organizationName, IEnumerable<(OrganizationUser orgUser, string token)> invites);
|
||||
Task SendOrganizationAcceptedEmailAsync(Organization organization, string userIdentifier,
|
||||
IEnumerable<string> adminEmails);
|
||||
Task SendOrganizationInviteEmailAsync(string organizationName, OrganizationUser orgUser, ExpiringToken token);
|
||||
Task BulkSendOrganizationInviteEmailAsync(string organizationName, IEnumerable<(OrganizationUser orgUser, ExpiringToken token)> invites);
|
||||
Task SendOrganizationAcceptedEmailAsync(Organization organization, string userIdentifier, IEnumerable<string> adminEmails);
|
||||
Task SendOrganizationConfirmedEmailAsync(string organizationName, string email);
|
||||
Task SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(string organizationName, string email);
|
||||
Task SendPasswordlessSignInAsync(string returnUrl, string token, string email);
|
||||
|
@ -12,6 +12,7 @@ using System.Reflection;
|
||||
using Bit.Core.Models.Mail.Provider;
|
||||
using Bit.Core.Models.Table.Provider;
|
||||
using HandlebarsDotNet;
|
||||
using Bit.Core.Models.Business;
|
||||
|
||||
namespace Bit.Core.Services
|
||||
{
|
||||
@ -174,10 +175,10 @@ namespace Bit.Core.Services
|
||||
await _mailDeliveryService.SendEmailAsync(message);
|
||||
}
|
||||
|
||||
public Task SendOrganizationInviteEmailAsync(string organizationName, OrganizationUser orgUser, string token) =>
|
||||
public Task SendOrganizationInviteEmailAsync(string organizationName, OrganizationUser orgUser, ExpiringToken token) =>
|
||||
BulkSendOrganizationInviteEmailAsync(organizationName, new[] { (orgUser, token) });
|
||||
|
||||
public async Task BulkSendOrganizationInviteEmailAsync(string organizationName, IEnumerable<(OrganizationUser orgUser, string token)> invites)
|
||||
public async Task BulkSendOrganizationInviteEmailAsync(string organizationName, IEnumerable<(OrganizationUser orgUser, ExpiringToken token)> invites)
|
||||
{
|
||||
MailQueueMessage CreateMessage(string email, object model)
|
||||
{
|
||||
@ -192,7 +193,8 @@ namespace Bit.Core.Services
|
||||
Email = WebUtility.UrlEncode(invite.orgUser.Email),
|
||||
OrganizationId = invite.orgUser.OrganizationId.ToString(),
|
||||
OrganizationUserId = invite.orgUser.Id.ToString(),
|
||||
Token = WebUtility.UrlEncode(invite.token),
|
||||
Token = WebUtility.UrlEncode(invite.token.Token),
|
||||
ExpirationDate = $"{invite.token.ExpirationDate.ToLongDateString()} {invite.token.ExpirationDate.ToShortTimeString()} UTC",
|
||||
OrganizationNameUrlEncoded = WebUtility.UrlEncode(organizationName),
|
||||
WebVaultUrl = _globalSettings.BaseServiceUri.VaultWithHash,
|
||||
SiteName = _globalSettings.SiteName,
|
||||
|
@ -1289,15 +1289,16 @@ namespace Bit.Core.Services
|
||||
string MakeToken(OrganizationUser orgUser) =>
|
||||
_dataProtector.Protect($"OrganizationUserInvite {orgUser.Id} {orgUser.Email} {CoreHelpers.ToEpocMilliseconds(DateTime.UtcNow)}");
|
||||
await _mailService.BulkSendOrganizationInviteEmailAsync(organization.Name,
|
||||
orgUsers.Select(o => (o, MakeToken(o))));
|
||||
orgUsers.Select(o => (o, new ExpiringToken(MakeToken(o), DateTime.UtcNow.AddDays(5)))));
|
||||
}
|
||||
|
||||
private async Task SendInviteAsync(OrganizationUser orgUser, Organization organization)
|
||||
{
|
||||
var nowMillis = CoreHelpers.ToEpocMilliseconds(DateTime.UtcNow);
|
||||
var now = DateTime.UtcNow;
|
||||
var nowMillis = CoreHelpers.ToEpocMilliseconds(now);
|
||||
var token = _dataProtector.Protect(
|
||||
$"OrganizationUserInvite {orgUser.Id} {orgUser.Email} {nowMillis}");
|
||||
await _mailService.SendOrganizationInviteEmailAsync(organization.Name, orgUser, token);
|
||||
await _mailService.SendOrganizationInviteEmailAsync(organization.Name, orgUser, new ExpiringToken(token, now.AddDays(5)));
|
||||
}
|
||||
|
||||
public async Task<OrganizationUser> AcceptUserAsync(Guid organizationUserId, User user, string token,
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Models.Business;
|
||||
using Bit.Core.Models.Mail;
|
||||
using Bit.Core.Models.Table;
|
||||
using Bit.Core.Models.Table.Provider;
|
||||
@ -44,12 +45,12 @@ namespace Bit.Core.Services
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task SendOrganizationInviteEmailAsync(string organizationName, OrganizationUser orgUser, string token)
|
||||
public Task SendOrganizationInviteEmailAsync(string organizationName, OrganizationUser orgUser, ExpiringToken token)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task BulkSendOrganizationInviteEmailAsync(string organizationName, IEnumerable<(OrganizationUser orgUser, string token)> invites)
|
||||
public Task BulkSendOrganizationInviteEmailAsync(string organizationName, IEnumerable<(OrganizationUser orgUser, ExpiringToken token)> invites)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ namespace Bit.Core.Test.Services
|
||||
.CreateManyAsync(Arg.Is<IEnumerable<OrganizationUser>>(users => users.Count() == expectedNewUsersCount));
|
||||
await sutProvider.GetDependency<IMailService>().Received(1)
|
||||
.BulkSendOrganizationInviteEmailAsync(org.Name,
|
||||
Arg.Is<IEnumerable<(OrganizationUser, string)>>(messages => messages.Count() == expectedNewUsersCount));
|
||||
Arg.Is<IEnumerable<(OrganizationUser, ExpiringToken)>>(messages => messages.Count() == expectedNewUsersCount));
|
||||
|
||||
// Send events
|
||||
await sutProvider.GetDependency<IEventService>().Received(1)
|
||||
@ -116,7 +116,7 @@ namespace Bit.Core.Test.Services
|
||||
.CreateManyAsync(Arg.Is<IEnumerable<OrganizationUser>>(users => users.Count() == expectedNewUsersCount));
|
||||
await sutProvider.GetDependency<IMailService>().Received(1)
|
||||
.BulkSendOrganizationInviteEmailAsync(org.Name,
|
||||
Arg.Is<IEnumerable<(OrganizationUser, string)>>(messages => messages.Count() == expectedNewUsersCount));
|
||||
Arg.Is<IEnumerable<(OrganizationUser, ExpiringToken)>>(messages => messages.Count() == expectedNewUsersCount));
|
||||
|
||||
// Sent events
|
||||
await sutProvider.GetDependency<IEventService>().Received(1)
|
||||
|
Loading…
Reference in New Issue
Block a user