mirror of
https://github.com/bitwarden/server
synced 2025-12-06 00:03:34 +00:00
[PM-26967] Added new metric properties (#6519)
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
using Bit.Core.Context;
|
using Bit.Api.Dirt.Models.Response;
|
||||||
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
|
using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
|
||||||
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
@@ -61,8 +62,9 @@ public class OrganizationReportsController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
var latestReport = await _getOrganizationReportQuery.GetLatestOrganizationReportAsync(organizationId);
|
var latestReport = await _getOrganizationReportQuery.GetLatestOrganizationReportAsync(organizationId);
|
||||||
|
var response = latestReport == null ? null : new OrganizationReportResponseModel(latestReport);
|
||||||
|
|
||||||
return Ok(latestReport);
|
return Ok(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{organizationId}/{reportId}")]
|
[HttpGet("{organizationId}/{reportId}")]
|
||||||
@@ -102,7 +104,8 @@ public class OrganizationReportsController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
var report = await _addOrganizationReportCommand.AddOrganizationReportAsync(request);
|
var report = await _addOrganizationReportCommand.AddOrganizationReportAsync(request);
|
||||||
return Ok(report);
|
var response = report == null ? null : new OrganizationReportResponseModel(report);
|
||||||
|
return Ok(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPatch("{organizationId}/{reportId}")]
|
[HttpPatch("{organizationId}/{reportId}")]
|
||||||
@@ -119,7 +122,8 @@ public class OrganizationReportsController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
var updatedReport = await _updateOrganizationReportCommand.UpdateOrganizationReportAsync(request);
|
var updatedReport = await _updateOrganizationReportCommand.UpdateOrganizationReportAsync(request);
|
||||||
return Ok(updatedReport);
|
var response = new OrganizationReportResponseModel(updatedReport);
|
||||||
|
return Ok(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -182,10 +186,10 @@ public class OrganizationReportsController : Controller
|
|||||||
{
|
{
|
||||||
throw new BadRequestException("Report ID in the request body must match the route parameter");
|
throw new BadRequestException("Report ID in the request body must match the route parameter");
|
||||||
}
|
}
|
||||||
|
|
||||||
var updatedReport = await _updateOrganizationReportSummaryCommand.UpdateOrganizationReportSummaryAsync(request);
|
var updatedReport = await _updateOrganizationReportSummaryCommand.UpdateOrganizationReportSummaryAsync(request);
|
||||||
|
var response = new OrganizationReportResponseModel(updatedReport);
|
||||||
|
|
||||||
return Ok(updatedReport);
|
return Ok(response);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -228,7 +232,9 @@ public class OrganizationReportsController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
var updatedReport = await _updateOrganizationReportDataCommand.UpdateOrganizationReportDataAsync(request);
|
var updatedReport = await _updateOrganizationReportDataCommand.UpdateOrganizationReportDataAsync(request);
|
||||||
return Ok(updatedReport);
|
var response = new OrganizationReportResponseModel(updatedReport);
|
||||||
|
|
||||||
|
return Ok(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -265,7 +271,6 @@ public class OrganizationReportsController : Controller
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
||||||
if (!await _currentContext.AccessReports(organizationId))
|
if (!await _currentContext.AccessReports(organizationId))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
@@ -282,10 +287,9 @@ public class OrganizationReportsController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
var updatedReport = await _updateOrganizationReportApplicationDataCommand.UpdateOrganizationReportApplicationDataAsync(request);
|
var updatedReport = await _updateOrganizationReportApplicationDataCommand.UpdateOrganizationReportApplicationDataAsync(request);
|
||||||
|
var response = new OrganizationReportResponseModel(updatedReport);
|
||||||
|
|
||||||
|
return Ok(response);
|
||||||
|
|
||||||
return Ok(updatedReport);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex) when (!(ex is BadRequestException || ex is NotFoundException))
|
catch (Exception ex) when (!(ex is BadRequestException || ex is NotFoundException))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
using Bit.Core.Dirt.Entities;
|
||||||
|
|
||||||
|
namespace Bit.Api.Dirt.Models.Response;
|
||||||
|
|
||||||
|
public class OrganizationReportResponseModel
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public Guid OrganizationId { get; set; }
|
||||||
|
public string? ReportData { get; set; }
|
||||||
|
public string? ContentEncryptionKey { get; set; }
|
||||||
|
public string? SummaryData { get; set; }
|
||||||
|
public string? ApplicationData { get; set; }
|
||||||
|
public int? PasswordCount { get; set; }
|
||||||
|
public int? PasswordAtRiskCount { get; set; }
|
||||||
|
public int? MemberCount { get; set; }
|
||||||
|
public DateTime? CreationDate { get; set; } = null;
|
||||||
|
public DateTime? RevisionDate { get; set; } = null;
|
||||||
|
|
||||||
|
public OrganizationReportResponseModel(OrganizationReport organizationReport)
|
||||||
|
{
|
||||||
|
if (organizationReport == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Id = organizationReport.Id;
|
||||||
|
OrganizationId = organizationReport.OrganizationId;
|
||||||
|
ReportData = organizationReport.ReportData;
|
||||||
|
ContentEncryptionKey = organizationReport.ContentEncryptionKey;
|
||||||
|
SummaryData = organizationReport.SummaryData;
|
||||||
|
ApplicationData = organizationReport.ApplicationData;
|
||||||
|
PasswordCount = organizationReport.PasswordCount;
|
||||||
|
PasswordAtRiskCount = organizationReport.PasswordAtRiskCount;
|
||||||
|
MemberCount = organizationReport.MemberCount;
|
||||||
|
CreationDate = organizationReport.CreationDate;
|
||||||
|
RevisionDate = organizationReport.RevisionDate;
|
||||||
|
}
|
||||||
|
}
|
||||||
48
src/Core/Dirt/Models/Data/OrganizationReportMetricsData.cs
Normal file
48
src/Core/Dirt/Models/Data/OrganizationReportMetricsData.cs
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
||||||
|
|
||||||
|
namespace Bit.Core.Dirt.Reports.Models.Data;
|
||||||
|
|
||||||
|
public class OrganizationReportMetricsData
|
||||||
|
{
|
||||||
|
public Guid OrganizationId { get; set; }
|
||||||
|
public int? ApplicationCount { get; set; }
|
||||||
|
public int? ApplicationAtRiskCount { get; set; }
|
||||||
|
public int? CriticalApplicationCount { get; set; }
|
||||||
|
public int? CriticalApplicationAtRiskCount { get; set; }
|
||||||
|
public int? MemberCount { get; set; }
|
||||||
|
public int? MemberAtRiskCount { get; set; }
|
||||||
|
public int? CriticalMemberCount { get; set; }
|
||||||
|
public int? CriticalMemberAtRiskCount { get; set; }
|
||||||
|
public int? PasswordCount { get; set; }
|
||||||
|
public int? PasswordAtRiskCount { get; set; }
|
||||||
|
public int? CriticalPasswordCount { get; set; }
|
||||||
|
public int? CriticalPasswordAtRiskCount { get; set; }
|
||||||
|
|
||||||
|
public static OrganizationReportMetricsData From(Guid organizationId, OrganizationReportMetricsRequest? request)
|
||||||
|
{
|
||||||
|
if (request == null)
|
||||||
|
{
|
||||||
|
return new OrganizationReportMetricsData
|
||||||
|
{
|
||||||
|
OrganizationId = organizationId
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return new OrganizationReportMetricsData
|
||||||
|
{
|
||||||
|
OrganizationId = organizationId,
|
||||||
|
ApplicationCount = request.ApplicationCount,
|
||||||
|
ApplicationAtRiskCount = request.ApplicationAtRiskCount,
|
||||||
|
CriticalApplicationCount = request.CriticalApplicationCount,
|
||||||
|
CriticalApplicationAtRiskCount = request.CriticalApplicationAtRiskCount,
|
||||||
|
MemberCount = request.MemberCount,
|
||||||
|
MemberAtRiskCount = request.MemberAtRiskCount,
|
||||||
|
CriticalMemberCount = request.CriticalMemberCount,
|
||||||
|
CriticalMemberAtRiskCount = request.CriticalMemberAtRiskCount,
|
||||||
|
PasswordCount = request.PasswordCount,
|
||||||
|
PasswordAtRiskCount = request.PasswordAtRiskCount,
|
||||||
|
CriticalPasswordCount = request.CriticalPasswordCount,
|
||||||
|
CriticalPasswordAtRiskCount = request.CriticalPasswordAtRiskCount
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -35,14 +35,28 @@ public class AddOrganizationReportCommand : IAddOrganizationReportCommand
|
|||||||
throw new BadRequestException(errorMessage);
|
throw new BadRequestException(errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var requestMetrics = request.Metrics ?? new OrganizationReportMetricsRequest();
|
||||||
|
|
||||||
var organizationReport = new OrganizationReport
|
var organizationReport = new OrganizationReport
|
||||||
{
|
{
|
||||||
OrganizationId = request.OrganizationId,
|
OrganizationId = request.OrganizationId,
|
||||||
ReportData = request.ReportData,
|
ReportData = request.ReportData ?? string.Empty,
|
||||||
CreationDate = DateTime.UtcNow,
|
CreationDate = DateTime.UtcNow,
|
||||||
ContentEncryptionKey = request.ContentEncryptionKey,
|
ContentEncryptionKey = request.ContentEncryptionKey ?? string.Empty,
|
||||||
SummaryData = request.SummaryData,
|
SummaryData = request.SummaryData,
|
||||||
ApplicationData = request.ApplicationData,
|
ApplicationData = request.ApplicationData,
|
||||||
|
ApplicationCount = requestMetrics.ApplicationCount,
|
||||||
|
ApplicationAtRiskCount = requestMetrics.ApplicationAtRiskCount,
|
||||||
|
CriticalApplicationCount = requestMetrics.CriticalApplicationCount,
|
||||||
|
CriticalApplicationAtRiskCount = requestMetrics.CriticalApplicationAtRiskCount,
|
||||||
|
MemberCount = requestMetrics.MemberCount,
|
||||||
|
MemberAtRiskCount = requestMetrics.MemberAtRiskCount,
|
||||||
|
CriticalMemberCount = requestMetrics.CriticalMemberCount,
|
||||||
|
CriticalMemberAtRiskCount = requestMetrics.CriticalMemberAtRiskCount,
|
||||||
|
PasswordCount = requestMetrics.PasswordCount,
|
||||||
|
PasswordAtRiskCount = requestMetrics.PasswordAtRiskCount,
|
||||||
|
CriticalPasswordCount = requestMetrics.CriticalPasswordCount,
|
||||||
|
CriticalPasswordAtRiskCount = requestMetrics.CriticalPasswordAtRiskCount,
|
||||||
RevisionDate = DateTime.UtcNow
|
RevisionDate = DateTime.UtcNow
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
// FIXME: Update this file to be null safe and then delete the line below
|
namespace Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
|
||||||
|
|
||||||
public class AddOrganizationReportRequest
|
public class AddOrganizationReportRequest
|
||||||
{
|
{
|
||||||
public Guid OrganizationId { get; set; }
|
public Guid OrganizationId { get; set; }
|
||||||
public string ReportData { get; set; }
|
public string? ReportData { get; set; }
|
||||||
|
|
||||||
public string ContentEncryptionKey { get; set; }
|
public string? ContentEncryptionKey { get; set; }
|
||||||
|
|
||||||
public string SummaryData { get; set; }
|
public string? SummaryData { get; set; }
|
||||||
|
|
||||||
public string ApplicationData { get; set; }
|
public string? ApplicationData { get; set; }
|
||||||
|
|
||||||
|
public OrganizationReportMetricsRequest? Metrics { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
||||||
|
|
||||||
|
public class OrganizationReportMetricsRequest
|
||||||
|
{
|
||||||
|
[JsonPropertyName("totalApplicationCount")]
|
||||||
|
public int? ApplicationCount { get; set; } = null;
|
||||||
|
[JsonPropertyName("totalAtRiskApplicationCount")]
|
||||||
|
public int? ApplicationAtRiskCount { get; set; } = null;
|
||||||
|
[JsonPropertyName("totalCriticalApplicationCount")]
|
||||||
|
public int? CriticalApplicationCount { get; set; } = null;
|
||||||
|
[JsonPropertyName("totalCriticalAtRiskApplicationCount")]
|
||||||
|
public int? CriticalApplicationAtRiskCount { get; set; } = null;
|
||||||
|
[JsonPropertyName("totalMemberCount")]
|
||||||
|
public int? MemberCount { get; set; } = null;
|
||||||
|
[JsonPropertyName("totalAtRiskMemberCount")]
|
||||||
|
public int? MemberAtRiskCount { get; set; } = null;
|
||||||
|
[JsonPropertyName("totalCriticalMemberCount")]
|
||||||
|
public int? CriticalMemberCount { get; set; } = null;
|
||||||
|
[JsonPropertyName("totalCriticalAtRiskMemberCount")]
|
||||||
|
public int? CriticalMemberAtRiskCount { get; set; } = null;
|
||||||
|
[JsonPropertyName("totalPasswordCount")]
|
||||||
|
public int? PasswordCount { get; set; } = null;
|
||||||
|
[JsonPropertyName("totalAtRiskPasswordCount")]
|
||||||
|
public int? PasswordAtRiskCount { get; set; } = null;
|
||||||
|
[JsonPropertyName("totalCriticalPasswordCount")]
|
||||||
|
public int? CriticalPasswordCount { get; set; } = null;
|
||||||
|
[JsonPropertyName("totalCriticalAtRiskPasswordCount")]
|
||||||
|
public int? CriticalPasswordAtRiskCount { get; set; } = null;
|
||||||
|
}
|
||||||
@@ -1,11 +1,8 @@
|
|||||||
// FIXME: Update this file to be null safe and then delete the line below
|
namespace Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
|
||||||
|
|
||||||
public class UpdateOrganizationReportApplicationDataRequest
|
public class UpdateOrganizationReportApplicationDataRequest
|
||||||
{
|
{
|
||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
public Guid OrganizationId { get; set; }
|
public Guid OrganizationId { get; set; }
|
||||||
public string ApplicationData { get; set; }
|
public string? ApplicationData { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
// FIXME: Update this file to be null safe and then delete the line below
|
namespace Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
|
||||||
|
|
||||||
public class UpdateOrganizationReportSummaryRequest
|
public class UpdateOrganizationReportSummaryRequest
|
||||||
{
|
{
|
||||||
public Guid OrganizationId { get; set; }
|
public Guid OrganizationId { get; set; }
|
||||||
public Guid ReportId { get; set; }
|
public Guid ReportId { get; set; }
|
||||||
public string SummaryData { get; set; }
|
public string? SummaryData { get; set; }
|
||||||
|
public OrganizationReportMetricsRequest? Metrics { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ public class UpdateOrganizationReportApplicationDataCommand : IUpdateOrganizatio
|
|||||||
throw new BadRequestException("Organization report does not belong to the specified organization");
|
throw new BadRequestException("Organization report does not belong to the specified organization");
|
||||||
}
|
}
|
||||||
|
|
||||||
var updatedReport = await _organizationReportRepo.UpdateApplicationDataAsync(request.OrganizationId, request.Id, request.ApplicationData);
|
var updatedReport = await _organizationReportRepo.UpdateApplicationDataAsync(request.OrganizationId, request.Id, request.ApplicationData ?? string.Empty);
|
||||||
|
|
||||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Successfully updated organization report application data {reportId} for organization {organizationId}",
|
_logger.LogInformation(Constants.BypassFiltersEventId, "Successfully updated organization report application data {reportId} for organization {organizationId}",
|
||||||
request.Id, request.OrganizationId);
|
request.Id, request.OrganizationId);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Bit.Core.Dirt.Entities;
|
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.Interfaces;
|
||||||
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
||||||
using Bit.Core.Dirt.Repositories;
|
using Bit.Core.Dirt.Repositories;
|
||||||
@@ -53,7 +54,8 @@ public class UpdateOrganizationReportSummaryCommand : IUpdateOrganizationReportS
|
|||||||
throw new BadRequestException("Organization report does not belong to the specified organization");
|
throw new BadRequestException("Organization report does not belong to the specified organization");
|
||||||
}
|
}
|
||||||
|
|
||||||
var updatedReport = await _organizationReportRepo.UpdateSummaryDataAsync(request.OrganizationId, request.ReportId, request.SummaryData);
|
await _organizationReportRepo.UpdateMetricsAsync(request.ReportId, OrganizationReportMetricsData.From(request.OrganizationId, request.Metrics));
|
||||||
|
var updatedReport = await _organizationReportRepo.UpdateSummaryDataAsync(request.OrganizationId, request.ReportId, request.SummaryData ?? string.Empty);
|
||||||
|
|
||||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Successfully updated organization report summary {reportId} for organization {organizationId}",
|
_logger.LogInformation(Constants.BypassFiltersEventId, "Successfully updated organization report summary {reportId} for organization {organizationId}",
|
||||||
request.ReportId, request.OrganizationId);
|
request.ReportId, request.OrganizationId);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Bit.Core.Dirt.Entities;
|
using Bit.Core.Dirt.Entities;
|
||||||
using Bit.Core.Dirt.Models.Data;
|
using Bit.Core.Dirt.Models.Data;
|
||||||
|
using Bit.Core.Dirt.Reports.Models.Data;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
|
|
||||||
namespace Bit.Core.Dirt.Repositories;
|
namespace Bit.Core.Dirt.Repositories;
|
||||||
@@ -21,5 +22,8 @@ public interface IOrganizationReportRepository : IRepository<OrganizationReport,
|
|||||||
// ApplicationData methods
|
// ApplicationData methods
|
||||||
Task<OrganizationReportApplicationDataResponse> GetApplicationDataAsync(Guid reportId);
|
Task<OrganizationReportApplicationDataResponse> GetApplicationDataAsync(Guid reportId);
|
||||||
Task<OrganizationReport> UpdateApplicationDataAsync(Guid orgId, Guid reportId, string applicationData);
|
Task<OrganizationReport> UpdateApplicationDataAsync(Guid orgId, Guid reportId, string applicationData);
|
||||||
|
|
||||||
|
// Metrics methods
|
||||||
|
Task UpdateMetricsAsync(Guid reportId, OrganizationReportMetricsData metrics);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
using System.Data;
|
using System.Data;
|
||||||
using Bit.Core.Dirt.Entities;
|
using Bit.Core.Dirt.Entities;
|
||||||
using Bit.Core.Dirt.Models.Data;
|
using Bit.Core.Dirt.Models.Data;
|
||||||
|
using Bit.Core.Dirt.Reports.Models.Data;
|
||||||
using Bit.Core.Dirt.Repositories;
|
using Bit.Core.Dirt.Repositories;
|
||||||
using Bit.Core.Settings;
|
using Bit.Core.Settings;
|
||||||
using Bit.Infrastructure.Dapper.Repositories;
|
using Bit.Infrastructure.Dapper.Repositories;
|
||||||
@@ -173,4 +174,31 @@ public class OrganizationReportRepository : Repository<OrganizationReport, Guid>
|
|||||||
commandType: CommandType.StoredProcedure);
|
commandType: CommandType.StoredProcedure);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task UpdateMetricsAsync(Guid reportId, OrganizationReportMetricsData metrics)
|
||||||
|
{
|
||||||
|
using var connection = new SqlConnection(ConnectionString);
|
||||||
|
var parameters = new
|
||||||
|
{
|
||||||
|
Id = reportId,
|
||||||
|
ApplicationCount = metrics.ApplicationCount,
|
||||||
|
ApplicationAtRiskCount = metrics.ApplicationAtRiskCount,
|
||||||
|
CriticalApplicationCount = metrics.CriticalApplicationCount,
|
||||||
|
CriticalApplicationAtRiskCount = metrics.CriticalApplicationAtRiskCount,
|
||||||
|
MemberCount = metrics.MemberCount,
|
||||||
|
MemberAtRiskCount = metrics.MemberAtRiskCount,
|
||||||
|
CriticalMemberCount = metrics.CriticalMemberCount,
|
||||||
|
CriticalMemberAtRiskCount = metrics.CriticalMemberAtRiskCount,
|
||||||
|
PasswordCount = metrics.PasswordCount,
|
||||||
|
PasswordAtRiskCount = metrics.PasswordAtRiskCount,
|
||||||
|
CriticalPasswordCount = metrics.CriticalPasswordCount,
|
||||||
|
CriticalPasswordAtRiskCount = metrics.CriticalPasswordAtRiskCount,
|
||||||
|
RevisionDate = DateTime.UtcNow
|
||||||
|
};
|
||||||
|
|
||||||
|
await connection.ExecuteAsync(
|
||||||
|
$"[{Schema}].[OrganizationReport_UpdateMetrics]",
|
||||||
|
parameters,
|
||||||
|
commandType: CommandType.StoredProcedure);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using Bit.Core.Dirt.Entities;
|
using Bit.Core.Dirt.Entities;
|
||||||
using Bit.Core.Dirt.Models.Data;
|
using Bit.Core.Dirt.Models.Data;
|
||||||
|
using Bit.Core.Dirt.Reports.Models.Data;
|
||||||
using Bit.Core.Dirt.Repositories;
|
using Bit.Core.Dirt.Repositories;
|
||||||
using Bit.Infrastructure.EntityFramework.Repositories;
|
using Bit.Infrastructure.EntityFramework.Repositories;
|
||||||
using LinqToDB;
|
using LinqToDB;
|
||||||
@@ -184,4 +185,31 @@ public class OrganizationReportRepository :
|
|||||||
return Mapper.Map<OrganizationReport>(updatedReport);
|
return Mapper.Map<OrganizationReport>(updatedReport);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task UpdateMetricsAsync(Guid reportId, OrganizationReportMetricsData metrics)
|
||||||
|
{
|
||||||
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
|
{
|
||||||
|
var dbContext = GetDatabaseContext(scope);
|
||||||
|
|
||||||
|
return dbContext.OrganizationReports
|
||||||
|
.Where(p => p.Id == reportId)
|
||||||
|
.UpdateAsync(p => new Models.OrganizationReport
|
||||||
|
{
|
||||||
|
ApplicationCount = metrics.ApplicationCount,
|
||||||
|
ApplicationAtRiskCount = metrics.ApplicationAtRiskCount,
|
||||||
|
CriticalApplicationCount = metrics.CriticalApplicationCount,
|
||||||
|
CriticalApplicationAtRiskCount = metrics.CriticalApplicationAtRiskCount,
|
||||||
|
MemberCount = metrics.MemberCount,
|
||||||
|
MemberAtRiskCount = metrics.MemberAtRiskCount,
|
||||||
|
CriticalMemberCount = metrics.CriticalMemberCount,
|
||||||
|
CriticalMemberAtRiskCount = metrics.CriticalMemberAtRiskCount,
|
||||||
|
PasswordCount = metrics.PasswordCount,
|
||||||
|
PasswordAtRiskCount = metrics.PasswordAtRiskCount,
|
||||||
|
CriticalPasswordCount = metrics.CriticalPasswordCount,
|
||||||
|
CriticalPasswordAtRiskCount = metrics.CriticalPasswordAtRiskCount,
|
||||||
|
RevisionDate = DateTime.UtcNow
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
CREATE PROCEDURE [dbo].[OrganizationReport_UpdateMetrics]
|
||||||
|
@Id UNIQUEIDENTIFIER,
|
||||||
|
@ApplicationCount INT,
|
||||||
|
@ApplicationAtRiskCount INT,
|
||||||
|
@CriticalApplicationCount INT,
|
||||||
|
@CriticalApplicationAtRiskCount INT,
|
||||||
|
@MemberCount INT,
|
||||||
|
@MemberAtRiskCount INT,
|
||||||
|
@CriticalMemberCount INT,
|
||||||
|
@CriticalMemberAtRiskCount INT,
|
||||||
|
@PasswordCount INT,
|
||||||
|
@PasswordAtRiskCount INT,
|
||||||
|
@CriticalPasswordCount INT,
|
||||||
|
@CriticalPasswordAtRiskCount INT,
|
||||||
|
@RevisionDate DATETIME2(7)
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON;
|
||||||
|
|
||||||
|
UPDATE
|
||||||
|
[dbo].[OrganizationReport]
|
||||||
|
SET
|
||||||
|
[ApplicationCount] = @ApplicationCount,
|
||||||
|
[ApplicationAtRiskCount] = @ApplicationAtRiskCount,
|
||||||
|
[CriticalApplicationCount] = @CriticalApplicationCount,
|
||||||
|
[CriticalApplicationAtRiskCount] = @CriticalApplicationAtRiskCount,
|
||||||
|
[MemberCount] = @MemberCount,
|
||||||
|
[MemberAtRiskCount] = @MemberAtRiskCount,
|
||||||
|
[CriticalMemberCount] = @CriticalMemberCount,
|
||||||
|
[CriticalMemberAtRiskCount] = @CriticalMemberAtRiskCount,
|
||||||
|
[PasswordCount] = @PasswordCount,
|
||||||
|
[PasswordAtRiskCount] = @PasswordAtRiskCount,
|
||||||
|
[CriticalPasswordCount] = @CriticalPasswordCount,
|
||||||
|
[CriticalPasswordAtRiskCount] = @CriticalPasswordAtRiskCount,
|
||||||
|
[RevisionDate] = @RevisionDate
|
||||||
|
WHERE
|
||||||
|
[Id] = @Id
|
||||||
|
|
||||||
|
END
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using Bit.Api.Dirt.Controllers;
|
using Bit.Api.Dirt.Controllers;
|
||||||
|
using Bit.Api.Dirt.Models.Response;
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Dirt.Entities;
|
using Bit.Core.Dirt.Entities;
|
||||||
using Bit.Core.Dirt.Models.Data;
|
using Bit.Core.Dirt.Models.Data;
|
||||||
@@ -39,7 +40,8 @@ public class OrganizationReportControllerTests
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var okResult = Assert.IsType<OkObjectResult>(result);
|
var okResult = Assert.IsType<OkObjectResult>(result);
|
||||||
Assert.Equal(expectedReport, okResult.Value);
|
var expectedResponse = new OrganizationReportResponseModel(expectedReport);
|
||||||
|
Assert.Equivalent(expectedResponse, okResult.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
@@ -262,7 +264,8 @@ public class OrganizationReportControllerTests
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var okResult = Assert.IsType<OkObjectResult>(result);
|
var okResult = Assert.IsType<OkObjectResult>(result);
|
||||||
Assert.Equal(expectedReport, okResult.Value);
|
var expectedResponse = new OrganizationReportResponseModel(expectedReport);
|
||||||
|
Assert.Equivalent(expectedResponse, okResult.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
@@ -365,7 +368,8 @@ public class OrganizationReportControllerTests
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var okResult = Assert.IsType<OkObjectResult>(result);
|
var okResult = Assert.IsType<OkObjectResult>(result);
|
||||||
Assert.Equal(expectedReport, okResult.Value);
|
var expectedResponse = new OrganizationReportResponseModel(expectedReport);
|
||||||
|
Assert.Equivalent(expectedResponse, okResult.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
@@ -597,7 +601,8 @@ public class OrganizationReportControllerTests
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var okResult = Assert.IsType<OkObjectResult>(result);
|
var okResult = Assert.IsType<OkObjectResult>(result);
|
||||||
Assert.Equal(expectedReport, okResult.Value);
|
var expectedResponse = new OrganizationReportResponseModel(expectedReport);
|
||||||
|
Assert.Equivalent(expectedResponse, okResult.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
@@ -812,7 +817,8 @@ public class OrganizationReportControllerTests
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var okResult = Assert.IsType<OkObjectResult>(result);
|
var okResult = Assert.IsType<OkObjectResult>(result);
|
||||||
Assert.Equal(expectedReport, okResult.Value);
|
var expectedResponse = new OrganizationReportResponseModel(expectedReport);
|
||||||
|
Assert.Equivalent(expectedResponse, okResult.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
@@ -1050,7 +1056,8 @@ public class OrganizationReportControllerTests
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var okResult = Assert.IsType<OkObjectResult>(result);
|
var okResult = Assert.IsType<OkObjectResult>(result);
|
||||||
Assert.Equal(expectedReport, okResult.Value);
|
var expectedResponse = new OrganizationReportResponseModel(expectedReport);
|
||||||
|
Assert.Equivalent(expectedResponse, okResult.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using AutoFixture;
|
using AutoFixture;
|
||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.Dirt.Entities;
|
using Bit.Core.Dirt.Entities;
|
||||||
|
using Bit.Core.Dirt.Reports.Models.Data;
|
||||||
using Bit.Core.Dirt.Repositories;
|
using Bit.Core.Dirt.Repositories;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Test.AutoFixture.Attributes;
|
using Bit.Core.Test.AutoFixture.Attributes;
|
||||||
@@ -489,6 +490,49 @@ public class OrganizationReportRepositoryTests
|
|||||||
Assert.Null(result);
|
Assert.Null(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CiSkippedTheory, EfOrganizationReportAutoData]
|
||||||
|
public async Task UpdateMetricsAsync_ShouldUpdateMetricsCorrectly(
|
||||||
|
OrganizationReportRepository sqlOrganizationReportRepo,
|
||||||
|
SqlRepo.OrganizationRepository sqlOrganizationRepo)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var (org, report) = await CreateOrganizationAndReportAsync(sqlOrganizationRepo, sqlOrganizationReportRepo);
|
||||||
|
var metrics = new OrganizationReportMetricsData
|
||||||
|
{
|
||||||
|
ApplicationCount = 10,
|
||||||
|
ApplicationAtRiskCount = 2,
|
||||||
|
CriticalApplicationCount = 5,
|
||||||
|
CriticalApplicationAtRiskCount = 1,
|
||||||
|
MemberCount = 20,
|
||||||
|
MemberAtRiskCount = 4,
|
||||||
|
CriticalMemberCount = 10,
|
||||||
|
CriticalMemberAtRiskCount = 2,
|
||||||
|
PasswordCount = 100,
|
||||||
|
PasswordAtRiskCount = 15,
|
||||||
|
CriticalPasswordCount = 50,
|
||||||
|
CriticalPasswordAtRiskCount = 5
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await sqlOrganizationReportRepo.UpdateMetricsAsync(report.Id, metrics);
|
||||||
|
var updatedReport = await sqlOrganizationReportRepo.GetByIdAsync(report.Id);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(metrics.ApplicationCount, updatedReport.ApplicationCount);
|
||||||
|
Assert.Equal(metrics.ApplicationAtRiskCount, updatedReport.ApplicationAtRiskCount);
|
||||||
|
Assert.Equal(metrics.CriticalApplicationCount, updatedReport.CriticalApplicationCount);
|
||||||
|
Assert.Equal(metrics.CriticalApplicationAtRiskCount, updatedReport.CriticalApplicationAtRiskCount);
|
||||||
|
Assert.Equal(metrics.MemberCount, updatedReport.MemberCount);
|
||||||
|
Assert.Equal(metrics.MemberAtRiskCount, updatedReport.MemberAtRiskCount);
|
||||||
|
Assert.Equal(metrics.CriticalMemberCount, updatedReport.CriticalMemberCount);
|
||||||
|
Assert.Equal(metrics.CriticalMemberAtRiskCount, updatedReport.CriticalMemberAtRiskCount);
|
||||||
|
Assert.Equal(metrics.PasswordCount, updatedReport.PasswordCount);
|
||||||
|
Assert.Equal(metrics.PasswordAtRiskCount, updatedReport.PasswordAtRiskCount);
|
||||||
|
Assert.Equal(metrics.CriticalPasswordCount, updatedReport.CriticalPasswordCount);
|
||||||
|
Assert.Equal(metrics.CriticalPasswordAtRiskCount, updatedReport.CriticalPasswordAtRiskCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private async Task<(Organization, OrganizationReport)> CreateOrganizationAndReportAsync(
|
private async Task<(Organization, OrganizationReport)> CreateOrganizationAndReportAsync(
|
||||||
IOrganizationRepository orgRepo,
|
IOrganizationRepository orgRepo,
|
||||||
IOrganizationReportRepository orgReportRepo)
|
IOrganizationReportRepository orgReportRepo)
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
CREATE OR ALTER PROCEDURE [dbo].[OrganizationReport_UpdateMetrics]
|
||||||
|
@Id UNIQUEIDENTIFIER,
|
||||||
|
@ApplicationCount INT,
|
||||||
|
@ApplicationAtRiskCount INT,
|
||||||
|
@CriticalApplicationCount INT,
|
||||||
|
@CriticalApplicationAtRiskCount INT,
|
||||||
|
@MemberCount INT,
|
||||||
|
@MemberAtRiskCount INT,
|
||||||
|
@CriticalMemberCount INT,
|
||||||
|
@CriticalMemberAtRiskCount INT,
|
||||||
|
@PasswordCount INT,
|
||||||
|
@PasswordAtRiskCount INT,
|
||||||
|
@CriticalPasswordCount INT,
|
||||||
|
@CriticalPasswordAtRiskCount INT,
|
||||||
|
@RevisionDate DATETIME2(7)
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON;
|
||||||
|
|
||||||
|
UPDATE
|
||||||
|
[dbo].[OrganizationReport]
|
||||||
|
SET
|
||||||
|
[ApplicationCount] = @ApplicationCount,
|
||||||
|
[ApplicationAtRiskCount] = @ApplicationAtRiskCount,
|
||||||
|
[CriticalApplicationCount] = @CriticalApplicationCount,
|
||||||
|
[CriticalApplicationAtRiskCount] = @CriticalApplicationAtRiskCount,
|
||||||
|
[MemberCount] = @MemberCount,
|
||||||
|
[MemberAtRiskCount] = @MemberAtRiskCount,
|
||||||
|
[CriticalMemberCount] = @CriticalMemberCount,
|
||||||
|
[CriticalMemberAtRiskCount] = @CriticalMemberAtRiskCount,
|
||||||
|
[PasswordCount] = @PasswordCount,
|
||||||
|
[PasswordAtRiskCount] = @PasswordAtRiskCount,
|
||||||
|
[CriticalPasswordCount] = @CriticalPasswordCount,
|
||||||
|
[CriticalPasswordAtRiskCount] = @CriticalPasswordAtRiskCount,
|
||||||
|
[RevisionDate] = @RevisionDate
|
||||||
|
WHERE
|
||||||
|
[Id] = @Id
|
||||||
|
|
||||||
|
END
|
||||||
Reference in New Issue
Block a user