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;
using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
using Bit.Core.Dirt.Reports.ReportFeatures.OrganizationReportMembers.Interfaces;
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
using Bit.Core.Exceptions;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.Dirt.Controllers;
[Route("reports")]
[Authorize("Application")]
public class ReportsController : Controller
{
private readonly ICurrentContext _currentContext;
private readonly IMemberAccessReportQuery _memberAccessReportQuery;
private readonly IRiskInsightsReportQuery _riskInsightsReportQuery;
private readonly IAddPasswordHealthReportApplicationCommand _addPwdHealthReportAppCommand;
private readonly IGetPasswordHealthReportApplicationQuery _getPwdHealthReportAppQuery;
private readonly IDropPasswordHealthReportApplicationCommand _dropPwdHealthReportAppCommand;
public ReportsController(
ICurrentContext currentContext,
IMemberAccessReportQuery memberAccessReportQuery,
IRiskInsightsReportQuery riskInsightsReportQuery,
IAddPasswordHealthReportApplicationCommand addPasswordHealthReportApplicationCommand,
IGetPasswordHealthReportApplicationQuery getPasswordHealthReportApplicationQuery,
IDropPasswordHealthReportApplicationCommand dropPwdHealthReportAppCommand
)
{
_currentContext = currentContext;
_memberAccessReportQuery = memberAccessReportQuery;
_riskInsightsReportQuery = riskInsightsReportQuery;
_addPwdHealthReportAppCommand = addPasswordHealthReportApplicationCommand;
_getPwdHealthReportAppQuery = getPasswordHealthReportApplicationQuery;
_dropPwdHealthReportAppCommand = dropPwdHealthReportAppCommand;
}
///
/// Organization member information containing a list of cipher ids
/// assigned
///
/// Organzation Id
/// IEnumerable of MemberCipherDetailsResponseModel
/// If Access reports permission is not assigned
[HttpGet("member-cipher-details/{orgId}")]
public async Task> 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();
}
var riskDetails = await GetRiskInsightsReportDetails(new RiskInsightsReportRequest { OrganizationId = orgId });
var responses = riskDetails.Select(x => new MemberCipherDetailsResponseModel(x));
return responses;
}
///
/// Access details for an organization member. Includes the member information,
/// group collection assignment, and item counts
///
/// Organization Id
/// IEnumerable of MemberAccessReportResponseModel
/// If Access reports permission is not assigned
[HttpGet("member-access/{orgId}")]
public async Task> GetMemberAccessReport(Guid orgId)
{
if (!await _currentContext.AccessReports(orgId))
{
throw new NotFoundException();
}
var accessDetails = await GetMemberAccessDetails(new MemberAccessReportRequest { OrganizationId = orgId });
var responses = accessDetails.Select(x => new MemberAccessDetailReportResponseModel(x));
return responses;
}
///
/// Contains the organization member info, the cipher ids associated with the member,
/// and details on their collections, groups, and permissions
///
/// Request parameters
///
/// List of a user's permissions at a group and collection level as well as the number of ciphers
/// associated with that group/collection
///
private async Task> GetMemberAccessDetails(
MemberAccessReportRequest request)
{
var accessDetails = await _memberAccessReportQuery.GetMemberAccessReportsAsync(request);
return accessDetails;
}
///
/// Gets the risk insights report details from the risk insights query. Associates a user to their cipher ids
///
/// Request parameters
/// A list of risk insights data associating the user to cipher ids
private async Task> GetRiskInsightsReportDetails(
RiskInsightsReportRequest request)
{
var riskDetails = await _riskInsightsReportQuery.GetRiskInsightsReportDetails(request);
return riskDetails;
}
///
/// Get the password health report applications for an organization
///
/// A valid Organization Id
/// An Enumerable of PasswordHealthReportApplication
/// If the user lacks access
/// If the organization Id is not valid
[HttpGet("password-health-report-applications/{orgId}")]
public async Task> GetPasswordHealthReportApplications(Guid orgId)
{
if (!await _currentContext.AccessReports(orgId))
{
throw new NotFoundException();
}
return await _getPwdHealthReportAppQuery.GetPasswordHealthReportApplicationAsync(orgId);
}
///
/// Adds a new record into PasswordHealthReportApplication
///
/// A single instance of PasswordHealthReportApplication Model
/// A single instance of PasswordHealthReportApplication
/// If the organization Id is not valid
/// If the user lacks access
[HttpPost("password-health-report-application")]
public async Task 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);
}
///
/// Adds multiple records into PasswordHealthReportApplication
///
/// A enumerable of PasswordHealthReportApplicationModel
/// An Enumerable of PasswordHealthReportApplication
/// If user does not have access to the OrganizationId
/// If the organization Id is not valid
[HttpPost("password-health-report-applications")]
public async Task> AddPasswordHealthReportApplications(
[FromBody] IEnumerable request)
{
if (request.Any(_ => _currentContext.AccessReports(_.OrganizationId).Result == false))
{
throw new NotFoundException();
}
var commandRequests = request.Select(request => new AddPasswordHealthReportApplicationRequest
{
OrganizationId = request.OrganizationId,
Url = request.Url
}).ToList();
return await _addPwdHealthReportAppCommand.AddPasswordHealthReportApplicationAsync(commandRequests);
}
///
/// Drops a record from PasswordHealthReportApplication
///
///
/// A single instance of DropPasswordHealthReportApplicationRequest
/// { OrganizationId, array of PasswordHealthReportApplicationIds }
///
///
/// If user does not have access to the organization
/// If the organization does not have any records
[HttpDelete("password-health-report-application")]
public async Task DropPasswordHealthReportApplication(
[FromBody] DropPasswordHealthReportApplicationRequest request)
{
if (!await _currentContext.AccessReports(request.OrganizationId))
{
throw new NotFoundException();
}
await _dropPwdHealthReportAppCommand.DropPasswordHealthReportApplicationAsync(request);
}
}