Skip to content

Commit

Permalink
[AC 1526]Show current SM seat and service account usage in Bitwarden …
Browse files Browse the repository at this point in the history
…Portal (#3142)

* changes base on the tickets request

* Code refactoring

* Removed the unwanted method

* Add implementation to the new method

* Resolve some pr comments

* resolve lint issue

* resolve pr comments

* add the new noop files

* Add new noop file and resolve some pr comments

* resolve pr comments

* removed unused method
  • Loading branch information
cyprain-okeke authored Aug 2, 2023
1 parent 59bfecd commit a128454
Show file tree
Hide file tree
Showing 12 changed files with 248 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@ public async Task<IEnumerable<ProjectPermissionDetails>> GetManyByOrganizationId
return await projects.ToListAsync();
}

public async Task<int> GetProjectCountByOrganizationIdAsync(Guid organizationId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
return await dbContext.Project
.CountAsync(ou => ou.OrganizationId == organizationId);
}
}

public async Task<IEnumerable<Core.SecretsManager.Entities.Project>> GetManyByOrganizationIdWriteAccessAsync(
Guid organizationId, Guid userId, AccessClientType accessType)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,16 @@ public async Task<IEnumerable<SecretPermissionDetails>> GetManyByOrganizationIdA
return await secrets.ToListAsync();
}

public async Task<int> GetSecretsCountByOrganizationIdAsync(Guid organizationId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
return await dbContext.Secret
.CountAsync(ou => ou.OrganizationId == organizationId && ou.DeletedDate == null);
}
}

public async Task<IEnumerable<Core.SecretsManager.Entities.Secret>> GetManyByOrganizationIdInTrashByIdsAsync(Guid organizationId, IEnumerable<Guid> ids)
{
using (var scope = ServiceScopeFactory.CreateScope())
Expand Down
29 changes: 26 additions & 3 deletions src/Admin/Controllers/OrganizationsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Bit.Core.Models.OrganizationConnectionConfigs;
using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces;
using Bit.Core.Repositories;
using Bit.Core.SecretsManager.Repositories;
using Bit.Core.Services;
using Bit.Core.Settings;
using Bit.Core.Tools.Enums;
Expand Down Expand Up @@ -42,6 +43,9 @@ public class OrganizationsController : Controller
private readonly ILogger<OrganizationsController> _logger;
private readonly IAccessControlService _accessControlService;
private readonly ICurrentContext _currentContext;
private readonly ISecretRepository _secretRepository;
private readonly IProjectRepository _projectRepository;
private readonly IServiceAccountRepository _serviceAccountRepository;

public OrganizationsController(
IOrganizationService organizationService,
Expand All @@ -62,7 +66,10 @@ public OrganizationsController(
IProviderRepository providerRepository,
ILogger<OrganizationsController> logger,
IAccessControlService accessControlService,
ICurrentContext currentContext)
ICurrentContext currentContext,
ISecretRepository secretRepository,
IProjectRepository projectRepository,
IServiceAccountRepository serviceAccountRepository)
{
_organizationService = organizationService;
_organizationRepository = organizationRepository;
Expand All @@ -83,6 +90,9 @@ public OrganizationsController(
_logger = logger;
_accessControlService = accessControlService;
_currentContext = currentContext;
_secretRepository = secretRepository;
_projectRepository = projectRepository;
_serviceAccountRepository = serviceAccountRepository;
}

[RequirePermission(Permission.Org_List_View)]
Expand Down Expand Up @@ -137,7 +147,14 @@ public async Task<IActionResult> View(Guid id)
}
var users = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(id);
var billingSyncConnection = _globalSettings.EnableCloudCommunication ? await _organizationConnectionRepository.GetByOrganizationIdTypeAsync(id, OrganizationConnectionType.CloudBillingSync) : null;
return View(new OrganizationViewModel(organization, provider, billingSyncConnection, users, ciphers, collections, groups, policies));
var secrets = organization.UseSecretsManager ? await _secretRepository.GetSecretsCountByOrganizationIdAsync(id) : -1;
var projects = organization.UseSecretsManager ? await _projectRepository.GetProjectCountByOrganizationIdAsync(id) : -1;
var serviceAccounts = organization.UseSecretsManager ? await _serviceAccountRepository.GetServiceAccountCountByOrganizationIdAsync(id) : -1;
var smSeats = organization.UseSecretsManager
? await _organizationUserRepository.GetOccupiedSmSeatCountByOrganizationIdAsync(organization.Id)
: -1;
return View(new OrganizationViewModel(organization, provider, billingSyncConnection, users, ciphers, collections, groups, policies,
secrets, projects, serviceAccounts, smSeats));
}

[SelfHosted(NotSelfHostedOnly = true)]
Expand Down Expand Up @@ -165,8 +182,14 @@ public async Task<IActionResult> Edit(Guid id)
var users = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(id);
var billingInfo = await _paymentService.GetBillingAsync(organization);
var billingSyncConnection = _globalSettings.EnableCloudCommunication ? await _organizationConnectionRepository.GetByOrganizationIdTypeAsync(id, OrganizationConnectionType.CloudBillingSync) : null;
var secrets = organization.UseSecretsManager ? await _secretRepository.GetSecretsCountByOrganizationIdAsync(id) : -1;
var projects = organization.UseSecretsManager ? await _projectRepository.GetProjectCountByOrganizationIdAsync(id) : -1;
var serviceAccounts = organization.UseSecretsManager ? await _serviceAccountRepository.GetServiceAccountCountByOrganizationIdAsync(id) : -1;
var smSeats = organization.UseSecretsManager
? await _organizationUserRepository.GetOccupiedSmSeatCountByOrganizationIdAsync(organization.Id)
: -1;
return View(new OrganizationEditModel(organization, provider, users, ciphers, collections, groups, policies,
billingInfo, billingSyncConnection, _globalSettings));
billingInfo, billingSyncConnection, _globalSettings, secrets, projects, serviceAccounts, smSeats));
}

[HttpPost]
Expand Down
5 changes: 3 additions & 2 deletions src/Admin/Models/OrganizationEditModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ public OrganizationEditModel(Provider provider)
public OrganizationEditModel(Organization org, Provider provider, IEnumerable<OrganizationUserUserDetails> orgUsers,
IEnumerable<Cipher> ciphers, IEnumerable<Collection> collections, IEnumerable<Group> groups,
IEnumerable<Policy> policies, BillingInfo billingInfo, IEnumerable<OrganizationConnection> connections,
GlobalSettings globalSettings)
: base(org, provider, connections, orgUsers, ciphers, collections, groups, policies)
GlobalSettings globalSettings, int secrets, int projects, int serviceAccounts, int smSeats)
: base(org, provider, connections, orgUsers, ciphers, collections, groups, policies, secrets, projects,
serviceAccounts, smSeats)
{
BillingInfo = billingInfo;
BraintreeMerchantId = globalSettings.Braintree.MerchantId;
Expand Down
13 changes: 12 additions & 1 deletion src/Admin/Models/OrganizationViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ public OrganizationViewModel() { }

public OrganizationViewModel(Organization org, Provider provider, IEnumerable<OrganizationConnection> connections,
IEnumerable<OrganizationUserUserDetails> orgUsers, IEnumerable<Cipher> ciphers, IEnumerable<Collection> collections,
IEnumerable<Group> groups, IEnumerable<Policy> policies)
IEnumerable<Group> groups, IEnumerable<Policy> policies, int secretsCount, int projectCount, int serviceAccountsCount,
int smSeatsCount)

{
Organization = org;
Provider = provider;
Expand All @@ -37,6 +39,10 @@ public OrganizationViewModel(Organization org, Provider provider, IEnumerable<Or
orgUsers
.Where(u => u.Type == OrganizationUserType.Admin && u.Status == organizationUserStatus)
.Select(u => u.Email));
Secrets = secretsCount;
Projects = projectCount;
ServiceAccounts = serviceAccountsCount;
SmSeats = smSeatsCount;
}

public Organization Organization { get; set; }
Expand All @@ -53,4 +59,9 @@ public OrganizationViewModel(Organization org, Provider provider, IEnumerable<Or
public int GroupCount { get; set; }
public int PolicyCount { get; set; }
public bool HasPublicPrivateKeys { get; set; }
public int Secrets { get; set; }
public int Projects { get; set; }
public int ServiceAccounts { get; set; }
public int SmSeats { get; set; }
public bool UseSecretsManager => Organization.UseSecretsManager;
}
12 changes: 12 additions & 0 deletions src/Admin/Views/Organizations/_ViewInformation.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,18 @@
<dt class="col-sm-4 col-lg-3">Collections</dt>
<dd class="col-sm-8 col-lg-9">@Model.CollectionCount</dd>

<dt class="col-sm-4 col-lg-3">Secrets</dt>
<dd class="col-sm-8 col-lg-9">@(Model.UseSecretsManager ? Model.Secrets: "N/A")</dd>

<dt class="col-sm-4 col-lg-3">Projects</dt>
<dd class="col-sm-8 col-lg-9">@(Model.UseSecretsManager ? Model.Projects: "N/A")</dd>

<dt class="col-sm-4 col-lg-3">Service Accounts</dt>
<dd class="col-sm-8 col-lg-9">@(Model.UseSecretsManager ? Model.ServiceAccounts: "N/A")</dd>

<dt class="col-sm-4 col-lg-3">Secrets Manager Seats</dt>
<dd class="col-sm-8 col-lg-9">@(Model.UseSecretsManager ? Model.SmSeats: "N/A" )</dd>

<dt class="col-sm-4 col-lg-3">Groups</dt>
<dd class="col-sm-8 col-lg-9">@Model.GroupCount</dd>

Expand Down
30 changes: 15 additions & 15 deletions src/Admin/packages.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -2821,15 +2821,15 @@
"commercial.core": {
"type": "Project",
"dependencies": {
"Core": "[2023.7.0, )"
"Core": "[2023.7.1, )"
}
},
"commercial.infrastructure.entityframework": {
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )",
"Core": "[2023.7.0, )",
"Infrastructure.EntityFramework": "[2023.7.0, )"
"Core": "[2023.7.1, )",
"Infrastructure.EntityFramework": "[2023.7.1, )"
}
},
"core": {
Expand Down Expand Up @@ -2877,15 +2877,15 @@
"infrastructure.dapper": {
"type": "Project",
"dependencies": {
"Core": "[2023.7.0, )",
"Core": "[2023.7.1, )",
"Dapper": "[2.0.123, )"
}
},
"infrastructure.entityframework": {
"type": "Project",
"dependencies": {
"AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )",
"Core": "[2023.7.0, )",
"Core": "[2023.7.1, )",
"Microsoft.EntityFrameworkCore.Relational": "[7.0.5, )",
"Microsoft.EntityFrameworkCore.SqlServer": "[7.0.5, )",
"Microsoft.EntityFrameworkCore.Sqlite": "[7.0.5, )",
Expand All @@ -2897,38 +2897,38 @@
"migrator": {
"type": "Project",
"dependencies": {
"Core": "[2023.7.0, )",
"Core": "[2023.7.1, )",
"Microsoft.Extensions.Logging": "[6.0.0, )",
"dbup-sqlserver": "[5.0.8, )"
}
},
"mysqlmigrations": {
"type": "Project",
"dependencies": {
"Core": "[2023.7.0, )",
"Infrastructure.EntityFramework": "[2023.7.0, )"
"Core": "[2023.7.1, )",
"Infrastructure.EntityFramework": "[2023.7.1, )"
}
},
"postgresmigrations": {
"type": "Project",
"dependencies": {
"Core": "[2023.7.0, )",
"Infrastructure.EntityFramework": "[2023.7.0, )"
"Core": "[2023.7.1, )",
"Infrastructure.EntityFramework": "[2023.7.1, )"
}
},
"sharedweb": {
"type": "Project",
"dependencies": {
"Core": "[2023.7.0, )",
"Infrastructure.Dapper": "[2023.7.0, )",
"Infrastructure.EntityFramework": "[2023.7.0, )"
"Core": "[2023.7.1, )",
"Infrastructure.Dapper": "[2023.7.1, )",
"Infrastructure.EntityFramework": "[2023.7.1, )"
}
},
"sqlitemigrations": {
"type": "Project",
"dependencies": {
"Core": "[2023.7.0, )",
"Infrastructure.EntityFramework": "[2023.7.0, )"
"Core": "[2023.7.1, )",
"Infrastructure.EntityFramework": "[2023.7.1, )"
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/Core/SecretsManager/Repositories/IProjectRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ public interface IProjectRepository
Task<IEnumerable<Project>> ImportAsync(IEnumerable<Project> projects);
Task<(bool Read, bool Write)> AccessToProjectAsync(Guid id, Guid userId, AccessClientType accessType);
Task<bool> ProjectsAreInOrganization(List<Guid> projectIds, Guid organizationId);
Task<int> GetProjectCountByOrganizationIdAsync(Guid organizationId);
}
1 change: 1 addition & 0 deletions src/Core/SecretsManager/Repositories/ISecretRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ public interface ISecretRepository
Task UpdateRevisionDates(IEnumerable<Guid> ids);
Task<(bool Read, bool Write)> AccessToSecretAsync(Guid id, Guid userId, AccessClientType accessType);
Task EmptyTrash(DateTime nowTime, uint deleteAfterThisNumberOfDays);
Task<int> GetSecretsCountByOrganizationIdAsync(Guid organizationId);
}
65 changes: 65 additions & 0 deletions src/Core/SecretsManager/Repositories/Noop/NoopProjectRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using Bit.Core.Enums;
using Bit.Core.SecretsManager.Entities;
using Bit.Core.SecretsManager.Models.Data;

namespace Bit.Core.SecretsManager.Repositories.Noop;

public class NoopProjectRepository : IProjectRepository
{
public Task<IEnumerable<ProjectPermissionDetails>> GetManyByOrganizationIdAsync(Guid organizationId, Guid userId,
AccessClientType accessType)
{
return Task.FromResult(null as IEnumerable<ProjectPermissionDetails>);
}

public Task<IEnumerable<Project>> GetManyByOrganizationIdWriteAccessAsync(Guid organizationId, Guid userId,
AccessClientType accessType)
{
return Task.FromResult(null as IEnumerable<Project>);
}

public Task<IEnumerable<Project>> GetManyWithSecretsByIds(IEnumerable<Guid> ids)
{
return Task.FromResult(null as IEnumerable<Project>);
}

public Task<Project> GetByIdAsync(Guid id)
{
return Task.FromResult(null as Project);
}

public Task<Project> CreateAsync(Project project)
{
return Task.FromResult(null as Project);
}

public Task ReplaceAsync(Project project)
{
return Task.FromResult(0);
}

public Task DeleteManyByIdAsync(IEnumerable<Guid> ids)
{
return Task.FromResult(0);
}

public Task<IEnumerable<Project>> ImportAsync(IEnumerable<Project> projects)
{
return Task.FromResult(null as IEnumerable<Project>);
}

public Task<(bool Read, bool Write)> AccessToProjectAsync(Guid id, Guid userId, AccessClientType accessType)
{
return Task.FromResult((false, false));
}

public Task<bool> ProjectsAreInOrganization(List<Guid> projectIds, Guid organizationId)
{
return Task.FromResult(false);
}

public Task<int> GetProjectCountByOrganizationIdAsync(Guid organizationId)
{
return Task.FromResult(0);
}
}
Loading

0 comments on commit a128454

Please sign in to comment.