1
0
mirror of https://github.com/bitwarden/server synced 2026-01-06 02:23:51 +00:00

[PM-20112] Member access stored proc and splitting the query (#5943)

This commit is contained in:
Tom
2025-06-16 17:32:36 -04:00
committed by GitHub
parent 66d1c70dc6
commit b8244908ec
32 changed files with 10480 additions and 347 deletions

View File

@@ -1,5 +1,6 @@
using Bit.Api.Dirt.Models;
using Bit.Api.Dirt.Models.Response;
using Bit.Api.Tools.Models.Response;
using Bit.Core.Context;
using Bit.Core.Dirt.Reports.Entities;
using Bit.Core.Dirt.Reports.Models.Data;
@@ -17,21 +18,24 @@ namespace Bit.Api.Dirt.Controllers;
public class ReportsController : Controller
{
private readonly ICurrentContext _currentContext;
private readonly IMemberAccessCipherDetailsQuery _memberAccessCipherDetailsQuery;
private readonly IMemberAccessReportQuery _memberAccessReportQuery;
private readonly IRiskInsightsReportQuery _riskInsightsReportQuery;
private readonly IAddPasswordHealthReportApplicationCommand _addPwdHealthReportAppCommand;
private readonly IGetPasswordHealthReportApplicationQuery _getPwdHealthReportAppQuery;
private readonly IDropPasswordHealthReportApplicationCommand _dropPwdHealthReportAppCommand;
public ReportsController(
ICurrentContext currentContext,
IMemberAccessCipherDetailsQuery memberAccessCipherDetailsQuery,
IMemberAccessReportQuery memberAccessReportQuery,
IRiskInsightsReportQuery riskInsightsReportQuery,
IAddPasswordHealthReportApplicationCommand addPasswordHealthReportApplicationCommand,
IGetPasswordHealthReportApplicationQuery getPasswordHealthReportApplicationQuery,
IDropPasswordHealthReportApplicationCommand dropPwdHealthReportAppCommand
)
{
_currentContext = currentContext;
_memberAccessCipherDetailsQuery = memberAccessCipherDetailsQuery;
_memberAccessReportQuery = memberAccessReportQuery;
_riskInsightsReportQuery = riskInsightsReportQuery;
_addPwdHealthReportAppCommand = addPasswordHealthReportApplicationCommand;
_getPwdHealthReportAppQuery = getPasswordHealthReportApplicationQuery;
_dropPwdHealthReportAppCommand = dropPwdHealthReportAppCommand;
@@ -54,9 +58,9 @@ public class ReportsController : Controller
throw new NotFoundException();
}
var memberCipherDetails = await GetMemberCipherDetails(new MemberAccessCipherDetailsRequest { OrganizationId = orgId });
var riskDetails = await GetRiskInsightsReportDetails(new RiskInsightsReportRequest { OrganizationId = orgId });
var responses = memberCipherDetails.Select(x => new MemberCipherDetailsResponseModel(x));
var responses = riskDetails.Select(x => new MemberCipherDetailsResponseModel(x));
return responses;
}
@@ -69,16 +73,16 @@ public class ReportsController : Controller
/// <returns>IEnumerable of MemberAccessReportResponseModel</returns>
/// <exception cref="NotFoundException">If Access reports permission is not assigned</exception>
[HttpGet("member-access/{orgId}")]
public async Task<IEnumerable<MemberAccessReportResponseModel>> GetMemberAccessReport(Guid orgId)
public async Task<IEnumerable<MemberAccessDetailReportResponseModel>> GetMemberAccessReport(Guid orgId)
{
if (!await _currentContext.AccessReports(orgId))
{
throw new NotFoundException();
}
var memberCipherDetails = await GetMemberCipherDetails(new MemberAccessCipherDetailsRequest { OrganizationId = orgId });
var accessDetails = await GetMemberAccessDetails(new MemberAccessReportRequest { OrganizationId = orgId });
var responses = memberCipherDetails.Select(x => new MemberAccessReportResponseModel(x));
var responses = accessDetails.Select(x => new MemberAccessDetailReportResponseModel(x));
return responses;
}
@@ -87,13 +91,28 @@ public class ReportsController : Controller
/// Contains the organization member info, the cipher ids associated with the member,
/// and details on their collections, groups, and permissions
/// </summary>
/// <param name="request">Request to the MemberAccessCipherDetailsQuery</param>
/// <returns>IEnumerable of MemberAccessCipherDetails</returns>
private async Task<IEnumerable<MemberAccessCipherDetails>> GetMemberCipherDetails(MemberAccessCipherDetailsRequest request)
/// <param name="request">Request parameters</param>
/// <returns>
/// List of a user's permissions at a group and collection level as well as the number of ciphers
/// associated with that group/collection
/// </returns>
private async Task<IEnumerable<MemberAccessReportDetail>> GetMemberAccessDetails(
MemberAccessReportRequest request)
{
var memberCipherDetails =
await _memberAccessCipherDetailsQuery.GetMemberAccessCipherDetails(request);
return memberCipherDetails;
var accessDetails = await _memberAccessReportQuery.GetMemberAccessReportsAsync(request);
return accessDetails;
}
/// <summary>
/// Gets the risk insights report details from the risk insights query. Associates a user to their cipher ids
/// </summary>
/// <param name="request">Request parameters</param>
/// <returns>A list of risk insights data associating the user to cipher ids</returns>
private async Task<IEnumerable<RiskInsightsReportDetail>> GetRiskInsightsReportDetails(
RiskInsightsReportRequest request)
{
var riskDetails = await _riskInsightsReportQuery.GetRiskInsightsReportDetails(request);
return riskDetails;
}
/// <summary>

View File

@@ -0,0 +1,39 @@
using Bit.Core.Dirt.Reports.Models.Data;
namespace Bit.Api.Tools.Models.Response;
public class MemberAccessDetailReportResponseModel
{
public Guid? UserGuid { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public bool TwoFactorEnabled { get; set; }
public bool AccountRecoveryEnabled { get; set; }
public bool UsesKeyConnector { get; set; }
public Guid? CollectionId { get; set; }
public Guid? GroupId { get; set; }
public string GroupName { get; set; }
public string CollectionName { get; set; }
public bool? ReadOnly { get; set; }
public bool? HidePasswords { get; set; }
public bool? Manage { get; set; }
public IEnumerable<Guid> CipherIds { get; set; }
public MemberAccessDetailReportResponseModel(MemberAccessReportDetail reportDetail)
{
UserGuid = reportDetail.UserGuid;
UserName = reportDetail.UserName;
Email = reportDetail.Email;
TwoFactorEnabled = reportDetail.TwoFactorEnabled;
AccountRecoveryEnabled = reportDetail.AccountRecoveryEnabled;
UsesKeyConnector = reportDetail.UsesKeyConnector;
CollectionId = reportDetail.CollectionId;
GroupId = reportDetail.GroupId;
GroupName = reportDetail.GroupName;
CollectionName = reportDetail.CollectionName;
ReadOnly = reportDetail.ReadOnly;
HidePasswords = reportDetail.HidePasswords;
Manage = reportDetail.Manage;
CipherIds = reportDetail.CipherIds;
}
}

View File

@@ -15,12 +15,12 @@ public class MemberCipherDetailsResponseModel
/// </summary>
public IEnumerable<string> CipherIds { get; set; }
public MemberCipherDetailsResponseModel(MemberAccessCipherDetails memberAccessCipherDetails)
public MemberCipherDetailsResponseModel(RiskInsightsReportDetail reportDetail)
{
this.UserGuid = memberAccessCipherDetails.UserGuid;
this.UserName = memberAccessCipherDetails.UserName;
this.Email = memberAccessCipherDetails.Email;
this.UsesKeyConnector = memberAccessCipherDetails.UsesKeyConnector;
this.CipherIds = memberAccessCipherDetails.CipherIds;
this.UserGuid = reportDetail.UserGuid;
this.UserName = reportDetail.UserName;
this.Email = reportDetail.Email;
this.UsesKeyConnector = reportDetail.UsesKeyConnector;
this.CipherIds = reportDetail.CipherIds;
}
}