Skip to content

Commit f81d164

Browse files
author
maxkpower
authored
Trim the mail notification doc comments
1 parent ef5bc2f commit f81d164

5 files changed

Lines changed: 21 additions & 52 deletions

File tree

bitwarden_license/src/Services/Pam/Services/ILeaseRevokedMailNotifier.cs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@
44
namespace Bit.Services.Pam.Services;
55

66
/// <summary>
7-
/// Emails a lease holder that an operator ended their active access — the out-of-band twin of the
8-
/// <c>RefreshAccessRequest</c> push that <see cref="IRequesterNotifier" /> sends on the same path. The push has
9-
/// already re-locked the item on any client that happened to be open; this tells the person mid-task why.
7+
/// Emails a lease holder that an operator ended their active access: the out-of-band twin of the
8+
/// <c>RefreshAccessRequest</c> push from <see cref="IRequesterNotifier" /> on the same path.
109
/// </summary>
1110
/// <remarks>
1211
/// A courtesy, not a security control. The lease is dead server-side before anything here runs, so nothing this
@@ -19,13 +18,9 @@ public interface ILeaseRevokedMailNotifier
1918
{
2019
/// <summary>
2120
/// Tells <paramref name="lease" />'s holder that their access ended, but only when
22-
/// <paramref name="endAction" /> is <see cref="AccessLeaseAction.Revoked" />.
21+
/// <paramref name="endAction" /> is <see cref="AccessLeaseAction.Revoked" />. The name is neutral because this
22+
/// is handed every early end, so the rule that a holder is never mailed about their own action lives here.
2323
/// </summary>
24-
/// <remarks>
25-
/// Handed every early end rather than only the revocations so the rule that a holder is never mailed about
26-
/// their own action lives in one place. Mailing someone "your access was revoked" seconds after they ended it
27-
/// themselves is the kind of notification that teaches people to ignore the channel.
28-
/// </remarks>
2924
/// <param name="lease">The lease just ended. Its <c>Action</c> may not be stamped yet at the call site.</param>
3025
/// <param name="endAction">
3126
/// How it ended, passed rather than read from <paramref name="lease" /> for that reason:

bitwarden_license/src/Services/Pam/Services/LeaseRevokedMailNotifier.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public async Task NotifyLeaseEndedAsync(AccessLease lease, AccessLeaseAction end
3838
}
3939

4040
// Duplicates the guard inside IAccessMailNotifier to keep the organization read off every revocation in the
41-
// flag-off state which is every revocation on self-host.
41+
// flag-off state, which is every revocation on self-host.
4242
if (!_featureService.IsEnabled(FeatureFlagKeys.Pam))
4343
{
4444
return;

bitwarden_license/test/Services/Pam.Test/Services/LeaseRevokedMailNotifierTests.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,6 @@ public async Task NotifyLeaseEndedAsync_Revoked_MailsTheHolderWithTheWindowItCut
4141
Assert.Equal($"{_vaultUrl}/pam/requests/{lease.AccessRequestId}", mail.View.Url);
4242
}
4343

44-
/// <summary>
45-
/// The point of the whole feature. A holder who ends their own access already knows, and a mail thirty seconds
46-
/// behind their own click is what teaches people to filter the channel.
47-
/// </summary>
4844
[Theory]
4945
[BitAutoData(AccessLeaseAction.Cancelled)]
5046
[BitAutoData(AccessLeaseAction.None)]

src/Core/Pam/Models/Mail/AccessLeaseRevoked/AccessLeaseRevokedView.cs

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,19 @@ namespace Bit.Core.Pam.Models.Mail.AccessLeaseRevoked;
77
/// The lease holder's notice that someone else revoked their active access before its window ran out.
88
/// </summary>
99
/// <remarks>
10-
/// This is a courtesy, not a control: the lease is already dead and the client has already re-locked by the time
11-
/// this is composed. It exists so someone mid-task learns why their item locked itself.
12-
///
13-
/// Sent only when an operator revoked the lease. A holder who ends their own access is not mailed about it, so
14-
/// there is no field here for who ended it — this view only ever describes the one case.
10+
/// A courtesy, not a control: the lease is already dead and the client has already re-locked by the time this is
11+
/// composed. It is sent only when an operator revoked the lease, so there is no field for who ended it.
1512
///
1613
/// Bounded by zero knowledge in the same way its siblings are: the collection and cipher are named only by
17-
/// ciphertext the server cannot read. The revocation reason is left out for the reason <c>AccessRequest.Reason</c>
18-
/// is — free text that would name the very system being accessed, rendered into an HTML mail body.
14+
/// ciphertext the server cannot read, and the revocation reason is withheld for the reason
15+
/// <c>AccessRequest.Reason</c> is, being free text that would name the very system being accessed.
1916
/// <see cref="Url" /> carries the holder to it.
2017
/// </remarks>
2118
public class AccessLeaseRevokedView : BaseMailView
2219
{
2320
/// <summary>
24-
/// UTC is spelled out in the rendered string. There is no per-recipient timezone on this path, so an
25-
/// unqualified instant would be read as local time and would misstate how much of the window was cut short.
21+
/// UTC is spelled out in the rendered string: there is no per-recipient timezone on this path, so an unqualified
22+
/// instant would be read as local time and misstate how much of the window was cut short.
2623
/// </summary>
2724
private const string _windowFormat = "d MMM yyyy 'at' HH:mm 'UTC'";
2825

@@ -31,24 +28,22 @@ public class AccessLeaseRevokedView : BaseMailView
3128
/// <summary>The request the ended lease was minted from, which is what <see cref="Url" /> addresses.</summary>
3229
public required Guid AccessRequestId { get; init; }
3330

34-
/// <summary>Plaintext, unlike the collection and cipher names this mail deliberately omits.</summary>
3531
public required string OrganizationName { get; init; }
3632

3733
/// <summary>
3834
/// When the lease would have ended on its own, in UTC. Always in the future at send time:
39-
/// <c>RevokeAccessLeaseCommand</c> refuses a lease whose window has already closed, so a revoke is always an
40-
/// early end and this is what it cut short.
35+
/// <c>RevokeAccessLeaseCommand</c> refuses a lease whose window has already closed.
4136
/// </summary>
4237
public required DateTime NotAfter { get; init; }
4338

4439
public string ScheduledEnd => NotAfter.ToString(_windowFormat, CultureInfo.InvariantCulture);
4540

4641
/// <summary>
47-
/// The holder's own view of the request this lease came from, where the revocation is recorded as a decision
48-
/// with whatever reason the operator gave. The user-scoped PAM pages mount at <c>pam</c>
49-
/// (<c>apps/web/src/app/oss-routing.module.ts:687</c>) and the request page is <c>requests/:id</c> beneath it
50-
/// (<c>access-requests-routing.module.ts:47</c>). The organization-scoped admin surface under
51-
/// <c>/organizations/:organizationId/pam</c> is a different route tree and does not serve this page.
42+
/// The request this lease came from, where the revocation is recorded with whatever reason the operator gave.
43+
/// The user-scoped PAM pages mount at <c>pam</c> (<c>apps/web/src/app/oss-routing.module.ts:687</c>) with the
44+
/// request page at <c>requests/:id</c> beneath it (<c>access-requests-routing.module.ts:47</c>). The
45+
/// organization-scoped tree under <c>/organizations/:organizationId/pam</c> is a different route tree and does
46+
/// not serve this page.
5247
/// </summary>
5348
public string Url => $"{WebVaultUrl}/pam/requests/{AccessRequestId}";
5449
}

test/Core.Test/Pam/Models/Mail/AccessLeaseRevoked/AccessLeaseRevokedViewTests.cs

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,7 @@ public class AccessLeaseRevokedViewTests
1212
{
1313
private static readonly Guid _requestId = Guid.Parse("6f1b2d84-0c37-4a91-8e55-1d7c93a4b208");
1414

15-
/// <summary>
16-
/// <see cref="HandlebarMailRenderer" /> resolves both templates from the view's full class name and only fails
17-
/// when a mail is actually sent, which no other spec in this feature reaches. This is the spec that catches a
18-
/// misnamed or misplaced <c>.hbs</c>.
19-
/// </summary>
15+
/// <summary>The only spec that renders, so the only one that catches a misnamed or misplaced <c>.hbs</c>.</summary>
2016
[Fact]
2117
public async Task RenderAsync_SaysAccessWasRevokedAndPointsAtTheRequest()
2218
{
@@ -32,10 +28,7 @@ public async Task RenderAsync_SaysAccessWasRevokedAndPointsAtTheRequest()
3228
}
3329
}
3430

35-
/// <summary>
36-
/// A revoked lease is over. Copy that implies otherwise sends someone back to a "Start access" button that
37-
/// will not help them, so the body has to say both that it cannot be resumed and what to do instead.
38-
/// </summary>
31+
/// <summary>A revoked lease is over; copy implying otherwise sends the holder to a button that cannot help.</summary>
3932
[Fact]
4033
public async Task RenderAsync_SaysTheAccessCannotBeResumedAndThatANewRequestIsNeeded()
4134
{
@@ -50,11 +43,7 @@ public async Task RenderAsync_SaysTheAccessCannotBeResumedAndThatANewRequestIsNe
5043
}
5144
}
5245

53-
/// <summary>
54-
/// The reason an operator gave is free text that may name the very system being accessed, so it is linked to
55-
/// rather than rendered — the same call the request's reason and the approver's comment get. Nothing on the
56-
/// view can carry it.
57-
/// </summary>
46+
/// <summary>The reason is free text that may name the system being accessed, so it is linked to, not rendered.</summary>
5847
[Fact]
5948
public void View_HasNoPlaceToCarryTheRevocationReason() =>
6049
Assert.DoesNotContain(
@@ -63,9 +52,6 @@ public void View_HasNoPlaceToCarryTheRevocationReason() =>
6352
|| property.Name.Contains("Comment", StringComparison.OrdinalIgnoreCase)
6453
|| property.Name.Contains("Detail", StringComparison.OrdinalIgnoreCase));
6554

66-
/// <summary>
67-
/// An organization name is member-supplied text, so the HTML body must escape it rather than interpolate it raw.
68-
/// </summary>
6955
[Fact]
7056
public async Task RenderAsync_EscapesTheOrganizationNameInTheHtmlBody()
7157
{
@@ -93,10 +79,7 @@ public void Subject_SaysTheAccessWasRevokedWithoutNamingTheItemOrTheReason() =>
9379
NotAfter = new DateTime(2026, 9, 1, 17, 0, 0, DateTimeKind.Utc),
9480
};
9581

96-
/// <summary>
97-
/// Both templates wrap their copy across source lines, so a sentence these specs assert on is not contiguous in
98-
/// the rendered output. Collapsing runs of whitespace keeps them about the wording rather than the line breaks.
99-
/// </summary>
82+
/// <summary>Both templates wrap copy across source lines; collapsing whitespace keeps the specs about wording.</summary>
10083
private static string Reflow(string body) => Regex.Replace(body, @"\s+", " ");
10184

10285
private static Task<(string html, string txt)> RenderAsync(BaseMailView view) =>

0 commit comments

Comments
 (0)