-
Notifications
You must be signed in to change notification settings - Fork 1.5k
PM-31920 adding the whole report endpoints v2 #7079
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
prograhamming
wants to merge
7
commits into
main
Choose a base branch
from
dirt/pm-31920-whole-report-data-v2-endpoints-access-intelligence
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+1,558
β48
Draft
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
99bc641
pm-31920 adding the whole report endpoints v2
prograhamming 8002621
Merge branch 'main' into dirt/pm-31920-whole-report-data-v2-endpointsβ¦
prograhamming d9b1bb7
Merge branch 'main' into dirt/pm-31920-whole-report-data-v2-endpointsβ¦
prograhamming 5d3f7b7
Merge branch 'main' into dirt/pm-31920-whole-report-data-v2-endpointsβ¦
prograhamming 65092cb
pm-31920 changing approach to match others in codebase
prograhamming b6def67
Merge branch 'dirt/pm-31920-whole-report-data-v2-endpoints-access-intβ¦
prograhamming ffa2e80
Merge branch 'main' into dirt/pm-31920-whole-report-data-v2-endpointsβ¦
prograhamming File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
191 changes: 191 additions & 0 deletions
191
src/Api/Dirt/Controllers/OrganizationReportsV2Controller.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,191 @@ | ||
| ο»Ώusing Bit.Api.Dirt.Models.Response; | ||
| using Bit.Api.Utilities; | ||
| using Bit.Core; | ||
| using Bit.Core.Context; | ||
| using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces; | ||
| using Bit.Core.Dirt.Reports.ReportFeatures.Requests; | ||
| using Bit.Core.Dirt.Reports.Services; | ||
| using Bit.Core.Dirt.Repositories; | ||
| using Bit.Core.Exceptions; | ||
| using Bit.Core.Services; | ||
| using Bit.Core.Utilities; | ||
| 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 IOrganizationReportStorageService _storageService; | ||
| private readonly ICreateOrganizationReportV2Command _createCommand; | ||
| private readonly IUpdateOrganizationReportDataV2Command _updateDataCommand; | ||
| private readonly IGetOrganizationReportQuery _getOrganizationReportQuery; | ||
| private readonly IGetOrganizationReportDataV2Query _getDataQuery; | ||
| private readonly IUpdateOrganizationReportCommand _updateOrganizationReportCommand; | ||
| private readonly IOrganizationReportRepository _organizationReportRepo; | ||
|
|
||
| public OrganizationReportsV2Controller( | ||
| ICurrentContext currentContext, | ||
| IApplicationCacheService applicationCacheService, | ||
| IOrganizationReportStorageService storageService, | ||
| ICreateOrganizationReportV2Command createCommand, | ||
| IUpdateOrganizationReportDataV2Command updateDataCommand, | ||
| IGetOrganizationReportQuery getOrganizationReportQuery, | ||
| IGetOrganizationReportDataV2Query getDataQuery, | ||
| IUpdateOrganizationReportCommand updateOrganizationReportCommand, | ||
| IOrganizationReportRepository organizationReportRepo) | ||
| { | ||
| _currentContext = currentContext; | ||
| _applicationCacheService = applicationCacheService; | ||
| _storageService = storageService; | ||
| _createCommand = createCommand; | ||
| _updateDataCommand = updateDataCommand; | ||
| _getOrganizationReportQuery = getOrganizationReportQuery; | ||
| _getDataQuery = getDataQuery; | ||
| _updateOrganizationReportCommand = updateOrganizationReportCommand; | ||
| _organizationReportRepo = organizationReportRepo; | ||
| } | ||
|
|
||
| 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."); | ||
| } | ||
| } | ||
|
|
||
| [HttpPost("{organizationId}")] | ||
| public async Task<OrganizationReportV2ResponseModel> CreateOrganizationReportAsync( | ||
| Guid organizationId, | ||
| [FromBody] AddOrganizationReportRequest request) | ||
| { | ||
| if (organizationId == Guid.Empty) | ||
| { | ||
| throw new BadRequestException("Organization ID is required."); | ||
| } | ||
|
|
||
| if (request.OrganizationId != organizationId) | ||
| { | ||
| throw new BadRequestException("Organization ID in the request body must match the route parameter"); | ||
| } | ||
|
|
||
| await AuthorizeAsync(organizationId); | ||
|
|
||
| var report = await _createCommand.CreateAsync(request); | ||
|
|
||
| var fileData = report.GetReportFileData()!; | ||
|
|
||
| return new OrganizationReportV2ResponseModel | ||
| { | ||
| ReportDataUploadUrl = await _storageService.GetReportDataUploadUrlAsync(report, fileData), | ||
| ReportResponse = new OrganizationReportResponseModel(report), | ||
| FileUploadType = _storageService.FileUploadType | ||
| }; | ||
| } | ||
|
|
||
| [HttpGet("{organizationId}/{reportId}")] | ||
| public async Task<OrganizationReportResponseModel> GetOrganizationReportAsync( | ||
| Guid organizationId, | ||
| Guid reportId) | ||
| { | ||
| await AuthorizeAsync(organizationId); | ||
|
|
||
| var report = await _getOrganizationReportQuery.GetOrganizationReportAsync(reportId); | ||
|
|
||
| if (report.OrganizationId != organizationId) | ||
| { | ||
| throw new BadRequestException("Invalid report ID"); | ||
| } | ||
|
|
||
| return new OrganizationReportResponseModel(report); | ||
| } | ||
|
|
||
| [HttpPatch("{organizationId}/data/report/{reportId}")] | ||
| public async Task<OrganizationReportV2ResponseModel> GetReportDataUploadUrlAsync( | ||
| Guid organizationId, | ||
| Guid reportId, | ||
| [FromBody] UpdateOrganizationReportDataRequest request, | ||
| [FromQuery] string reportFileId) | ||
| { | ||
| if (request.OrganizationId != organizationId || request.ReportId != reportId) | ||
| { | ||
| throw new BadRequestException("Organization ID and Report ID must match route parameters"); | ||
| } | ||
|
|
||
| if (string.IsNullOrEmpty(reportFileId)) | ||
| { | ||
| throw new BadRequestException("ReportFileId query parameter is required"); | ||
| } | ||
|
|
||
| await AuthorizeAsync(organizationId); | ||
|
|
||
| var uploadUrl = await _updateDataCommand.GetUploadUrlAsync(request, reportFileId); | ||
|
|
||
| var report = await _getOrganizationReportQuery.GetOrganizationReportAsync(reportId); | ||
|
|
||
| return new OrganizationReportV2ResponseModel | ||
| { | ||
| ReportDataUploadUrl = uploadUrl, | ||
| ReportResponse = new OrganizationReportResponseModel(report), | ||
| FileUploadType = _storageService.FileUploadType | ||
| }; | ||
| } | ||
|
|
||
| [HttpPost("{organizationId}/{reportId}/file/report-data")] | ||
| [SelfHosted(SelfHostedOnly = true)] | ||
| [RequestSizeLimit(Constants.FileSize501mb)] | ||
| [DisableFormValueModelBinding] | ||
| public async Task UploadReportDataAsync(Guid organizationId, Guid reportId, [FromQuery] string reportFileId) | ||
|
||
| { | ||
| await AuthorizeAsync(organizationId); | ||
|
|
||
| if (!Request?.ContentType?.Contains("multipart/") ?? true) | ||
| { | ||
| throw new BadRequestException("Invalid content."); | ||
| } | ||
|
|
||
| if (string.IsNullOrEmpty(reportFileId)) | ||
| { | ||
| throw new BadRequestException("ReportFileId query parameter is required"); | ||
| } | ||
|
|
||
| var report = await _getOrganizationReportQuery.GetOrganizationReportAsync(reportId); | ||
| if (report.OrganizationId != organizationId) | ||
| { | ||
| throw new BadRequestException("Invalid report ID"); | ||
| } | ||
|
|
||
| var fileData = report.GetReportFileData(); | ||
| if (fileData == null || fileData.Id != reportFileId) | ||
| { | ||
| throw new NotFoundException(); | ||
| } | ||
|
|
||
| await Request.GetFileAsync(async (stream) => | ||
| { | ||
| await _storageService.UploadReportDataAsync(report, fileData, stream); | ||
| }); | ||
|
|
||
| var (valid, length) = await _storageService.ValidateFileAsync(report, fileData, 0, Constants.FileSize501mb); | ||
| if (!valid) | ||
| { | ||
| throw new BadRequestException("File received does not match expected constraints."); | ||
| } | ||
|
|
||
| fileData.Validated = true; | ||
| fileData.Size = length; | ||
| report.SetReportFileData(fileData); | ||
| report.RevisionDate = DateTime.UtcNow; | ||
| await _organizationReportRepo.ReplaceAsync(report); | ||
| } | ||
| } | ||
13 changes: 13 additions & 0 deletions
13
src/Api/Dirt/Models/Response/OrganizationReportV2ResponseModel.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| ο»Ώusing Bit.Core.Enums; | ||
| using Bit.Core.Models.Api; | ||
|
|
||
| namespace Bit.Api.Dirt.Models.Response; | ||
|
|
||
| public class OrganizationReportV2ResponseModel : ResponseModel | ||
| { | ||
| public OrganizationReportV2ResponseModel() : base("organizationReport-v2") { } | ||
|
|
||
| public string ReportDataUploadUrl { get; set; } = string.Empty; | ||
| public OrganizationReportResponseModel ReportResponse { get; set; } = null!; | ||
| public FileUploadType FileUploadType { get; set; } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| ο»Ώnamespace Bit.Core.Dirt.Enums; | ||
|
|
||
| public enum OrganizationReportType : byte | ||
| { | ||
| Data = 0, | ||
| File = 1 | ||
| } |
6 changes: 6 additions & 0 deletions
6
src/Core/Dirt/Models/Data/OrganizationReportDataFileStorageResponse.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| ο»Ώnamespace Bit.Core.Dirt.Models.Data; | ||
|
|
||
| public class OrganizationReportDataFileStorageResponse | ||
| { | ||
| public string DownloadUrl { get; set; } = string.Empty; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| ο»Ώ#nullable enable | ||
|
|
||
| using System.Diagnostics.CodeAnalysis; | ||
| using System.Text.Json.Serialization; | ||
| using static System.Text.Json.Serialization.JsonNumberHandling; | ||
|
|
||
| namespace Bit.Core.Dirt.Models.Data; | ||
|
|
||
| public class OrganizationReportFileData | ||
| { | ||
| [JsonNumberHandling(WriteAsString | AllowReadingFromString)] | ||
| public long Size { get; set; } | ||
|
|
||
| [DisallowNull] | ||
| public string? Id { get; set; } | ||
|
|
||
| public string FileName { get; set; } = "report-data.json"; | ||
|
|
||
| public bool Validated { get; set; } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
99 changes: 99 additions & 0 deletions
99
src/Core/Dirt/Reports/ReportFeatures/CreateOrganizationReportV2Command.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| ο»Ώusing Bit.Core.Dirt.Entities; | ||
| using Bit.Core.Dirt.Enums; | ||
| using Bit.Core.Dirt.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 Bit.Core.Repositories; | ||
| using Bit.Core.Utilities; | ||
| using Microsoft.Extensions.Logging; | ||
|
|
||
| namespace Bit.Core.Dirt.Reports.ReportFeatures; | ||
|
|
||
| public class CreateOrganizationReportV2Command : ICreateOrganizationReportV2Command | ||
| { | ||
| private readonly IOrganizationRepository _organizationRepo; | ||
| private readonly IOrganizationReportRepository _organizationReportRepo; | ||
| private readonly ILogger<CreateOrganizationReportV2Command> _logger; | ||
|
|
||
| public CreateOrganizationReportV2Command( | ||
| IOrganizationRepository organizationRepository, | ||
| IOrganizationReportRepository organizationReportRepository, | ||
| ILogger<CreateOrganizationReportV2Command> logger) | ||
| { | ||
| _organizationRepo = organizationRepository; | ||
| _organizationReportRepo = organizationReportRepository; | ||
| _logger = logger; | ||
| } | ||
|
|
||
| public async Task<OrganizationReport> CreateAsync(AddOrganizationReportRequest request) | ||
| { | ||
| _logger.LogInformation(Constants.BypassFiltersEventId, | ||
| "Creating organization report for organization {organizationId}", request.OrganizationId); | ||
|
|
||
| var (isValid, errorMessage) = await ValidateRequestAsync(request); | ||
| if (!isValid) | ||
| { | ||
| _logger.LogInformation(Constants.BypassFiltersEventId, | ||
| "Failed to create organization {organizationId} report: {errorMessage}", | ||
| request.OrganizationId, errorMessage); | ||
| throw new BadRequestException(errorMessage); | ||
| } | ||
|
|
||
| var fileData = new OrganizationReportFileData | ||
| { | ||
| Id = CoreHelpers.SecureRandomString(32, upper: false, special: false), | ||
| Validated = false | ||
| }; | ||
|
|
||
| var organizationReport = new OrganizationReport | ||
| { | ||
| OrganizationId = request.OrganizationId, | ||
| Type = OrganizationReportType.File, | ||
| CreationDate = DateTime.UtcNow, | ||
| ContentEncryptionKey = request.ContentEncryptionKey ?? string.Empty, | ||
| SummaryData = request.SummaryData, | ||
| ApplicationData = request.ApplicationData, | ||
| ApplicationCount = request.ReportMetrics?.ApplicationCount, | ||
| ApplicationAtRiskCount = request.ReportMetrics?.ApplicationAtRiskCount, | ||
| CriticalApplicationCount = request.ReportMetrics?.CriticalApplicationCount, | ||
| CriticalApplicationAtRiskCount = request.ReportMetrics?.CriticalApplicationAtRiskCount, | ||
| MemberCount = request.ReportMetrics?.MemberCount, | ||
| MemberAtRiskCount = request.ReportMetrics?.MemberAtRiskCount, | ||
| CriticalMemberCount = request.ReportMetrics?.CriticalMemberCount, | ||
| CriticalMemberAtRiskCount = request.ReportMetrics?.CriticalMemberAtRiskCount, | ||
| PasswordCount = request.ReportMetrics?.PasswordCount, | ||
| PasswordAtRiskCount = request.ReportMetrics?.PasswordAtRiskCount, | ||
| CriticalPasswordCount = request.ReportMetrics?.CriticalPasswordCount, | ||
| CriticalPasswordAtRiskCount = request.ReportMetrics?.CriticalPasswordAtRiskCount, | ||
| RevisionDate = DateTime.UtcNow | ||
| }; | ||
| organizationReport.SetReportFileData(fileData); | ||
|
|
||
| var data = await _organizationReportRepo.CreateAsync(organizationReport); | ||
|
|
||
| _logger.LogInformation(Constants.BypassFiltersEventId, | ||
| "Successfully created organization report for organization {organizationId}, {organizationReportId}", | ||
| request.OrganizationId, data.Id); | ||
|
|
||
| return data; | ||
| } | ||
|
|
||
| private async Task<(bool IsValid, string errorMessage)> ValidateRequestAsync( | ||
| AddOrganizationReportRequest request) | ||
| { | ||
| var organization = await _organizationRepo.GetByIdAsync(request.OrganizationId); | ||
| if (organization == null) | ||
| { | ||
| return (false, "Invalid Organization"); | ||
| } | ||
|
|
||
| if (string.IsNullOrWhiteSpace(request.ContentEncryptionKey)) | ||
| { | ||
| return (false, "Content Encryption Key is required"); | ||
| } | ||
|
|
||
| return (true, string.Empty); | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.