mirror of
https://github.com/bitwarden/server
synced 2025-12-16 16:23:31 +00:00
[PM-23756] Report summary endpoints- mocked (#6092)
This commit is contained in:
@@ -281,4 +281,127 @@ public class ReportsController : Controller
|
|||||||
}
|
}
|
||||||
return await _getOrganizationReportQuery.GetLatestOrganizationReportAsync(orgId);
|
return await _getOrganizationReportQuery.GetLatestOrganizationReportAsync(orgId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the Organization Report Summary for an organization.
|
||||||
|
/// This includes the latest report's encrypted data, encryption key, and date.
|
||||||
|
/// This is a mock implementation and should be replaced with actual data retrieval logic.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="orgId"></param>
|
||||||
|
/// <param name="from">Min date (example: 2023-01-01)</param>
|
||||||
|
/// <param name="to">Max date (example: 2023-12-31)</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="NotFoundException"></exception>
|
||||||
|
[HttpGet("organization-report-summary/{orgId}")]
|
||||||
|
public IEnumerable<OrganizationReportSummaryModel> GetOrganizationReportSummary(
|
||||||
|
[FromRoute] Guid orgId,
|
||||||
|
[FromQuery] DateOnly from,
|
||||||
|
[FromQuery] DateOnly to)
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
{
|
||||||
|
throw new BadRequestException(ModelState);
|
||||||
|
}
|
||||||
|
|
||||||
|
GuardOrganizationAccess(orgId);
|
||||||
|
|
||||||
|
// FIXME: remove this mock class when actual data retrieval is implemented
|
||||||
|
return MockOrganizationReportSummary.GetMockData()
|
||||||
|
.Where(_ => _.OrganizationId == orgId
|
||||||
|
&& _.Date >= from.ToDateTime(TimeOnly.MinValue)
|
||||||
|
&& _.Date <= to.ToDateTime(TimeOnly.MaxValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new Organization Report Summary for an organization.
|
||||||
|
/// This is a mock implementation and should be replaced with actual creation logic.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model"></param>
|
||||||
|
/// <returns>Returns 204 Created with the created OrganizationReportSummaryModel</returns>
|
||||||
|
/// <exception cref="NotFoundException"></exception>
|
||||||
|
[HttpPost("organization-report-summary")]
|
||||||
|
public IActionResult CreateOrganizationReportSummary([FromBody] OrganizationReportSummaryModel model)
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
{
|
||||||
|
throw new BadRequestException(ModelState);
|
||||||
|
}
|
||||||
|
|
||||||
|
GuardOrganizationAccess(model.OrganizationId);
|
||||||
|
|
||||||
|
// TODO: Implement actual creation logic
|
||||||
|
|
||||||
|
// Returns 204 No Content as a placeholder
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut("organization-report-summary")]
|
||||||
|
public IActionResult UpdateOrganizationReportSummary([FromBody] OrganizationReportSummaryModel model)
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
{
|
||||||
|
throw new BadRequestException(ModelState);
|
||||||
|
}
|
||||||
|
|
||||||
|
GuardOrganizationAccess(model.OrganizationId);
|
||||||
|
|
||||||
|
// TODO: Implement actual update logic
|
||||||
|
|
||||||
|
// Returns 204 No Content as a placeholder
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GuardOrganizationAccess(Guid organizationId)
|
||||||
|
{
|
||||||
|
if (!_currentContext.AccessReports(organizationId).Result)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: remove this mock class when actual data retrieval is implemented
|
||||||
|
private class MockOrganizationReportSummary
|
||||||
|
{
|
||||||
|
public static List<OrganizationReportSummaryModel> GetMockData()
|
||||||
|
{
|
||||||
|
return new List<OrganizationReportSummaryModel>
|
||||||
|
{
|
||||||
|
new OrganizationReportSummaryModel
|
||||||
|
{
|
||||||
|
OrganizationId = Guid.Parse("cf6cb873-4916-4b2b-aef0-b20d00e7f3e2"),
|
||||||
|
EncryptedData = "2.EtCcxDEBoF1MYChYHC4Q1w==|RyZ07R7qEFBbc/ICLFpEMockL9K+PD6rOod6DGHHrkaRLHUDqDwmxbu3jnD0cg8s7GIYmp0jApHXC+82QdApk87pA0Kr8fN2Rj0+8bDQCjhKfoRTipAB25S/n2E+ttjvlFfag92S66XqUH9S/eZw/Q==|0bPfykHk3SqS/biLNcNoYtH6YTstBEKu3AhvdZZLxhU=",
|
||||||
|
EncryptionKey = "2.Dd/TtdNwxWdYg9+fRkxh6w==|8KAiK9SoadgFRmyVOchd4tNh2vErD1Rv9x1gqtsE5tzxKE/V/5kkr1WuVG+QpEj//YaQt221UEMESRSXicZ7a9cB6xXLBkbbFwmecQRJVBs=|902em44n9cwciZzYrYuX6MRzRa+4hh1HHfNAxyJx/IM=",
|
||||||
|
Date = DateTime.UtcNow
|
||||||
|
},
|
||||||
|
new OrganizationReportSummaryModel
|
||||||
|
{
|
||||||
|
OrganizationId = Guid.Parse("cf6cb873-4916-4b2b-aef0-b20d00e7f3e2"),
|
||||||
|
EncryptedData = "2.HvY4fAvbzYV1hqa3255m5Q==|WcKga2Wka5i8fVso8MgjzfBAwxaqdhZDL3bnvhDsisZ0r9lNKQcG3YUQSFpJxr74cgg5QRQaFieCUe2YppciHDT6bsaE2VzFce3cNNB821uTFqnlJClkGJpG1nGvPupdErrg4Ik57WenEzYesmR4pw==|F0aJfF+1MlPm+eAlQnDgFnwfv198N9VtPqFJa4+UFqk=",
|
||||||
|
EncryptionKey = "2.ctMgLN4ycPusbQArG/uiag==|NtqiQsAoUxMSTBQsxAMyVLWdt5lVEUGZQNxZSBU4l76ywH2f6dx5FWFrcF3t3GBqy5yDoc5eBg0VlJDW9coqzp8j9n8h1iMrtmXPyBMAhbc=|pbH+w68BUdUKYCfNRpjd8NENw2lZ0vfxgMuTrsrRCTQ=",
|
||||||
|
Date = DateTime.UtcNow.AddMonths(-1)
|
||||||
|
},
|
||||||
|
new OrganizationReportSummaryModel
|
||||||
|
{
|
||||||
|
OrganizationId = Guid.Parse("cf6cb873-4916-4b2b-aef0-b20d00e7f3e2"),
|
||||||
|
EncryptedData = "2.NH4qLZYUkz/+qpB/mRsLTA==|LEFt05jJz0ngh+Hl5lqk6kebj7lZMefA3eFdL1kLJSGdD3uTOngRwH7GXLQNFeQOxutnLX9YUILbUEPwaM8gCwNQ1KWYdB1Z+Ky4nzKRb60N7L5aTA2za6zXTIdjv7Zwhg0jPZ6sPevTuvSyqjMCuA==|Uuu6gZaF0wvB2mHFwtvHegMxfe8DgsYWTRfGiVn4lkM=",
|
||||||
|
EncryptionKey = "2.3YwG78ykSxAn44NcymdG4w==|4jfn0nLoFielicAFbmq27DNUUjV4SwGePnjYRmOa7hk4pEPnQRS3MsTJFbutVyXOgKFY9Yn2yGFZownY9EmXOMM+gHPD0t6TfzUKqQcRyuI=|wasP9zZEL9mFH5HzJYrMxnKUr/XlFKXCxG9uW66uaPU=",
|
||||||
|
Date = DateTime.UtcNow.AddMonths(-1)
|
||||||
|
},
|
||||||
|
new OrganizationReportSummaryModel
|
||||||
|
{
|
||||||
|
OrganizationId = Guid.Parse("cf6cb873-4916-4b2b-aef0-b20d00e7f3e2"),
|
||||||
|
EncryptedData = "2.YmKWj/707wDPONh+JXPBOw==|Fx4jcUHmnUnSMCU8vdThMSYpDyKPnC09TxpSbNxia0M6MFbd5WHElcVribrYgTENyU0HlqPW43hThJ6xXCM0EjEWP7/jb/0l07vMNkA7sDYq+czf0XnYZgZSGKh06wFVz8xkhaPTdsiO4CXuMsoH+w==|DDVwVFHzdfbPQe3ycCx82eYVHDW97V/eWTPsNpHX/+U=",
|
||||||
|
EncryptionKey = "2.f/U45I7KF+JKfnvOArUyaw==|zNhhS2q2WwBl6SqLWMkxrXC8EX91Ra9LJExywkJhsRbxubRLt7fK+YWc8T1LUaDmMwJ3G8buSPGzyacKX0lnUR33dW6DIaLNgRZ/ekb/zkg=|qFoIZWwS0foiiIOyikFRwQKmmmI2HeyHcOVklJnIILI=",
|
||||||
|
Date = DateTime.UtcNow.AddMonths(-1)
|
||||||
|
},
|
||||||
|
new OrganizationReportSummaryModel
|
||||||
|
{
|
||||||
|
OrganizationId = Guid.Parse("cf6cb873-4916-4b2b-aef0-b20d00e7f3e2"),
|
||||||
|
EncryptedData = "2.WYauwooJUEY3kZsDPphmrA==|oguYW6h10A4GxK4KkRS0X32qSTekU2CkGqNDNGfisUgvJzsyoVTafO9sVcdPdg4BUM7YNkPMjYiKEc5jMHkIgLzbnM27jcGvMJrrccSrLHiWL6/mEiqQkV3TlfiZF9i3wqj1ITsYRzM454uNle6Wrg==|uR67aFYb1i5LSidWib0iTf8091l8GY5olHkVXse3CAw=",
|
||||||
|
EncryptionKey = "2.ZyV9+9A2cxNaf8dfzfbnlA==|hhorBpVkcrrhTtNmd6SNHYI8gPNokGLOC22Vx8Qa/AotDAcyuYWw56zsawMnzpAdJGEJFtszKM2+VUVOcroCTMWHpy8yNf/kZA6uPk3Lz3s=|ASzVeJf+K1ZB8NXuypamRBGRuRq0GUHZBEy5r/O7ORY=",
|
||||||
|
Date = DateTime.UtcNow.AddMonths(-1)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Bit.Api.Dirt.Models.Response;
|
||||||
|
|
||||||
|
public class OrganizationReportSummaryModel
|
||||||
|
{
|
||||||
|
public Guid OrganizationId { get; set; }
|
||||||
|
public required string EncryptedData { get; set; }
|
||||||
|
public required string EncryptionKey { get; set; }
|
||||||
|
public DateTime Date { get; set; }
|
||||||
|
}
|
||||||
@@ -1,12 +1,14 @@
|
|||||||
using AutoFixture;
|
using AutoFixture;
|
||||||
using Bit.Api.Dirt.Controllers;
|
using Bit.Api.Dirt.Controllers;
|
||||||
using Bit.Api.Dirt.Models;
|
using Bit.Api.Dirt.Models;
|
||||||
|
using Bit.Api.Dirt.Models.Response;
|
||||||
using Bit.Core.Context;
|
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;
|
||||||
using Bit.Test.Common.AutoFixture;
|
using Bit.Test.Common.AutoFixture;
|
||||||
using Bit.Test.Common.AutoFixture.Attributes;
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
@@ -280,4 +282,185 @@ public class ReportsControllerTests
|
|||||||
_ = sutProvider.GetDependency<IGetOrganizationReportQuery>()
|
_ = sutProvider.GetDependency<IGetOrganizationReportQuery>()
|
||||||
.Received(0);
|
.Received(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public void CreateOrganizationReportSummary_ReturnsNoContent_WhenAccessGranted(SutProvider<ReportsController> sutProvider)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var orgId = Guid.NewGuid();
|
||||||
|
var model = new OrganizationReportSummaryModel
|
||||||
|
{
|
||||||
|
OrganizationId = orgId,
|
||||||
|
EncryptedData = "mock-data",
|
||||||
|
EncryptionKey = "mock-key",
|
||||||
|
Date = DateTime.UtcNow
|
||||||
|
};
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessReports(orgId).Returns(true);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = sutProvider.Sut.CreateOrganizationReportSummary(model);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.IsType<NoContentResult>(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public void CreateOrganizationReportSummary_ThrowsNotFoundException_WhenAccessDenied(SutProvider<ReportsController> sutProvider)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var orgId = Guid.NewGuid();
|
||||||
|
var model = new OrganizationReportSummaryModel
|
||||||
|
{
|
||||||
|
OrganizationId = orgId,
|
||||||
|
EncryptedData = "mock-data",
|
||||||
|
EncryptionKey = "mock-key",
|
||||||
|
Date = DateTime.UtcNow
|
||||||
|
};
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessReports(orgId).Returns(false);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assert.Throws<Bit.Core.Exceptions.NotFoundException>(
|
||||||
|
() => sutProvider.Sut.CreateOrganizationReportSummary(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public void GetOrganizationReportSummary_ThrowsNotFoundException_WhenAccessDenied(
|
||||||
|
SutProvider<ReportsController> sutProvider
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var orgId = Guid.NewGuid();
|
||||||
|
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessReports(orgId).Returns(false);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assert.Throws<Bit.Core.Exceptions.NotFoundException>(
|
||||||
|
() => sutProvider.Sut.GetOrganizationReportSummary(orgId, DateOnly.FromDateTime(DateTime.UtcNow), DateOnly.FromDateTime(DateTime.UtcNow)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public void GetOrganizationReportSummary_returnsExpectedResult(
|
||||||
|
SutProvider<ReportsController> sutProvider
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var orgId = Guid.NewGuid();
|
||||||
|
var dates = new[]
|
||||||
|
{
|
||||||
|
DateOnly.FromDateTime(DateTime.UtcNow),
|
||||||
|
DateOnly.FromDateTime(DateTime.UtcNow.AddMonths(-1))
|
||||||
|
};
|
||||||
|
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessReports(orgId).Returns(true);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = sutProvider.Sut.GetOrganizationReportSummary(orgId, dates[0], dates[1]);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public void CreateOrganizationReportSummary_ReturnsNoContent_WhenModelIsValidAndAccessGranted(
|
||||||
|
SutProvider<ReportsController> sutProvider
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var orgId = Guid.NewGuid();
|
||||||
|
var model = new OrganizationReportSummaryModel
|
||||||
|
{
|
||||||
|
OrganizationId = orgId,
|
||||||
|
EncryptedData = "mock-data",
|
||||||
|
EncryptionKey = "mock-key"
|
||||||
|
};
|
||||||
|
sutProvider.Sut.ModelState.Clear();
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessReports(orgId).Returns(true);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = sutProvider.Sut.CreateOrganizationReportSummary(model);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.IsType<NoContentResult>(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public void CreateOrganizationReportSummary_ThrowsBadRequestException_WhenModelStateIsInvalid(
|
||||||
|
SutProvider<ReportsController> sutProvider
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var orgId = Guid.NewGuid();
|
||||||
|
var model = new OrganizationReportSummaryModel
|
||||||
|
{
|
||||||
|
OrganizationId = orgId,
|
||||||
|
EncryptedData = "mock-data",
|
||||||
|
EncryptionKey = "mock-key"
|
||||||
|
};
|
||||||
|
sutProvider.Sut.ModelState.AddModelError("key", "error");
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assert.Throws<BadRequestException>(() => sutProvider.Sut.CreateOrganizationReportSummary(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public void UpdateOrganizationReportSummary_ReturnsNoContent_WhenModelIsValidAndAccessGranted(
|
||||||
|
SutProvider<ReportsController> sutProvider
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var orgId = Guid.NewGuid();
|
||||||
|
var model = new OrganizationReportSummaryModel
|
||||||
|
{
|
||||||
|
OrganizationId = orgId,
|
||||||
|
EncryptedData = "mock-data",
|
||||||
|
EncryptionKey = "mock-key"
|
||||||
|
};
|
||||||
|
sutProvider.Sut.ModelState.Clear();
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessReports(orgId).Returns(true);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = sutProvider.Sut.UpdateOrganizationReportSummary(model);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.IsType<NoContentResult>(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public void UpdateOrganizationReportSummary_ThrowsBadRequestException_WhenModelStateIsInvalid(
|
||||||
|
SutProvider<ReportsController> sutProvider
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var orgId = Guid.NewGuid();
|
||||||
|
var model = new OrganizationReportSummaryModel
|
||||||
|
{
|
||||||
|
OrganizationId = orgId,
|
||||||
|
EncryptedData = "mock-data",
|
||||||
|
EncryptionKey = "mock-key"
|
||||||
|
};
|
||||||
|
sutProvider.Sut.ModelState.AddModelError("key", "error");
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assert.Throws<BadRequestException>(() => sutProvider.Sut.UpdateOrganizationReportSummary(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public void UpdateOrganizationReportSummary_ThrowsNotFoundException_WhenAccessDenied(
|
||||||
|
SutProvider<ReportsController> sutProvider
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var orgId = Guid.NewGuid();
|
||||||
|
var model = new OrganizationReportSummaryModel
|
||||||
|
{
|
||||||
|
OrganizationId = orgId,
|
||||||
|
EncryptedData = "mock-data",
|
||||||
|
EncryptionKey = "mock-key"
|
||||||
|
};
|
||||||
|
sutProvider.Sut.ModelState.Clear();
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessReports(orgId).Returns(false);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assert.Throws<NotFoundException>(() => sutProvider.Sut.UpdateOrganizationReportSummary(model));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user