-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Expand file tree
/
Copy pathOrganizationUsersControllerBindOrganizationTests.cs
More file actions
144 lines (115 loc) · 5.62 KB
/
Copy pathOrganizationUsersControllerBindOrganizationTests.cs
File metadata and controls
144 lines (115 loc) · 5.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
using System.Net;
using Bit.Api.IntegrationTest.Factories;
using Bit.Api.IntegrationTest.Helpers;
using Bit.Core.Billing.Enums;
using Bit.Core.Enums;
using Bit.Core.Repositories;
using Xunit;
namespace Bit.Api.IntegrationTest.AdminConsole.Controllers;
/// <summary>
/// Integration tests for <see cref="Bit.Api.AdminConsole.Attributes.BindOrganizationAttribute"/>,
/// exercised through the GET reset-password-details endpoint which binds an Organization from the
/// <c>orgId</c> route parameter.
/// </summary>
public class OrganizationUsersControllerBindOrganizationTests : IClassFixture<ApiApplicationFactory>, IAsyncLifetime
{
private readonly ApiApplicationFactory _factory;
private readonly HttpClient _client;
private readonly LoginHelper _loginHelper;
private string _ownerEmail = null!;
public OrganizationUsersControllerBindOrganizationTests(ApiApplicationFactory factory)
{
_factory = factory;
_client = _factory.CreateClient();
_loginHelper = new LoginHelper(_factory, _client);
}
public async Task InitializeAsync()
{
_ownerEmail = $"bind-org-test-{Guid.NewGuid()}@example.com";
await _factory.LoginWithNewAccount(_ownerEmail);
}
public Task DisposeAsync()
{
_client.Dispose();
return Task.CompletedTask;
}
[Fact]
public async Task GetResetPasswordDetails_HappyPath_ReturnsOk()
{
// Arrange
var (organization, _) = await OrganizationTestHelpers.SignUpAsync(_factory,
plan: PlanType.EnterpriseAnnually,
ownerEmail: _ownerEmail,
passwordManagerSeats: 10,
paymentMethod: PaymentMethodType.Card);
var organizationRepository = _factory.GetService<IOrganizationRepository>();
organization.UseResetPassword = true;
await organizationRepository.ReplaceAsync(organization);
await _loginHelper.LoginAsync(_ownerEmail);
var (_, memberOrgUser) = await OrganizationTestHelpers.CreateNewUserWithAccountAsync(
_factory, organization.Id, OrganizationUserType.User);
var orgUserRepository = _factory.GetService<IOrganizationUserRepository>();
memberOrgUser.ResetPasswordKey = "encrypted-reset-password-key";
await orgUserRepository.ReplaceAsync(memberOrgUser);
// Act
var response = await _client.GetAsync(
$"organizations/{organization.Id}/users/{memberOrgUser.Id}/reset-password-details");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
await organizationRepository.DeleteAsync(organization);
}
[Fact]
public async Task GetResetPasswordDetails_OrgUserNotFound_ReturnsNotFound()
{
// Arrange — org exists and auth passes, but the org user ID in the path does not exist.
// BindOrganizationAttribute successfully binds the org; the endpoint then throws
// NotFoundException because the repository returns null for the unknown org user ID.
var (organization, _) = await OrganizationTestHelpers.SignUpAsync(_factory,
plan: PlanType.EnterpriseAnnually,
ownerEmail: _ownerEmail,
passwordManagerSeats: 10,
paymentMethod: PaymentMethodType.Card);
var organizationRepository = _factory.GetService<IOrganizationRepository>();
organization.UseResetPassword = true;
await organizationRepository.ReplaceAsync(organization);
await _loginHelper.LoginAsync(_ownerEmail);
// Act — use a random Guid that has no matching OrganizationUser row
var response = await _client.GetAsync(
$"organizations/{organization.Id}/users/{Guid.NewGuid()}/reset-password-details");
// Assert
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
await organizationRepository.DeleteAsync(organization);
}
[Fact]
public async Task GetResetPasswordDetails_OrgUserBelongsToDifferentOrg_ReturnsNotFound()
{
// Arrange — create two separate organizations
var (org1, _) = await OrganizationTestHelpers.SignUpAsync(_factory,
plan: PlanType.EnterpriseAnnually,
ownerEmail: _ownerEmail,
passwordManagerSeats: 10,
paymentMethod: PaymentMethodType.Card);
var secondOwnerEmail = $"bind-org-test-owner2-{Guid.NewGuid()}@example.com";
await _factory.LoginWithNewAccount(secondOwnerEmail);
var (org2, _) = await OrganizationTestHelpers.SignUpAsync(_factory,
plan: PlanType.EnterpriseAnnually,
ownerEmail: secondOwnerEmail,
passwordManagerSeats: 10,
paymentMethod: PaymentMethodType.Card);
var organizationRepository = _factory.GetService<IOrganizationRepository>();
org1.UseResetPassword = true;
await organizationRepository.ReplaceAsync(org1);
// Create a user in org2
var (_, org2MemberOrgUser) = await OrganizationTestHelpers.CreateNewUserWithAccountAsync(
_factory, org2.Id, OrganizationUserType.User);
// Log in as owner of org1 (who has ManageAccountRecovery for org1)
await _loginHelper.LoginAsync(_ownerEmail);
// Act — request org1's endpoint but pass an org user ID that belongs to org2
var response = await _client.GetAsync(
$"organizations/{org1.Id}/users/{org2MemberOrgUser.Id}/reset-password-details");
// Assert — the org user's OrganizationId does not match org1, so NotFoundException is thrown
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
await organizationRepository.DeleteAsync(org1);
await organizationRepository.DeleteAsync(org2);
}
}