Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PM-10311] Account Management: Create helper methods for checking against verified domains #4636

Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
113851b
Add HasVerifiedDomainsAsync method to IOrganizationDomainService
r-tome Aug 13, 2024
5b7e189
Add GetManagedUserIdsByOrganizationIdAsync method to IOrganizationUse…
r-tome Aug 13, 2024
594e5f3
Fix case on the sproc OrganizationUser_ReadManagedIdsByOrganizationId…
r-tome Aug 13, 2024
0e9e532
Update the EF query to use the Email from the User table
r-tome Aug 13, 2024
7bdcea7
Merge branch 'main' into ac/pm-10311/create-helper-methods-for-checki…
r-tome Aug 15, 2024
f34e643
dotnet format
r-tome Aug 15, 2024
6788d65
Fix IOrganizationDomainService.HasVerifiedDomainsAsync by checking th…
r-tome Aug 15, 2024
c3fd0a0
Rename IOrganizationUserRepository.GetManagedUserIdsByOrganizationAsync
r-tome Aug 15, 2024
f3924ad
Fix domain queries
r-tome Aug 15, 2024
ee575de
Add OrganizationUserRepository integration tests
r-tome Aug 15, 2024
67cd929
Add summary to IOrganizationDomainService.HasVerifiedDomainsAsync
r-tome Aug 16, 2024
4a24b3e
chore: Rename IOrganizationUserRepository.GetManagedUserIdsByOrganiza…
r-tome Aug 16, 2024
6cd25de
Add IsManagedByAnyOrganizationAsync method to IUserRepository
r-tome Aug 16, 2024
a9dbc31
Add integration tests for UserRepository.IsManagedByAnyOrganizationAsync
r-tome Aug 16, 2024
445f7e4
Merge branch 'main' into ac/pm-10311/create-helper-methods-for-checki…
r-tome Aug 16, 2024
4d4ec8e
Refactor to IUserService.IsManagedByAnyOrganizationAsync and IOrganiz…
r-tome Aug 17, 2024
9092295
Merge branch 'ac/pm-10311/create-helper-methods-for-checking-against-…
r-tome Aug 17, 2024
ca2e727
chore: Refactor IsManagedByAnyOrganizationAsync method in UserService
r-tome Aug 17, 2024
b6672d1
Refactor IOrganizationService.GetUsersOrganizationManagementStatusAsy…
r-tome Aug 19, 2024
6785c50
Merge branch 'main' into ac/pm-10311/create-helper-methods-for-checki…
r-tome Aug 20, 2024
ba52318
Merge branch 'main' into ac/pm-10311/create-helper-methods-for-checki…
r-tome Aug 26, 2024
2a94bdf
Extract IOrganizationService.GetUsersOrganizationManagementStatusAsyn…
r-tome Aug 26, 2024
ad03931
Update comments in OrganizationDomainService to use proper capitaliza…
r-tome Aug 26, 2024
f318511
Move OrganizationDomainService to AdminConsole ownership and update n…
r-tome Aug 26, 2024
f6cecf0
Merge branch 'main' into ac/pm-10311/create-helper-methods-for-checki…
r-tome Aug 27, 2024
7ffd16d
feat: Add support for organization domains in enterprise plans
r-tome Aug 27, 2024
fdedcf9
feat: Add HasOrganizationDomains property to OrganizationAbility class
r-tome Aug 27, 2024
399bf01
refactor: Update GetOrganizationUsersManagementStatusQuery to use IAp…
r-tome Aug 27, 2024
8beea6b
Merge branch 'main' into ac/pm-10311/create-helper-methods-for-checki…
r-tome Aug 28, 2024
e7c7d99
Remove HasOrganizationDomains and use UseSso to check if Organization…
r-tome Aug 28, 2024
a69b2e3
Merge branch 'main' into ac/pm-10311/create-helper-methods-for-checki…
r-tome Aug 29, 2024
e612929
Refactor UserService.IsManagedByAnyOrganizationAsync to simply check …
r-tome Aug 29, 2024
e088574
Merge branch 'main' into ac/pm-10311/create-helper-methods-for-checki…
r-tome Sep 4, 2024
083fe91
Add TODO comment for replacing 'UseSso' organization ability on user …
r-tome Sep 4, 2024
d5ef49c
Bump date on migration script
r-tome Sep 4, 2024
274390c
Merge branch 'main' into ac/pm-10311/create-helper-methods-for-checki…
r-tome Sep 5, 2024
530c43e
Add indexes to OrganizationDomain table
r-tome Sep 5, 2024
c040638
Merge branch 'main' into ac/pm-10311/create-helper-methods-for-checki…
r-tome Sep 10, 2024
6254665
Bump script migration date; Remove WITH ONLINE = ON from data migration.
r-tome Sep 10, 2024
a543efd
Merge branch 'main' into ac/pm-10311/create-helper-methods-for-checki…
r-tome Sep 10, 2024
975e70a
Merge branch 'main' into ac/pm-10311/create-helper-methods-for-checki…
r-tome Sep 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Admin/Jobs/DeleteUnverifiedOrganizationDomainsJob.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using Bit.Core;
using Bit.Core.AdminConsole.Services;
using Bit.Core.Jobs;
using Bit.Core.Services;
using Quartz;

namespace Bit.Admin.Jobs;
Expand Down
2 changes: 1 addition & 1 deletion src/Api/Jobs/ValidateOrganizationDomainJob.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using Bit.Core;
using Bit.Core.AdminConsole.Services;
using Bit.Core.Jobs;
using Bit.Core.Services;
using Quartz;

namespace Bit.Api.Jobs;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
using Bit.Core.Repositories;
using Bit.Core.Services;

namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers;

public class GetOrganizationUsersManagementStatusQuery : IGetOrganizationUsersManagementStatusQuery
{
private readonly IApplicationCacheService _applicationCacheService;
private readonly IOrganizationUserRepository _organizationUserRepository;

public GetOrganizationUsersManagementStatusQuery(
IApplicationCacheService applicationCacheService,
IOrganizationUserRepository organizationUserRepository)
{
_applicationCacheService = applicationCacheService;
_organizationUserRepository = organizationUserRepository;
}

public async Task<IDictionary<Guid, bool>> GetUsersOrganizationManagementStatusAsync(Guid organizationId, IEnumerable<Guid> organizationUserIds)
{
if (organizationUserIds.Any())
{
// Users can only be managed by an Organization that is enabled and can have organization domains
var organizationAbility = await _applicationCacheService.GetOrganizationAbilityAsync(organizationId);

// TODO: Replace "UseSso" with a new organization ability like "UseOrganizationDomains" (PM-11622).
// Verified domains were tied to SSO, so we currently check the "UseSso" organization ability.
if (organizationAbility is { Enabled: true, UseSso: true })
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we've identified that UseSso is not very apt here and are going to improve this as tech debt: lets add a comment here indicating that UseSso is used to signify that the organization has access to domain verification. Bonus points if it includes a link to a Jira ticket.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I said I'd follow this up, so I've created a ticket for it here that you can link to: https://bitwarden.atlassian.net/browse/PM-11622

{
// Get all organization users with claimed domains by the organization
var organizationUsersWithClaimedDomain = await _organizationUserRepository.GetManyByOrganizationWithClaimedDomainsAsync(organizationId);

// Create a dictionary with the OrganizationUserId and a boolean indicating if the user is managed by the organization
return organizationUserIds.ToDictionary(ouId => ouId, ouId => organizationUsersWithClaimedDomain.Any(ou => ou.Id == ouId));
}
}

return organizationUserIds.ToDictionary(ouId => ouId, _ => false);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;

public interface IGetOrganizationUsersManagementStatusQuery
{
/// <summary>
/// Checks whether each user in the provided list of organization user IDs is managed by the specified organization.
/// </summary>
/// <param name="organizationId">The unique identifier of the organization to check against.</param>
/// <param name="organizationUserIds">A list of OrganizationUserIds to be checked.</param>
/// <remarks>
/// A managed user is a user whose email domain matches one of the Organization's verified domains.
/// The organization must be enabled and be on an Enterprise plan.
/// </remarks>
/// <returns>
/// A dictionary containing the OrganizationUserId and a boolean indicating if the user is managed by the organization.
/// </returns>
Task<IDictionary<Guid, bool>> GetUsersOrganizationManagementStatusAsync(Guid organizationId,
IEnumerable<Guid> organizationUserIds);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,9 @@ public interface IOrganizationRepository : IRepository<Organization, Guid>
Task<SelfHostedOrganizationDetails?> GetSelfHostedOrganizationDetailsById(Guid id);
Task<ICollection<Organization>> SearchUnassignedToProviderAsync(string name, string ownerEmail, int skip, int take);
Task<IEnumerable<string>> GetOwnerEmailAddressesById(Guid organizationId);

/// <summary>
/// Gets the organization that has a claimed domain matching the user's email domain.
/// </summary>
Task<Organization> GetByClaimedUserDomainAsync(Guid userId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,8 @@ Task<ICollection<OrganizationUserOrganizationDetails>> GetManyDetailsByUserAsync
UpdateEncryptedDataForKeyRotation UpdateForKeyRotation(Guid userId,
IEnumerable<OrganizationUser> resetPasswordKeys);

/// <summary>
/// Returns a list of OrganizationUsers with email domains that match one of the Organization's claimed domains.
/// </summary>
Task<ICollection<OrganizationUser>> GetManyByOrganizationWithClaimedDomainsAsync(Guid organizationId);
}
11 changes: 11 additions & 0 deletions src/Core/AdminConsole/Services/IOrganizationDomainService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Bit.Core.AdminConsole.Services;

public interface IOrganizationDomainService
{
Task ValidateOrganizationsDomainAsync();
Task OrganizationDomainMaintenanceAsync();
/// <summary>
/// Indicates if the organization has any verified domains.
/// </summary>
Task<bool> HasVerifiedDomainsAsync(Guid orgId);
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
using Bit.Core.Enums;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Settings;
using Microsoft.Extensions.Logging;

namespace Bit.Core.Services;
namespace Bit.Core.AdminConsole.Services.Implementations;

public class OrganizationDomainService : IOrganizationDomainService
{
Expand Down Expand Up @@ -53,7 +54,7 @@ public async Task ValidateOrganizationsDomainAsync()
{
_logger.LogInformation(Constants.BypassFiltersEventId, "Successfully validated domain");

//update entry on OrganizationDomain table
// Update entry on OrganizationDomain table
domain.SetLastCheckedDate();
domain.SetVerifiedDate();
domain.SetJobRunCount();
Expand All @@ -64,7 +65,7 @@ await _eventService.LogOrganizationDomainEventAsync(domain, EventType.Organizati
}
else
{
//update entry on OrganizationDomain table
// Update entry on OrganizationDomain table
domain.SetLastCheckedDate();
domain.SetJobRunCount();
domain.SetNextRunDate(_globalSettings.DomainVerification.VerificationInterval);
Expand All @@ -78,7 +79,7 @@ await _eventService.LogOrganizationDomainEventAsync(domain, EventType.Organizati
}
catch (Exception ex)
{
//update entry on OrganizationDomain table
// Update entry on OrganizationDomain table
domain.SetLastCheckedDate();
domain.SetJobRunCount();
domain.SetNextRunDate(_globalSettings.DomainVerification.VerificationInterval);
Expand Down Expand Up @@ -117,7 +118,7 @@ await _mailService.SendUnverifiedOrganizationDomainEmailAsync(adminEmails,

_logger.LogInformation(Constants.BypassFiltersEventId, "Expired domain: {domainName}", domain.DomainName);
}
//delete domains that have not been verified within 7 days
// Delete domains that have not been verified within 7 days
var status = await _domainRepository.DeleteExpiredAsync(_globalSettings.DomainVerification.ExpirationPeriod);
_logger.LogInformation(Constants.BypassFiltersEventId, "Delete status {status}", status);
}
Expand All @@ -127,6 +128,12 @@ await _mailService.SendUnverifiedOrganizationDomainEmailAsync(adminEmails,
}
}

public async Task<bool> HasVerifiedDomainsAsync(Guid orgId)
{
var orgDomains = await _domainRepository.GetDomainsByOrganizationIdAsync(orgId);
return orgDomains.Any(od => od.VerifiedDate != null);
}

private async Task<List<string>> GetAdminEmailsAsync(Guid organizationId)
{
var orgUsers = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(organizationId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ private static void AddOrganizationUserCommandsQueries(this IServiceCollection s
{
services.AddScoped<ICountNewSmSeatsRequiredQuery, CountNewSmSeatsRequiredQuery>();
services.AddScoped<IAcceptOrgUserCommand, AcceptOrgUserCommand>();
services.AddScoped<IGetOrganizationUsersManagementStatusQuery, GetOrganizationUsersManagementStatusQuery>();
}

// TODO: move to OrganizationSubscriptionServiceCollectionExtensions when OrganizationUser methods are moved out of
Expand Down
7 changes: 0 additions & 7 deletions src/Core/Services/IOrganizationDomainService.cs

This file was deleted.

9 changes: 9 additions & 0 deletions src/Core/Services/IUserService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,13 @@ Task<IdentityResult> UpdatePasswordHash(User user, string newPassword,
/// We force these users to the web to migrate their encryption scheme.
/// </summary>
Task<bool> IsLegacyUser(string userId);

/// <summary>
/// Indicates if the user is managed by any organization.
/// </summary>
/// <remarks>
/// A managed user is a user whose email domain matches one of the Organization's verified domains.
/// The organization must be enabled and be on an Enterprise plan.
/// </remarks>
Task<bool> IsManagedByAnyOrganizationAsync(Guid userId);
}
10 changes: 10 additions & 0 deletions src/Core/Services/Implementations/UserService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@
return null;
}

_currentContext.User = await _userRepository.GetByIdAsync(userIdGuid);

Check warning on line 157 in src/Core/Services/Implementations/UserService.cs

View workflow job for this annotation

GitHub Actions / Quality scan

'_currentContext' is null on at least one execution path. (https://rules.sonarsource.com/csharp/RSPEC-2259)
return _currentContext.User;
}

Expand All @@ -165,7 +165,7 @@
return _currentContext.User;
}

_currentContext.User = await _userRepository.GetByIdAsync(userId);

Check warning on line 168 in src/Core/Services/Implementations/UserService.cs

View workflow job for this annotation

GitHub Actions / Quality scan

'_currentContext' is null on at least one execution path. (https://rules.sonarsource.com/csharp/RSPEC-2259)
return _currentContext.User;
}

Expand Down Expand Up @@ -935,7 +935,7 @@
}
catch when (!_globalSettings.SelfHosted)
{
await paymentService.CancelAndRecoverChargesAsync(user);

Check warning on line 938 in src/Core/Services/Implementations/UserService.cs

View workflow job for this annotation

GitHub Actions / Quality scan

'paymentService' is null on at least one execution path. (https://rules.sonarsource.com/csharp/RSPEC-2259)
throw;
}
return new Tuple<bool, string>(string.IsNullOrWhiteSpace(paymentIntentClientSecret),
Expand Down Expand Up @@ -1244,6 +1244,16 @@
return IsLegacyUser(user);
}

public async Task<bool> IsManagedByAnyOrganizationAsync(Guid userId)
{
// Users can only be managed by an Organization that is enabled and can have organization domains
var organization = await _organizationRepository.GetByClaimedUserDomainAsync(userId);

// TODO: Replace "UseSso" with a new organization ability like "UseOrganizationDomains" (PM-11622).
// Verified domains were tied to SSO, so we currently check the "UseSso" organization ability.
return organization is { Enabled: true, UseSso: true };
}

/// <inheritdoc cref="IsLegacyUser(string)"/>
public static bool IsLegacyUser(User user)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,4 +167,17 @@
new { OrganizationId = organizationId },
commandType: CommandType.StoredProcedure);
}

public async Task<Organization> GetByClaimedUserDomainAsync(Guid userId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var result = await connection.QueryAsync<Organization>(
"[dbo].[Organization_ReadByClaimedUserEmailDomain]",
new { UserId = userId },
commandType: CommandType.StoredProcedure);

Check warning on line 178 in src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationRepository.cs

View check run for this annotation

Codecov / codecov/patch

src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationRepository.cs#L172-L178

Added lines #L172 - L178 were not covered by tests

return result.SingleOrDefault();

Check warning on line 180 in src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationRepository.cs

View workflow job for this annotation

GitHub Actions / Run tests

Possible null reference return.

Check warning on line 180 in src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationRepository.cs

View workflow job for this annotation

GitHub Actions / Quality scan

Possible null reference return.

Check warning on line 180 in src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationRepository.cs

View workflow job for this annotation

GitHub Actions / Run tests

Possible null reference return.

Check warning on line 180 in src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationRepository.cs

View workflow job for this annotation

GitHub Actions / Build artifacts (Admin, ./src, true)

Possible null reference return.

Check warning on line 180 in src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationRepository.cs

View workflow job for this annotation

GitHub Actions / Build artifacts (Api, ./src)

Possible null reference return.

Check warning on line 180 in src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationRepository.cs

View workflow job for this annotation

GitHub Actions / Build artifacts (Events, ./src)

Possible null reference return.

Check warning on line 180 in src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationRepository.cs

View workflow job for this annotation

GitHub Actions / Build artifacts (Billing, ./src)

Possible null reference return.

Check warning on line 180 in src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationRepository.cs

View workflow job for this annotation

GitHub Actions / Build artifacts (Identity, ./src)

Possible null reference return.

Check warning on line 180 in src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationRepository.cs

View workflow job for this annotation

GitHub Actions / Build artifacts (EventsProcessor, ./src)

Possible null reference return.

Check warning on line 180 in src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationRepository.cs

View workflow job for this annotation

GitHub Actions / Build artifacts (Scim, ./bitwarden_license/src, true)

Possible null reference return.

Check warning on line 180 in src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationRepository.cs

View workflow job for this annotation

GitHub Actions / Build artifacts (Icons, ./src)

Possible null reference return.

Check warning on line 180 in src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationRepository.cs

View workflow job for this annotation

GitHub Actions / Build artifacts (Notifications, ./src)

Possible null reference return.

Check warning on line 180 in src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationRepository.cs

View workflow job for this annotation

GitHub Actions / Build artifacts (Sso, ./bitwarden_license/src, true)

Possible null reference return.

Check warning on line 180 in src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationRepository.cs

View check run for this annotation

Codecov / codecov/patch

src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationRepository.cs#L180

Added line #L180 was not covered by tests

Check warning on line 180 in src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationRepository.cs

View workflow job for this annotation

GitHub Actions / Upload

Possible null reference return.
}
}

Check warning on line 182 in src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationRepository.cs

View check run for this annotation

Codecov / codecov/patch

src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationRepository.cs#L182

Added line #L182 was not covered by tests
}
Original file line number Diff line number Diff line change
Expand Up @@ -545,4 +545,17 @@
transaction: transaction,
commandType: CommandType.StoredProcedure);
}

public async Task<ICollection<OrganizationUser>> GetManyByOrganizationWithClaimedDomainsAsync(Guid organizationId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<OrganizationUser>(
$"[{Schema}].[OrganizationUser_ReadByOrganizationIdWithClaimedDomains]",
new { OrganizationId = organizationId },
commandType: CommandType.StoredProcedure);

Check warning on line 556 in src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationUserRepository.cs

View check run for this annotation

Codecov / codecov/patch

src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationUserRepository.cs#L550-L556

Added lines #L550 - L556 were not covered by tests

return results.ToList();

Check warning on line 558 in src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationUserRepository.cs

View check run for this annotation

Codecov / codecov/patch

src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationUserRepository.cs#L558

Added line #L558 was not covered by tests
}
}

Check warning on line 560 in src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationUserRepository.cs

View check run for this annotation

Codecov / codecov/patch

src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationUserRepository.cs#L560

Added line #L560 was not covered by tests
}
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,25 @@
return await query.ToListAsync();
}

public async Task<Core.AdminConsole.Entities.Organization> GetByClaimedUserDomainAsync(Guid userId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);

Check warning on line 278 in src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationRepository.cs

View check run for this annotation

Codecov / codecov/patch

src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationRepository.cs#L275-L278

Added lines #L275 - L278 were not covered by tests

var query = from u in dbContext.Users
join ou in dbContext.OrganizationUsers on u.Id equals ou.UserId
join o in dbContext.Organizations on ou.OrganizationId equals o.Id
join od in dbContext.OrganizationDomains on ou.OrganizationId equals od.OrganizationId
where u.Id == userId
&& od.VerifiedDate != null
&& u.Email.ToLower().EndsWith("@" + od.DomainName.ToLower())
select o;

Check warning on line 287 in src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationRepository.cs

View check run for this annotation

Codecov / codecov/patch

src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationRepository.cs#L280-L287

Added lines #L280 - L287 were not covered by tests

return await query.FirstOrDefaultAsync();

Check warning on line 289 in src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationRepository.cs

View check run for this annotation

Codecov / codecov/patch

src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationRepository.cs#L289

Added line #L289 was not covered by tests
}
}

Check warning on line 291 in src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationRepository.cs

View check run for this annotation

Codecov / codecov/patch

src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationRepository.cs#L291

Added line #L291 was not covered by tests

public Task EnableCollectionEnhancements(Guid organizationId)
{
throw new NotImplementedException("Collection enhancements migration is not yet supported for Entity Framework.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -711,4 +711,14 @@
};
}

public async Task<ICollection<Core.Entities.OrganizationUser>> GetManyByOrganizationWithClaimedDomainsAsync(Guid organizationId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = new OrganizationUserReadByClaimedOrganizationDomainsQuery(organizationId);
var data = await query.Run(dbContext).ToListAsync();
return data;

Check warning on line 721 in src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationUserRepository.cs

View check run for this annotation

Codecov / codecov/patch

src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationUserRepository.cs#L715-L721

Added lines #L715 - L721 were not covered by tests
}
}

Check warning on line 723 in src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationUserRepository.cs

View check run for this annotation

Codecov / codecov/patch

src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationUserRepository.cs#L723

Added line #L723 was not covered by tests
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Bit.Core.Entities;

namespace Bit.Infrastructure.EntityFramework.Repositories.Queries;

public class OrganizationUserReadByClaimedOrganizationDomainsQuery : IQuery<OrganizationUser>
{
private readonly Guid _organizationId;

public OrganizationUserReadByClaimedOrganizationDomainsQuery(Guid organizationId)
{
_organizationId = organizationId;
}

Check warning on line 12 in src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/OrganizationUserReadByClaimedOrganizationDomainsQuery.cs

View check run for this annotation

Codecov / codecov/patch

src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/OrganizationUserReadByClaimedOrganizationDomainsQuery.cs#L9-L12

Added lines #L9 - L12 were not covered by tests

public IQueryable<OrganizationUser> Run(DatabaseContext dbContext)
{
var query = from ou in dbContext.OrganizationUsers
join u in dbContext.Users on ou.UserId equals u.Id
where ou.OrganizationId == _organizationId
&& dbContext.OrganizationDomains
.Any(od => od.OrganizationId == _organizationId &&
od.VerifiedDate != null &&
u.Email.ToLower().EndsWith("@" + od.DomainName.ToLower()))
select ou;

Check warning on line 23 in src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/OrganizationUserReadByClaimedOrganizationDomainsQuery.cs

View check run for this annotation

Codecov / codecov/patch

src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/OrganizationUserReadByClaimedOrganizationDomainsQuery.cs#L15-L23

Added lines #L15 - L23 were not covered by tests

return query;
}

Check warning on line 26 in src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/OrganizationUserReadByClaimedOrganizationDomainsQuery.cs

View check run for this annotation

Codecov / codecov/patch

src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/OrganizationUserReadByClaimedOrganizationDomainsQuery.cs#L25-L26

Added lines #L25 - L26 were not covered by tests
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
CREATE PROCEDURE [dbo].[OrganizationUser_ReadByOrganizationIdWithClaimedDomains]
@OrganizationId UNIQUEIDENTIFIER
AS
BEGIN
SET NOCOUNT ON;

SELECT OU.*
FROM [dbo].[OrganizationUserView] OU
INNER JOIN [dbo].[UserView] U ON OU.[UserId] = U.[Id]
WHERE OU.[OrganizationId] = @OrganizationId
AND EXISTS (
SELECT 1
FROM [dbo].[OrganizationDomainView] OD
WHERE OD.[OrganizationId] = @OrganizationId
AND OD.[VerifiedDate] IS NOT NULL
AND U.[Email] LIKE '%@' + OD.[DomainName]
);
END
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
CREATE PROCEDURE [dbo].[Organization_ReadByClaimedUserEmailDomain]
@UserId UNIQUEIDENTIFIER
AS
BEGIN
SET NOCOUNT ON;

SELECT O.*
FROM [dbo].[UserView] U
INNER JOIN [dbo].[OrganizationUserView] OU ON U.[Id] = OU.[UserId]
INNER JOIN [dbo].[OrganizationView] O ON OU.[OrganizationId] = O.[Id]
INNER JOIN [dbo].[OrganizationDomainView] OD ON OU.[OrganizationId] = OD.[OrganizationId]
WHERE U.[Id] = @UserId
AND OD.[VerifiedDate] IS NOT NULL
AND U.[Email] LIKE '%@' + OD.[DomainName];
END
Loading
Loading