1
0
mirror of https://github.com/bitwarden/server.git synced 2025-06-30 15:42:48 -05:00

SM-1146: Secrets Manager total counts (#4200)

* SM-1146: SM Organization Counts for Projects, Secrets, Machine Accounts

* SM-1146: Project total counts

* SM-1146: models object renames

* SM-1146: Service Account total counts

* SM-1146: Unit test coverage for counts controller

* SM-1146: Counts controller simplification, UT update

* SM-1146: Service Account total counts from Service Account auth user

* SM-1146: Integration Tests for total counts controller

* SM-1146: Explicitly denying access for Service Accounts

* SM-1146: Fix broken ProjectsController integration test

* SM-1146: Integration tests for counts controller

* SM-1146: Explicitly denying access for Service Accounts cleanup

* SM-1146: Test cleanup

* SM-1146: PR review comments fix

* SM-1146: People, Service Accounts positive count on write access

* Update bitwarden_license/src/Commercial.Infrastructure.EntityFramework/SecretsManager/Repositories/ProjectRepository.cs

Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com>

---------

Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com>
This commit is contained in:
Maciej Zieniuk
2024-08-08 15:12:52 +02:00
committed by GitHub
parent bb02bdb3e8
commit 77f8cc58e8
19 changed files with 1095 additions and 5 deletions

View File

@ -0,0 +1,119 @@
#nullable enable
using Bit.Api.SecretsManager.Models.Response;
using Bit.Core.Context;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.SecretsManager.Queries.Interfaces;
using Bit.Core.SecretsManager.Repositories;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.SecretsManager.Controllers;
[Authorize("secrets")]
public class CountsController : Controller
{
private readonly ICurrentContext _currentContext;
private readonly IAccessClientQuery _accessClientQuery;
private readonly IProjectRepository _projectRepository;
private readonly ISecretRepository _secretRepository;
private readonly IServiceAccountRepository _serviceAccountRepository;
public CountsController(
ICurrentContext currentContext,
IAccessClientQuery accessClientQuery,
IProjectRepository projectRepository,
ISecretRepository secretRepository,
IServiceAccountRepository serviceAccountRepository)
{
_currentContext = currentContext;
_accessClientQuery = accessClientQuery;
_projectRepository = projectRepository;
_secretRepository = secretRepository;
_serviceAccountRepository = serviceAccountRepository;
}
[HttpGet("organizations/{organizationId}/sm-counts")]
public async Task<OrganizationCountsResponseModel> GetByOrganizationAsync([FromRoute] Guid organizationId)
{
var (accessType, userId) = await GetAccessClientAsync(organizationId);
var projectsCountTask = _projectRepository.GetProjectCountByOrganizationIdAsync(organizationId,
userId, accessType);
var secretsCountTask = _secretRepository.GetSecretsCountByOrganizationIdAsync(organizationId,
userId, accessType);
var serviceAccountsCountsTask = _serviceAccountRepository.GetServiceAccountCountByOrganizationIdAsync(
organizationId, userId, accessType);
var counts = await Task.WhenAll(projectsCountTask, secretsCountTask, serviceAccountsCountsTask);
return new OrganizationCountsResponseModel
{
Projects = counts[0],
Secrets = counts[1],
ServiceAccounts = counts[2]
};
}
[HttpGet("projects/{projectId}/sm-counts")]
public async Task<ProjectCountsResponseModel> GetByProjectAsync([FromRoute] Guid projectId)
{
var project = await _projectRepository.GetByIdAsync(projectId);
if (project == null)
{
throw new NotFoundException();
}
var (accessType, userId) = await GetAccessClientAsync(project.OrganizationId);
var projectsCounts = await _projectRepository.GetProjectCountsByIdAsync(projectId, userId, accessType);
return new ProjectCountsResponseModel
{
Secrets = projectsCounts.Secrets,
People = projectsCounts.People,
ServiceAccounts = projectsCounts.ServiceAccounts
};
}
[HttpGet("service-accounts/{serviceAccountId}/sm-counts")]
public async Task<ServiceAccountCountsResponseModel> GetByServiceAccountAsync([FromRoute] Guid serviceAccountId)
{
var serviceAccount = await _serviceAccountRepository.GetByIdAsync(serviceAccountId);
if (serviceAccount == null)
{
throw new NotFoundException();
}
var (accessType, userId) = await GetAccessClientAsync(serviceAccount.OrganizationId);
var serviceAccountCounts =
await _serviceAccountRepository.GetServiceAccountCountsByIdAsync(serviceAccountId, userId, accessType);
return new ServiceAccountCountsResponseModel
{
Projects = serviceAccountCounts.Projects,
People = serviceAccountCounts.People,
AccessTokens = serviceAccountCounts.AccessTokens
};
}
private async Task<(AccessClientType, Guid)> GetAccessClientAsync(Guid organizationId)
{
if (!_currentContext.AccessSecretsManager(organizationId))
{
throw new NotFoundException();
}
var (accessType, userId) = await _accessClientQuery.GetAccessClientAsync(User, organizationId);
if (accessType == AccessClientType.ServiceAccount)
{
throw new NotFoundException();
}
return (accessType, userId);
}
}

View File

@ -0,0 +1,15 @@
#nullable enable
using Bit.Core.Models.Api;
namespace Bit.Api.SecretsManager.Models.Response;
public class OrganizationCountsResponseModel() : ResponseModel(_objectName)
{
private const string _objectName = "organizationCounts";
public int Projects { get; set; }
public int Secrets { get; set; }
public int ServiceAccounts { get; set; }
}

View File

@ -0,0 +1,15 @@
#nullable enable
using Bit.Core.Models.Api;
namespace Bit.Api.SecretsManager.Models.Response;
public class ProjectCountsResponseModel() : ResponseModel(_objectName)
{
private const string _objectName = "projectCounts";
public int Secrets { get; set; }
public int People { get; set; }
public int ServiceAccounts { get; set; }
}

View File

@ -0,0 +1,15 @@
#nullable enable
using Bit.Core.Models.Api;
namespace Bit.Api.SecretsManager.Models.Response;
public class ServiceAccountCountsResponseModel() : ResponseModel(_objectName)
{
private const string _objectName = "serviceAccountCounts";
public int Projects { get; set; }
public int People { get; set; }
public int AccessTokens { get; set; }
}