Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
73 changes: 36 additions & 37 deletions src/Api/Dirt/Controllers/OrganizationReportsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -467,43 +467,6 @@ public async Task<IActionResult> DownloadReportFileAsync(Guid organizationId, Gu
return File(stream, "application/octet-stream", fileData.FileName);
}

private async Task AuthorizeAsync(Guid organizationId)
{
if (!await _currentContext.AccessReports(organizationId))
{
throw new NotFoundException();
}

var orgAbility = await _organizationAbilityCacheService.GetOrganizationAbilityAsync(organizationId);
if (orgAbility is null || !orgAbility.UseRiskInsights)
{
throw new BadRequestException("Your organization's plan does not support this feature.");
}
}

private static void EnsureValidIds(Guid organizationId, Guid? reportId = null)
{
if (organizationId == Guid.Empty)
{
throw new BadRequestException("OrganizationId is required.");
}

if (reportId.HasValue && reportId.Value == Guid.Empty)
{
throw new BadRequestException("ReportId is required.");
}
}

private async Task<OrganizationReport> GetAuthorizedReportAsync(Guid organizationId, Guid reportId)
{
EnsureValidIds(organizationId, reportId);
await AuthorizeAsync(organizationId);
var report = await _getOrganizationReportQuery.GetOrganizationReportAsync(reportId);
if (report.OrganizationId != organizationId) throw new BadRequestException("Invalid report ID");
return report;
}


// Is being used by client on V2

[HttpGet("{organizationId}/data/summary/{reportId}")]
Expand Down Expand Up @@ -576,4 +539,40 @@ public async Task<IActionResult> UpdateOrganizationReportApplicationDataAsync(

return Ok(response);
}

private async Task AuthorizeAsync(Guid organizationId)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Note: all the same logic here, just moved private methods to the bottom of file to improve order

{
if (!await _currentContext.AccessReports(organizationId))
{
throw new NotFoundException();
}

var orgAbility = await _organizationAbilityCacheService.GetOrganizationAbilityAsync(organizationId);
if (orgAbility is null || !orgAbility.UseRiskInsights)
{
throw new BadRequestException("Your organization's plan does not support this feature.");
}
}

private static void EnsureValidIds(Guid organizationId, Guid? reportId = null)
{
if (organizationId == Guid.Empty)
{
throw new BadRequestException("OrganizationId is required.");
}

if (reportId.HasValue && reportId.Value == Guid.Empty)
{
throw new BadRequestException("ReportId is required.");
}
}

private async Task<OrganizationReport> GetAuthorizedReportAsync(Guid organizationId, Guid reportId)
{
EnsureValidIds(organizationId, reportId);
await AuthorizeAsync(organizationId);
var report = await _getOrganizationReportQuery.GetOrganizationReportAsync(reportId);
if (report.OrganizationId != organizationId) throw new BadRequestException("Invalid report ID");
return report;
}
}
64 changes: 23 additions & 41 deletions src/Api/Dirt/Controllers/ReportsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Bit.Api.Dirt.Models.Response;
using Bit.Api.Tools.Models.Response;
using Bit.Core;
using Bit.Core.AdminConsole.AbilitiesCache;
using Bit.Core.Context;
using Bit.Core.Dirt.Entities;
using Bit.Core.Dirt.Reports.Models.Data;
Expand All @@ -26,6 +27,7 @@ public class ReportsController : Controller
private readonly IGetPasswordHealthReportApplicationQuery _getPwdHealthReportAppQuery;
private readonly IDropPasswordHealthReportApplicationCommand _dropPwdHealthReportAppCommand;
private readonly IGetPasskeyDirectoryQuery _getPasskeyDirectoryQuery;
private readonly IOrganizationAbilityCacheService _organizationAbilityCacheService;
private readonly ILogger<ReportsController> _logger;

public ReportsController(
Expand All @@ -36,6 +38,7 @@ public ReportsController(
IGetPasswordHealthReportApplicationQuery getPasswordHealthReportApplicationQuery,
IDropPasswordHealthReportApplicationCommand dropPwdHealthReportAppCommand,
IGetPasskeyDirectoryQuery getPasskeyDirectoryQuery,
IOrganizationAbilityCacheService organizationAbilityCacheService,
ILogger<ReportsController> logger
)
{
Expand All @@ -46,6 +49,7 @@ ILogger<ReportsController> logger
_getPwdHealthReportAppQuery = getPasswordHealthReportApplicationQuery;
_dropPwdHealthReportAppCommand = dropPwdHealthReportAppCommand;
_getPasskeyDirectoryQuery = getPasskeyDirectoryQuery;
_organizationAbilityCacheService = organizationAbilityCacheService;
_logger = logger;
}

Expand All @@ -59,12 +63,7 @@ ILogger<ReportsController> logger
[HttpGet("member-cipher-details/{orgId}")]
public async Task<IEnumerable<MemberCipherDetailsResponseModel>> GetMemberCipherDetails(Guid orgId)
{
// Using the AccessReports permission here until new permissions
// are needed for more control over reports
if (!await _currentContext.AccessReports(orgId))
{
throw new NotFoundException();
}
await AuthorizeAsync(orgId);

var riskDetails = await GetRiskInsightsReportDetails(new RiskInsightsReportRequest { OrganizationId = orgId });

Expand Down Expand Up @@ -125,39 +124,11 @@ private async Task<IEnumerable<RiskInsightsReportDetail>> GetRiskInsightsReportD
[HttpGet("password-health-report-applications/{orgId}")]
public async Task<IEnumerable<PasswordHealthReportApplication>> GetPasswordHealthReportApplications(Guid orgId)
{
if (!await _currentContext.AccessReports(orgId))
{
throw new NotFoundException();
}
await AuthorizeAsync(orgId);

return await _getPwdHealthReportAppQuery.GetPasswordHealthReportApplicationAsync(orgId);
}

/// <summary>
/// Adds a new record into PasswordHealthReportApplication
/// </summary>
/// <param name="request">A single instance of PasswordHealthReportApplication Model</param>
/// <returns>A single instance of PasswordHealthReportApplication</returns>
/// <exception cref="BadRequestException">If the organization Id is not valid</exception>
/// <exception cref="NotFoundException">If the user lacks access</exception>
[HttpPost("password-health-report-application")]
public async Task<PasswordHealthReportApplication> AddPasswordHealthReportApplication(
[FromBody] PasswordHealthReportApplicationModel request)
{
if (!await _currentContext.AccessReports(request.OrganizationId))
{
throw new NotFoundException();
}

var commandRequest = new AddPasswordHealthReportApplicationRequest
{
OrganizationId = request.OrganizationId,
Url = request.Url
};

return await _addPwdHealthReportAppCommand.AddPasswordHealthReportApplicationAsync(commandRequest);
}
Comment on lines -136 to -159

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Note: this endpoint is removed as it is never called in the clients codebase. Only the endpoint for adding multiple critical applications at once is in use


/// <summary>
/// Adds multiple records into PasswordHealthReportApplication
/// </summary>
Expand All @@ -169,9 +140,9 @@ public async Task<PasswordHealthReportApplication> AddPasswordHealthReportApplic
public async Task<IEnumerable<PasswordHealthReportApplication>> AddPasswordHealthReportApplications(
[FromBody] IEnumerable<PasswordHealthReportApplicationModel> request)
{
if (request.Any(_ => _currentContext.AccessReports(_.OrganizationId).Result == false))
foreach (var item in request)
{
throw new NotFoundException();
await AuthorizeAsync(item.OrganizationId);
}

var commandRequests = request.Select(request => new AddPasswordHealthReportApplicationRequest
Expand All @@ -197,10 +168,7 @@ public async Task<IEnumerable<PasswordHealthReportApplication>> AddPasswordHealt
public async Task DropPasswordHealthReportApplication(
[FromBody] DropPasswordHealthReportApplicationRequest request)
{
if (!await _currentContext.AccessReports(request.OrganizationId))
{
throw new NotFoundException();
}
await AuthorizeAsync(request.OrganizationId);

await _dropPwdHealthReportAppCommand.DropPasswordHealthReportApplicationAsync(request);
}
Expand All @@ -222,4 +190,18 @@ public async Task<IEnumerable<PasskeyDirectoryResponseModel>> GetPasskeyDirector
Instructions = e.Instructions
});
}

private async Task AuthorizeAsync(Guid organizationId)
{
if (!await _currentContext.AccessReports(organizationId))
{
throw new NotFoundException();
}

var orgAbility = await _organizationAbilityCacheService.GetOrganizationAbilityAsync(organizationId);
if (orgAbility is null || !orgAbility.UseRiskInsights)
{
throw new BadRequestException("Your organization's plan does not support this feature.");
}
}
}
Loading
Loading