-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Expand file tree
/
Copy pathInitPendingOrganizationValidator.cs
More file actions
178 lines (152 loc) · 6.32 KB
/
InitPendingOrganizationValidator.cs
File metadata and controls
178 lines (152 loc) · 6.32 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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.OrganizationFeatures.Organizations;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements;
using Bit.Core.AdminConsole.Services;
using Bit.Core.AdminConsole.Utilities.v2.Validation;
using Bit.Core.Auth.Models.Business.Tokenables;
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
using Bit.Core.Billing.Enums;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Repositories;
using Bit.Core.Tokens;
using static Bit.Core.AdminConsole.Utilities.v2.Validation.ValidationResultHelpers;
using Error = Bit.Core.AdminConsole.Utilities.v2.Error;
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers;
public interface IInitPendingOrganizationValidator
{
/// <summary>
/// Validates all preconditions for initializing a pending organization.
/// </summary>
Task<ValidationResult<InitPendingOrganizationValidationRequest>> ValidateAsync(
InitPendingOrganizationValidationRequest request);
}
public class InitPendingOrganizationValidator : IInitPendingOrganizationValidator
{
private readonly IDataProtectorTokenFactory<OrgUserInviteTokenable> _orgUserInviteTokenDataFactory;
private readonly IPolicyService _policyService;
private readonly IPolicyRequirementQuery _policyRequirementQuery;
private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery;
private readonly IOrganizationUserRepository _organizationUserRepository;
public InitPendingOrganizationValidator(
IDataProtectorTokenFactory<OrgUserInviteTokenable> orgUserInviteTokenDataFactory,
IPolicyService policyService,
IPolicyRequirementQuery policyRequirementQuery,
ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery,
IOrganizationUserRepository organizationUserRepository)
{
_orgUserInviteTokenDataFactory = orgUserInviteTokenDataFactory;
_policyService = policyService;
_policyRequirementQuery = policyRequirementQuery;
_twoFactorIsEnabledQuery = twoFactorIsEnabledQuery;
_organizationUserRepository = organizationUserRepository;
}
public async Task<ValidationResult<InitPendingOrganizationValidationRequest>> ValidateAsync(
InitPendingOrganizationValidationRequest request)
{
if (!ValidateInviteToken(request.OrganizationUser, request.User, request.EmailToken))
{
return Invalid(request, new InvalidTokenError());
}
var emailError = ValidateUserEmail(request.OrganizationUser, request.User);
if (emailError != null)
{
return Invalid(request, emailError);
}
var matchError = ValidateOrganizationMatch(request.OrganizationUser, request.OrganizationId);
if (matchError != null)
{
return Invalid(request, matchError);
}
var stateError = ValidateOrganizationState(request.Organization);
if (stateError != null)
{
return Invalid(request, stateError);
}
var policyError = await ValidatePoliciesAsync(request.User, request.OrganizationId);
if (policyError != null)
{
return Invalid(request, policyError);
}
var limitError = await ValidateFreeOrganizationLimitAsync(
request.User, request.Organization, request.OrganizationUser);
if (limitError != null)
{
return Invalid(request, limitError);
}
return Valid(request);
}
private bool ValidateInviteToken(OrganizationUser orgUser, User user, string emailToken)
{
return OrgUserInviteTokenable.ValidateOrgUserInviteStringToken(
_orgUserInviteTokenDataFactory, emailToken, orgUser);
}
private static Error? ValidateUserEmail(OrganizationUser orgUser, User user)
{
if (string.IsNullOrWhiteSpace(orgUser.Email) ||
!orgUser.Email.Equals(user.Email, StringComparison.InvariantCultureIgnoreCase))
{
return new EmailMismatchError();
}
return null;
}
private static Error? ValidateOrganizationState(Organization org)
{
if (org.Enabled)
{
return new OrganizationAlreadyEnabledError();
}
if (org.Status != OrganizationStatusType.Pending)
{
return new OrganizationNotPendingError();
}
if (!string.IsNullOrEmpty(org.PublicKey) || !string.IsNullOrEmpty(org.PrivateKey))
{
return new OrganizationHasKeysError();
}
return null;
}
private static Error? ValidateOrganizationMatch(OrganizationUser orgUser, Guid organizationId)
{
if (orgUser.OrganizationId != organizationId)
{
return new OrganizationMismatchError();
}
return null;
}
private async Task<Error?> ValidatePoliciesAsync(User user, Guid organizationId)
{
var autoConfirmReq = await _policyRequirementQuery.GetAsync<AutomaticUserConfirmationPolicyRequirement>(user.Id);
if (autoConfirmReq.CannotCreateNewOrganization())
{
return new SingleOrgPolicyViolationError();
}
var anySingleOrgPolicies = await _policyService.AnyPoliciesApplicableToUserAsync(user.Id, PolicyType.SingleOrg);
if (anySingleOrgPolicies)
{
return new SingleOrgPolicyViolationError();
}
var twoFactorReq = await _policyRequirementQuery.GetAsync<RequireTwoFactorPolicyRequirement>(user.Id);
if (twoFactorReq.IsTwoFactorRequiredForOrganization(organizationId) &&
!await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user))
{
return new TwoFactorRequiredError();
}
return null;
}
private async Task<Error?> ValidateFreeOrganizationLimitAsync(User user, Organization org, OrganizationUser orgUser)
{
if (org.PlanType == PlanType.Free &&
(orgUser.Type == OrganizationUserType.Owner || orgUser.Type == OrganizationUserType.Admin))
{
var adminCount = await _organizationUserRepository.GetCountByFreeOrganizationAdminUserAsync(user.Id);
if (adminCount > 0)
{
return new FreeOrgAdminLimitError();
}
}
return null;
}
}