Skip to content
Closed
12 changes: 0 additions & 12 deletions src/Api/Dirt/Controllers/OrganizationReportsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ IUpdateOrganizationReportApplicationDataCommand updateOrganizationReportApplicat
_updateOrganizationReportApplicationDataCommand = updateOrganizationReportApplicationDataCommand;
}

#region Whole OrganizationReport Endpoints

[HttpGet("{organizationId}/latest")]
public async Task<IActionResult> GetLatestOrganizationReportAsync(Guid organizationId)
Expand Down Expand Up @@ -126,10 +125,6 @@ public async Task<IActionResult> UpdateOrganizationReportAsync(Guid organization
return Ok(response);
}

#endregion

# region SummaryData Field Endpoints

[HttpGet("{organizationId}/data/summary")]
public async Task<IActionResult> GetOrganizationReportSummaryDataByDateRangeAsync(
Guid organizationId, [FromQuery] DateTime startDate, [FromQuery] DateTime endDate)
Expand Down Expand Up @@ -191,9 +186,7 @@ public async Task<IActionResult> UpdateOrganizationReportSummaryAsync(Guid organ

return Ok(response);
}
#endregion

#region ReportData Field Endpoints

[HttpGet("{organizationId}/data/report/{reportId}")]
public async Task<IActionResult> GetOrganizationReportDataAsync(Guid organizationId, Guid reportId)
Expand Down Expand Up @@ -237,10 +230,6 @@ public async Task<IActionResult> UpdateOrganizationReportDataAsync(Guid organiza
return Ok(response);
}

#endregion

#region ApplicationData Field Endpoints

[HttpGet("{organizationId}/data/application/{reportId}")]
public async Task<IActionResult> GetOrganizationReportApplicationDataAsync(Guid organizationId, Guid reportId)
{
Expand Down Expand Up @@ -297,5 +286,4 @@ public async Task<IActionResult> UpdateOrganizationReportApplicationDataAsync(Gu
}
}

#endregion
}
109 changes: 109 additions & 0 deletions src/Api/Dirt/Controllers/OrganizationReportsV2Controller.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
using Bit.Api.Dirt.Models.Response;
using Bit.Core.Context;
using Bit.Core.Dirt.Models.Data;
using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
using Bit.Core.Exceptions;
using Bit.Core.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace Bit.Api.Dirt.Controllers;

[Route("reports/v2/organizations")]
[Authorize("Application")]
public class OrganizationReportsV2Controller : Controller
{
private readonly ICurrentContext _currentContext;
private readonly IApplicationCacheService _applicationCacheService;
private readonly IGetOrganizationReportSummaryDataByDateRangeV2Query _getSummaryByDateRangeQuery;
private readonly IGetOrganizationReportSummaryDataV2Query _getSummaryDataQuery;
private readonly IUpdateOrganizationReportSummaryV2Command _updateSummaryCommand;

public OrganizationReportsV2Controller(
ICurrentContext currentContext,
IApplicationCacheService applicationCacheService,
IGetOrganizationReportSummaryDataByDateRangeV2Query getSummaryByDateRangeQuery,
IGetOrganizationReportSummaryDataV2Query getSummaryDataQuery,
IUpdateOrganizationReportSummaryV2Command updateSummaryCommand)
{
_currentContext = currentContext;
_applicationCacheService = applicationCacheService;
_getSummaryByDateRangeQuery = getSummaryByDateRangeQuery;
_getSummaryDataQuery = getSummaryDataQuery;
_updateSummaryCommand = updateSummaryCommand;
}

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

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

[HttpGet("{organizationId}/data/summary")]
public async Task<IEnumerable<OrganizationReportSummaryDataResponse>> GetOrganizationReportSummaryDataByDateRangeV2Async(
Guid organizationId, [FromQuery] DateTime startDate, [FromQuery] DateTime endDate)
{
if (organizationId == Guid.Empty) throw new BadRequestException("OrganizationId is required.");

if (startDate == default) throw new BadRequestException("Start date is required.");

if (endDate == default) throw new BadRequestException("End date is required.");

if (startDate > endDate) throw new BadRequestException("Start date must be before or equal to end date.");

await AuthorizeAsync(organizationId);

return await _getSummaryByDateRangeQuery
.GetSummaryDataByDateRangeAsync(organizationId, startDate, endDate);
}

[HttpGet("{organizationId}/data/summary/{reportId}")]
public async Task<OrganizationReportSummaryDataResponse> GetOrganizationReportSummaryV2Async(
Guid organizationId, Guid reportId)
{
if (organizationId == Guid.Empty) throw new BadRequestException("OrganizationId is required.");

if (reportId == Guid.Empty) throw new BadRequestException("ReportId is required.");

await AuthorizeAsync(organizationId);

var summaryData = await _getSummaryDataQuery
.GetSummaryDataAsync(organizationId, reportId);

if (summaryData == null) throw new NotFoundException("Organization report summary data not found.");

return summaryData;
}

[HttpPatch("{organizationId}/data/summary/{reportId}")]
public async Task<OrganizationReportResponseModel> UpdateOrganizationReportSummaryV2Async(
Guid organizationId, Guid reportId,
[FromBody] UpdateOrganizationReportSummaryRequest request)
{
if (organizationId == Guid.Empty) throw new BadRequestException("OrganizationId is required.");

if (reportId == Guid.Empty) throw new BadRequestException("ReportId is required.");

if (request.OrganizationId != organizationId) throw new BadRequestException("Organization ID in the request body must match the route parameter");

if (request.ReportId != reportId) throw new BadRequestException("Report ID in the request body must match the route parameter");

if (string.IsNullOrWhiteSpace(request.SummaryData)) throw new BadRequestException("Summary Data is required");

await AuthorizeAsync(organizationId);

var updatedReport = await _updateSummaryCommand
.UpdateSummaryAsync(request);

return new OrganizationReportResponseModel(updatedReport);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class OrganizationReportMetricsData
public int? CriticalPasswordCount { get; set; }
public int? CriticalPasswordAtRiskCount { get; set; }

public static OrganizationReportMetricsData From(Guid organizationId, OrganizationReportMetricsRequest? request)
public static OrganizationReportMetricsData From(Guid organizationId, OrganizationReportMetrics? request)
{
if (request == null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public async Task<OrganizationReport> AddOrganizationReportAsync(AddOrganization
throw new BadRequestException(errorMessage);
}

var requestMetrics = request.Metrics ?? new OrganizationReportMetricsRequest();
var requestMetrics = request.Metrics ?? new OrganizationReportMetrics();

var organizationReport = new OrganizationReport
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Bit.Core.Dirt.Models.Data;
using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
using Bit.Core.Dirt.Repositories;
using Microsoft.Extensions.Logging;

namespace Bit.Core.Dirt.Reports.ReportFeatures;

public class GetOrganizationReportSummaryDataByDateRangeV2Query : IGetOrganizationReportSummaryDataByDateRangeV2Query
{
private readonly IOrganizationReportRepository _organizationReportRepo;
private readonly ILogger<GetOrganizationReportSummaryDataByDateRangeV2Query> _logger;

public GetOrganizationReportSummaryDataByDateRangeV2Query(
IOrganizationReportRepository organizationReportRepo,
ILogger<GetOrganizationReportSummaryDataByDateRangeV2Query> logger)
{
_organizationReportRepo = organizationReportRepo;
_logger = logger;
}

public async Task<IEnumerable<OrganizationReportSummaryDataResponse>> GetSummaryDataByDateRangeAsync(
Guid organizationId, DateTime startDate, DateTime endDate)
{
var summaryDataList = await _organizationReportRepo
.GetSummaryDataByDateRangeAsync(organizationId, startDate, endDate);

return summaryDataList ?? Enumerable.Empty<OrganizationReportSummaryDataResponse>();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using Bit.Core.Dirt.Models.Data;
using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
using Bit.Core.Dirt.Repositories;
using Microsoft.Extensions.Logging;

namespace Bit.Core.Dirt.Reports.ReportFeatures;

public class GetOrganizationReportSummaryDataV2Query : IGetOrganizationReportSummaryDataV2Query
{
private readonly IOrganizationReportRepository _organizationReportRepo;
private readonly ILogger<GetOrganizationReportSummaryDataV2Query> _logger;

public GetOrganizationReportSummaryDataV2Query(
IOrganizationReportRepository organizationReportRepo,
ILogger<GetOrganizationReportSummaryDataV2Query> logger)
{
_organizationReportRepo = organizationReportRepo;
_logger = logger;
}

public async Task<OrganizationReportSummaryDataResponse?> GetSummaryDataAsync(
Guid organizationId, Guid reportId)
{
var report = await _organizationReportRepo.GetByIdAsync(reportId);
if (report == null || report.OrganizationId != organizationId)
{
return null;
}

return new OrganizationReportSummaryDataResponse
{
SummaryData = report.SummaryData
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Bit.Core.Dirt.Models.Data;

namespace Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;

public interface IGetOrganizationReportSummaryDataByDateRangeV2Query
{
Task<IEnumerable<OrganizationReportSummaryDataResponse>> GetSummaryDataByDateRangeAsync(
Guid organizationId, DateTime startDate, DateTime endDate);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Bit.Core.Dirt.Models.Data;

namespace Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;

public interface IGetOrganizationReportSummaryDataV2Query
{
Task<OrganizationReportSummaryDataResponse?> GetSummaryDataAsync(
Guid organizationId, Guid reportId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Bit.Core.Dirt.Entities;
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;

namespace Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;

public interface IUpdateOrganizationReportSummaryV2Command
{
Task<OrganizationReport> UpdateSummaryAsync(
UpdateOrganizationReportSummaryRequest request);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
using Bit.Core.Dirt.Reports.ReportFeatures.OrganizationReportMembers.Interfaces;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;

namespace Bit.Core.Dirt.Reports.ReportFeatures;

Expand All @@ -23,5 +24,10 @@ public static void AddReportingServices(this IServiceCollection services)
services.AddScoped<IUpdateOrganizationReportDataCommand, UpdateOrganizationReportDataCommand>();
services.AddScoped<IGetOrganizationReportApplicationDataQuery, GetOrganizationReportApplicationDataQuery>();
services.AddScoped<IUpdateOrganizationReportApplicationDataCommand, UpdateOrganizationReportApplicationDataCommand>();

// v2 summary data
services.TryAddScoped<IGetOrganizationReportSummaryDataByDateRangeV2Query, GetOrganizationReportSummaryDataByDateRangeV2Query>();
services.TryAddScoped<IGetOrganizationReportSummaryDataV2Query, GetOrganizationReportSummaryDataV2Query>();
services.TryAddScoped<IUpdateOrganizationReportSummaryV2Command, UpdateOrganizationReportSummaryV2Command>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ public class AddOrganizationReportRequest

public string? ApplicationData { get; set; }

public OrganizationReportMetricsRequest? Metrics { get; set; }
public OrganizationReportMetrics? Metrics { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Bit.Core.Dirt.Reports.ReportFeatures.Requests;

public class OrganizationReportMetricsRequest
public class OrganizationReportMetrics
{
[JsonPropertyName("totalApplicationCount")]
public int? ApplicationCount { get; set; } = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ public class UpdateOrganizationReportSummaryRequest
public Guid OrganizationId { get; set; }
public Guid ReportId { get; set; }
public string? SummaryData { get; set; }
public OrganizationReportMetricsRequest? Metrics { get; set; }
public OrganizationReportMetrics? Metrics { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using Bit.Core.Dirt.Entities;
using Bit.Core.Dirt.Reports.Models.Data;
using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
using Bit.Core.Dirt.Repositories;
using Bit.Core.Exceptions;
using Microsoft.Extensions.Logging;

namespace Bit.Core.Dirt.Reports.ReportFeatures;

public class UpdateOrganizationReportSummaryV2Command : IUpdateOrganizationReportSummaryV2Command
{
private readonly IOrganizationReportRepository _organizationReportRepo;
private readonly ILogger<UpdateOrganizationReportSummaryV2Command> _logger;

public UpdateOrganizationReportSummaryV2Command(
IOrganizationReportRepository organizationReportRepository,
ILogger<UpdateOrganizationReportSummaryV2Command> logger)
{
_organizationReportRepo = organizationReportRepository;
_logger = logger;
}

public async Task<OrganizationReport> UpdateSummaryAsync(
UpdateOrganizationReportSummaryRequest request)
{
var existingReport = await _organizationReportRepo.GetByIdAsync(request.ReportId);
if (existingReport == null)
{
throw new NotFoundException("Organization report not found");
}

if (existingReport.OrganizationId != request.OrganizationId)
{
throw new NotFoundException("Organization report not found");
}

await _organizationReportRepo.UpdateMetricsAsync(
request.ReportId, OrganizationReportMetricsData.From(request.OrganizationId, request.Metrics));

return await _organizationReportRepo.UpdateSummaryDataAsync(
request.OrganizationId, request.ReportId, request.SummaryData ?? string.Empty);
}
}
Loading
Loading