mirror of
https://github.com/bitwarden/server.git
synced 2025-07-09 03:43:51 -05:00
PM-19715 & PM-19712 Move Files to DIRT ownership on Server (#5769)
* PM-19715 PM-19711 moving reports to dirt directory and adding dirt as codeowners * PM-19715 creating two sub folders for reports and events * PM-19714 changing dirt paths for codeowners * PM-19714 fixing codeowners file * PM-19714 fixing codeowners * PM-19714 moving hibpController to dirt ownership * PM-19715 moving controller
This commit is contained in:
@ -0,0 +1,20 @@
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Bit.Core.Tools.Entities;
|
||||
|
||||
public class PasswordHealthReportApplication : ITableObject<Guid>, IRevisable
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid OrganizationId { get; set; }
|
||||
public string? Uri { get; set; }
|
||||
public DateTime CreationDate { get; set; } = DateTime.UtcNow;
|
||||
public DateTime RevisionDate { get; set; } = DateTime.UtcNow;
|
||||
|
||||
public void SetNewId()
|
||||
{
|
||||
Id = CoreHelpers.GenerateComb();
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
namespace Bit.Core.Tools.Models.Data;
|
||||
|
||||
public class MemberAccessDetails
|
||||
{
|
||||
public Guid? CollectionId { get; set; }
|
||||
public Guid? GroupId { get; set; }
|
||||
public string GroupName { get; set; }
|
||||
public string CollectionName { get; set; }
|
||||
public int ItemCount { get; set; }
|
||||
public bool? ReadOnly { get; set; }
|
||||
public bool? HidePasswords { get; set; }
|
||||
public bool? Manage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The CipherIds associated with the group/collection access
|
||||
/// </summary>
|
||||
public IEnumerable<string> CollectionCipherIds { get; set; }
|
||||
}
|
||||
|
||||
public class MemberAccessCipherDetails
|
||||
{
|
||||
public string UserName { get; set; }
|
||||
public string Email { get; set; }
|
||||
public bool TwoFactorEnabled { get; set; }
|
||||
public bool AccountRecoveryEnabled { get; set; }
|
||||
public int GroupsCount { get; set; }
|
||||
public int CollectionsCount { get; set; }
|
||||
public int TotalItemCount { get; set; }
|
||||
public Guid? UserGuid { get; set; }
|
||||
public bool UsesKeyConnector { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The details for the member's collection access depending
|
||||
/// on the collections and groups they are assigned to
|
||||
/// </summary>
|
||||
public IEnumerable<MemberAccessDetails> AccessDetails { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A distinct list of the cipher ids associated with
|
||||
/// the organization member
|
||||
/// </summary>
|
||||
public IEnumerable<string> CipherIds { get; set; }
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Tools.Entities;
|
||||
using Bit.Core.Tools.ReportFeatures.Interfaces;
|
||||
using Bit.Core.Tools.ReportFeatures.Requests;
|
||||
using Bit.Core.Tools.Repositories;
|
||||
|
||||
namespace Bit.Core.Tools.ReportFeatures;
|
||||
|
||||
public class AddPasswordHealthReportApplicationCommand : IAddPasswordHealthReportApplicationCommand
|
||||
{
|
||||
private IOrganizationRepository _organizationRepo;
|
||||
private IPasswordHealthReportApplicationRepository _passwordHealthReportApplicationRepo;
|
||||
|
||||
public AddPasswordHealthReportApplicationCommand(
|
||||
IOrganizationRepository organizationRepository,
|
||||
IPasswordHealthReportApplicationRepository passwordHealthReportApplicationRepository)
|
||||
{
|
||||
_organizationRepo = organizationRepository;
|
||||
_passwordHealthReportApplicationRepo = passwordHealthReportApplicationRepository;
|
||||
}
|
||||
|
||||
public async Task<PasswordHealthReportApplication> AddPasswordHealthReportApplicationAsync(AddPasswordHealthReportApplicationRequest request)
|
||||
{
|
||||
var (req, IsValid, errorMessage) = await ValidateRequestAsync(request);
|
||||
if (!IsValid)
|
||||
{
|
||||
throw new BadRequestException(errorMessage);
|
||||
}
|
||||
|
||||
var passwordHealthReportApplication = new PasswordHealthReportApplication
|
||||
{
|
||||
OrganizationId = request.OrganizationId,
|
||||
Uri = request.Url,
|
||||
};
|
||||
|
||||
passwordHealthReportApplication.SetNewId();
|
||||
|
||||
var data = await _passwordHealthReportApplicationRepo.CreateAsync(passwordHealthReportApplication);
|
||||
return data;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<PasswordHealthReportApplication>> AddPasswordHealthReportApplicationAsync(IEnumerable<AddPasswordHealthReportApplicationRequest> requests)
|
||||
{
|
||||
var requestsList = requests.ToList();
|
||||
|
||||
// create tasks to validate each request
|
||||
var tasks = requestsList.Select(async request =>
|
||||
{
|
||||
var (req, IsValid, errorMessage) = await ValidateRequestAsync(request);
|
||||
if (!IsValid)
|
||||
{
|
||||
throw new BadRequestException(errorMessage);
|
||||
}
|
||||
});
|
||||
|
||||
// run validations and allow exceptions to bubble
|
||||
await Task.WhenAll(tasks);
|
||||
|
||||
// create PasswordHealthReportApplication entities
|
||||
var passwordHealthReportApplications = requestsList.Select(request =>
|
||||
{
|
||||
var pwdHealthReportApplication = new PasswordHealthReportApplication
|
||||
{
|
||||
OrganizationId = request.OrganizationId,
|
||||
Uri = request.Url,
|
||||
};
|
||||
pwdHealthReportApplication.SetNewId();
|
||||
return pwdHealthReportApplication;
|
||||
});
|
||||
|
||||
// create and return the entities
|
||||
var response = new List<PasswordHealthReportApplication>();
|
||||
foreach (var record in passwordHealthReportApplications)
|
||||
{
|
||||
var data = await _passwordHealthReportApplicationRepo.CreateAsync(record);
|
||||
response.Add(data);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private async Task<Tuple<AddPasswordHealthReportApplicationRequest, bool, string>> ValidateRequestAsync(
|
||||
AddPasswordHealthReportApplicationRequest request)
|
||||
{
|
||||
// verify that the organization exists
|
||||
var organization = await _organizationRepo.GetByIdAsync(request.OrganizationId);
|
||||
if (organization == null)
|
||||
{
|
||||
return new Tuple<AddPasswordHealthReportApplicationRequest, bool, string>(request, false, "Invalid Organization");
|
||||
}
|
||||
|
||||
// ensure that we have a URL
|
||||
if (string.IsNullOrWhiteSpace(request.Url))
|
||||
{
|
||||
return new Tuple<AddPasswordHealthReportApplicationRequest, bool, string>(request, false, "URL is required");
|
||||
}
|
||||
|
||||
return new Tuple<AddPasswordHealthReportApplicationRequest, bool, string>(request, true, string.Empty);
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Tools.ReportFeatures.Interfaces;
|
||||
using Bit.Core.Tools.ReportFeatures.Requests;
|
||||
using Bit.Core.Tools.Repositories;
|
||||
|
||||
namespace Bit.Core.Tools.ReportFeatures;
|
||||
|
||||
public class DropPasswordHealthReportApplicationCommand : IDropPasswordHealthReportApplicationCommand
|
||||
{
|
||||
private IPasswordHealthReportApplicationRepository _passwordHealthReportApplicationRepo;
|
||||
|
||||
public DropPasswordHealthReportApplicationCommand(
|
||||
IPasswordHealthReportApplicationRepository passwordHealthReportApplicationRepository)
|
||||
{
|
||||
_passwordHealthReportApplicationRepo = passwordHealthReportApplicationRepository;
|
||||
}
|
||||
|
||||
public async Task DropPasswordHealthReportApplicationAsync(DropPasswordHealthReportApplicationRequest request)
|
||||
{
|
||||
var data = await _passwordHealthReportApplicationRepo.GetByOrganizationIdAsync(request.OrganizationId);
|
||||
if (data == null)
|
||||
{
|
||||
throw new BadRequestException("Organization does not have any records.");
|
||||
}
|
||||
|
||||
data.Where(_ => request.PasswordHealthReportApplicationIds.Contains(_.Id)).ToList().ForEach(async _ =>
|
||||
{
|
||||
await _passwordHealthReportApplicationRepo.DeleteAsync(_);
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Tools.Entities;
|
||||
using Bit.Core.Tools.ReportFeatures.Interfaces;
|
||||
using Bit.Core.Tools.Repositories;
|
||||
|
||||
namespace Bit.Core.Tools.ReportFeatures;
|
||||
|
||||
public class GetPasswordHealthReportApplicationQuery : IGetPasswordHealthReportApplicationQuery
|
||||
{
|
||||
private IPasswordHealthReportApplicationRepository _passwordHealthReportApplicationRepo;
|
||||
|
||||
public GetPasswordHealthReportApplicationQuery(
|
||||
IPasswordHealthReportApplicationRepository passwordHealthReportApplicationRepo)
|
||||
{
|
||||
_passwordHealthReportApplicationRepo = passwordHealthReportApplicationRepo;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<PasswordHealthReportApplication>> GetPasswordHealthReportApplicationAsync(Guid organizationId)
|
||||
{
|
||||
if (organizationId == Guid.Empty)
|
||||
{
|
||||
throw new BadRequestException("OrganizationId is required.");
|
||||
}
|
||||
|
||||
return await _passwordHealthReportApplicationRepo.GetByOrganizationIdAsync(organizationId);
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
using Bit.Core.Tools.Entities;
|
||||
using Bit.Core.Tools.ReportFeatures.Requests;
|
||||
|
||||
namespace Bit.Core.Tools.ReportFeatures.Interfaces;
|
||||
|
||||
public interface IAddPasswordHealthReportApplicationCommand
|
||||
{
|
||||
Task<PasswordHealthReportApplication> AddPasswordHealthReportApplicationAsync(AddPasswordHealthReportApplicationRequest request);
|
||||
Task<IEnumerable<PasswordHealthReportApplication>> AddPasswordHealthReportApplicationAsync(IEnumerable<AddPasswordHealthReportApplicationRequest> requests);
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
using Bit.Core.Tools.ReportFeatures.Requests;
|
||||
|
||||
namespace Bit.Core.Tools.ReportFeatures.Interfaces;
|
||||
|
||||
public interface IDropPasswordHealthReportApplicationCommand
|
||||
{
|
||||
Task DropPasswordHealthReportApplicationAsync(DropPasswordHealthReportApplicationRequest request);
|
||||
}
|
||||
|
@ -0,0 +1,8 @@
|
||||
using Bit.Core.Tools.Entities;
|
||||
|
||||
namespace Bit.Core.Tools.ReportFeatures.Interfaces;
|
||||
|
||||
public interface IGetPasswordHealthReportApplicationQuery
|
||||
{
|
||||
Task<IEnumerable<PasswordHealthReportApplication>> GetPasswordHealthReportApplicationAsync(Guid organizationId);
|
||||
}
|
@ -0,0 +1,206 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Models.Data.Organizations;
|
||||
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Tools.Models.Data;
|
||||
using Bit.Core.Tools.ReportFeatures.OrganizationReportMembers.Interfaces;
|
||||
using Bit.Core.Tools.ReportFeatures.Requests;
|
||||
using Bit.Core.Vault.Models.Data;
|
||||
using Bit.Core.Vault.Queries;
|
||||
using Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||
using Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Requests;
|
||||
|
||||
namespace Bit.Core.Tools.ReportFeatures;
|
||||
|
||||
public class MemberAccessCipherDetailsQuery : IMemberAccessCipherDetailsQuery
|
||||
{
|
||||
private readonly IOrganizationUserUserDetailsQuery _organizationUserUserDetailsQuery;
|
||||
private readonly IGroupRepository _groupRepository;
|
||||
private readonly ICollectionRepository _collectionRepository;
|
||||
private readonly IOrganizationCiphersQuery _organizationCiphersQuery;
|
||||
private readonly IApplicationCacheService _applicationCacheService;
|
||||
private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery;
|
||||
|
||||
public MemberAccessCipherDetailsQuery(
|
||||
IOrganizationUserUserDetailsQuery organizationUserUserDetailsQuery,
|
||||
IGroupRepository groupRepository,
|
||||
ICollectionRepository collectionRepository,
|
||||
IOrganizationCiphersQuery organizationCiphersQuery,
|
||||
IApplicationCacheService applicationCacheService,
|
||||
ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery
|
||||
)
|
||||
{
|
||||
_organizationUserUserDetailsQuery = organizationUserUserDetailsQuery;
|
||||
_groupRepository = groupRepository;
|
||||
_collectionRepository = collectionRepository;
|
||||
_organizationCiphersQuery = organizationCiphersQuery;
|
||||
_applicationCacheService = applicationCacheService;
|
||||
_twoFactorIsEnabledQuery = twoFactorIsEnabledQuery;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<MemberAccessCipherDetails>> GetMemberAccessCipherDetails(MemberAccessCipherDetailsRequest request)
|
||||
{
|
||||
var orgUsers = await _organizationUserUserDetailsQuery.GetOrganizationUserUserDetails(
|
||||
new OrganizationUserUserDetailsQueryRequest
|
||||
{
|
||||
OrganizationId = request.OrganizationId,
|
||||
IncludeCollections = true,
|
||||
IncludeGroups = true
|
||||
});
|
||||
|
||||
var orgGroups = await _groupRepository.GetManyByOrganizationIdAsync(request.OrganizationId);
|
||||
var orgAbility = await _applicationCacheService.GetOrganizationAbilityAsync(request.OrganizationId);
|
||||
var orgCollectionsWithAccess = await _collectionRepository.GetManyByOrganizationIdWithAccessAsync(request.OrganizationId);
|
||||
var orgItems = await _organizationCiphersQuery.GetAllOrganizationCiphers(request.OrganizationId);
|
||||
var organizationUsersTwoFactorEnabled = await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(orgUsers);
|
||||
|
||||
var memberAccessCipherDetails = GenerateAccessDataParallel(
|
||||
orgGroups,
|
||||
orgCollectionsWithAccess,
|
||||
orgItems,
|
||||
organizationUsersTwoFactorEnabled,
|
||||
orgAbility);
|
||||
|
||||
return memberAccessCipherDetails;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a report for all members of an organization. Containing summary information
|
||||
/// such as item, collection, and group counts. Including the cipherIds a member is assigned.
|
||||
/// Child collection includes detailed information on the user and group collections along
|
||||
/// with their permissions.
|
||||
/// </summary>
|
||||
/// <param name="orgGroups">Organization groups collection</param>
|
||||
/// <param name="orgCollectionsWithAccess">Collections for the organization and the groups/users and permissions</param>
|
||||
/// <param name="orgItems">Cipher items for the organization with the collections associated with them</param>
|
||||
/// <param name="organizationUsersTwoFactorEnabled">Organization users and two factor status</param>
|
||||
/// <param name="orgAbility">Organization ability for account recovery status</param>
|
||||
/// <returns>List of the MemberAccessCipherDetailsModel</returns>;
|
||||
private IEnumerable<MemberAccessCipherDetails> GenerateAccessDataParallel(
|
||||
ICollection<Group> orgGroups,
|
||||
ICollection<Tuple<Collection, CollectionAccessDetails>> orgCollectionsWithAccess,
|
||||
IEnumerable<CipherOrganizationDetailsWithCollections> orgItems,
|
||||
IEnumerable<(OrganizationUserUserDetails user, bool twoFactorIsEnabled)> organizationUsersTwoFactorEnabled,
|
||||
OrganizationAbility orgAbility)
|
||||
{
|
||||
var orgUsers = organizationUsersTwoFactorEnabled.Select(x => x.user).ToList();
|
||||
var groupNameDictionary = orgGroups.ToDictionary(x => x.Id, x => x.Name);
|
||||
var collectionItems = orgItems
|
||||
.SelectMany(x => x.CollectionIds,
|
||||
(cipher, collectionId) => new { Cipher = cipher, CollectionId = collectionId })
|
||||
.GroupBy(y => y.CollectionId,
|
||||
(key, ciphers) => new { CollectionId = key, Ciphers = ciphers });
|
||||
var itemLookup = collectionItems.ToDictionary(x => x.CollectionId.ToString(), x => x.Ciphers.Select(c => c.Cipher.Id.ToString()).ToList());
|
||||
|
||||
var memberAccessCipherDetails = new ConcurrentBag<MemberAccessCipherDetails>();
|
||||
|
||||
Parallel.ForEach(orgUsers, user =>
|
||||
{
|
||||
var groupAccessDetails = new List<MemberAccessDetails>();
|
||||
var userCollectionAccessDetails = new List<MemberAccessDetails>();
|
||||
|
||||
foreach (var tCollect in orgCollectionsWithAccess)
|
||||
{
|
||||
if (itemLookup.TryGetValue(tCollect.Item1.Id.ToString(), out var items))
|
||||
{
|
||||
var itemCounts = items.Count;
|
||||
|
||||
if (tCollect.Item2.Groups.Any())
|
||||
{
|
||||
var groupDetails = tCollect.Item2.Groups
|
||||
.Where(tCollectGroups => user.Groups.Contains(tCollectGroups.Id))
|
||||
.Select(x => new MemberAccessDetails
|
||||
{
|
||||
CollectionId = tCollect.Item1.Id,
|
||||
CollectionName = tCollect.Item1.Name,
|
||||
GroupId = x.Id,
|
||||
GroupName = groupNameDictionary[x.Id],
|
||||
ReadOnly = x.ReadOnly,
|
||||
HidePasswords = x.HidePasswords,
|
||||
Manage = x.Manage,
|
||||
ItemCount = itemCounts,
|
||||
CollectionCipherIds = items
|
||||
});
|
||||
|
||||
groupAccessDetails.AddRange(groupDetails);
|
||||
}
|
||||
|
||||
if (tCollect.Item2.Users.Any())
|
||||
{
|
||||
var userCollectionDetails = tCollect.Item2.Users
|
||||
.Where(tCollectUser => tCollectUser.Id == user.Id)
|
||||
.Select(x => new MemberAccessDetails
|
||||
{
|
||||
CollectionId = tCollect.Item1.Id,
|
||||
CollectionName = tCollect.Item1.Name,
|
||||
ReadOnly = x.ReadOnly,
|
||||
HidePasswords = x.HidePasswords,
|
||||
Manage = x.Manage,
|
||||
ItemCount = itemCounts,
|
||||
CollectionCipherIds = items
|
||||
});
|
||||
|
||||
userCollectionAccessDetails.AddRange(userCollectionDetails);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var report = new MemberAccessCipherDetails
|
||||
{
|
||||
UserName = user.Name,
|
||||
Email = user.Email,
|
||||
TwoFactorEnabled = organizationUsersTwoFactorEnabled.FirstOrDefault(u => u.user.Id == user.Id).twoFactorIsEnabled,
|
||||
AccountRecoveryEnabled = !string.IsNullOrEmpty(user.ResetPasswordKey) && orgAbility.UseResetPassword,
|
||||
UserGuid = user.Id,
|
||||
UsesKeyConnector = user.UsesKeyConnector
|
||||
};
|
||||
|
||||
var userAccessDetails = new List<MemberAccessDetails>();
|
||||
if (user.Groups.Any())
|
||||
{
|
||||
var userGroups = groupAccessDetails.Where(x => user.Groups.Contains(x.GroupId.GetValueOrDefault()));
|
||||
userAccessDetails.AddRange(userGroups);
|
||||
}
|
||||
|
||||
var groupsWithoutCollections = user.Groups.Where(x => !userAccessDetails.Any(y => x == y.GroupId));
|
||||
if (groupsWithoutCollections.Any())
|
||||
{
|
||||
var emptyGroups = groupsWithoutCollections.Select(x => new MemberAccessDetails
|
||||
{
|
||||
GroupId = x,
|
||||
GroupName = groupNameDictionary[x],
|
||||
ItemCount = 0
|
||||
});
|
||||
userAccessDetails.AddRange(emptyGroups);
|
||||
}
|
||||
|
||||
if (user.Collections.Any())
|
||||
{
|
||||
var userCollections = userCollectionAccessDetails.Where(x => user.Collections.Any(y => x.CollectionId == y.Id));
|
||||
userAccessDetails.AddRange(userCollections);
|
||||
}
|
||||
report.AccessDetails = userAccessDetails;
|
||||
|
||||
var userCiphers = report.AccessDetails
|
||||
.Where(x => x.ItemCount > 0)
|
||||
.SelectMany(y => y.CollectionCipherIds)
|
||||
.Distinct();
|
||||
report.CipherIds = userCiphers;
|
||||
report.TotalItemCount = userCiphers.Count();
|
||||
|
||||
var distinctItems = report.AccessDetails.Where(x => x.CollectionId.HasValue).Select(x => x.CollectionId).Distinct();
|
||||
report.CollectionsCount = distinctItems.Count();
|
||||
report.GroupsCount = report.AccessDetails.Select(x => x.GroupId).Where(y => y.HasValue).Distinct().Count();
|
||||
|
||||
memberAccessCipherDetails.Add(report);
|
||||
});
|
||||
|
||||
return memberAccessCipherDetails;
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
using Bit.Core.Tools.Models.Data;
|
||||
using Bit.Core.Tools.ReportFeatures.Requests;
|
||||
|
||||
namespace Bit.Core.Tools.ReportFeatures.OrganizationReportMembers.Interfaces;
|
||||
|
||||
public interface IMemberAccessCipherDetailsQuery
|
||||
{
|
||||
Task<IEnumerable<MemberAccessCipherDetails>> GetMemberAccessCipherDetails(MemberAccessCipherDetailsRequest request);
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
using Bit.Core.Tools.ReportFeatures.Interfaces;
|
||||
using Bit.Core.Tools.ReportFeatures.OrganizationReportMembers.Interfaces;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Bit.Core.Tools.ReportFeatures;
|
||||
|
||||
public static class ReportingServiceCollectionExtensions
|
||||
{
|
||||
public static void AddReportingServices(this IServiceCollection services)
|
||||
{
|
||||
services.AddScoped<IMemberAccessCipherDetailsQuery, MemberAccessCipherDetailsQuery>();
|
||||
services.AddScoped<IAddPasswordHealthReportApplicationCommand, AddPasswordHealthReportApplicationCommand>();
|
||||
services.AddScoped<IGetPasswordHealthReportApplicationQuery, GetPasswordHealthReportApplicationQuery>();
|
||||
services.AddScoped<IDropPasswordHealthReportApplicationCommand, DropPasswordHealthReportApplicationCommand>();
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
namespace Bit.Core.Tools.ReportFeatures.Requests;
|
||||
|
||||
public class AddPasswordHealthReportApplicationRequest
|
||||
{
|
||||
public Guid OrganizationId { get; set; }
|
||||
public string Url { get; set; }
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
namespace Bit.Core.Tools.ReportFeatures.Requests;
|
||||
|
||||
public class DropPasswordHealthReportApplicationRequest
|
||||
{
|
||||
public Guid OrganizationId { get; set; }
|
||||
public IEnumerable<Guid> PasswordHealthReportApplicationIds { get; set; }
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
namespace Bit.Core.Tools.ReportFeatures.Requests;
|
||||
|
||||
public class MemberAccessCipherDetailsRequest
|
||||
{
|
||||
public Guid OrganizationId { get; set; }
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Tools.Entities;
|
||||
|
||||
namespace Bit.Core.Tools.Repositories;
|
||||
|
||||
public interface IPasswordHealthReportApplicationRepository : IRepository<PasswordHealthReportApplication, Guid>
|
||||
{
|
||||
Task<ICollection<PasswordHealthReportApplication>> GetByOrganizationIdAsync(Guid organizationId);
|
||||
}
|
Reference in New Issue
Block a user