Files
server/src/Infrastructure.Dapper/DapperHelpers.cs
Jake Fink 989603ddd3 [Pm 3797 Part 2] Add emergency access rotations (#3434)
## Type of change

<!-- (mark with an `X`) -->

```
- [ ] Bug fix
- [ ] New feature development
- [x] Tech debt (refactoring, code cleanup, dependency upgrades, etc)
- [ ] Build/deploy pipeline (DevOps)
- [ ] Other
```

## Objective
<!--Describe what the purpose of this PR is. For example: what bug you're fixing or what new feature you're adding-->
See #3425 for part 1 and background.

This PR adds emergency access to the rotation. All new code is hidden behind a feature flag.

The Accounts controller has also been moved to Auth ownership.

## Code changes
<!--Explain the changes you've made to each file or major component. This should help the reviewer understand your changes-->
<!--Also refer to any related changes or PRs in other repositories-->

* **file.ext:** Description of what was changed and why
* **AccountsController.cs:** Moved to Auth ownership. Emergency access validation was added (as well as initializing empty lists to avoid errors).
* **EmergencyAccessRotationValidator.cs:** Performs validation on the provided list of new emergency access keys.
* **EmergencyAccessRepository.cs:** Adds a method to rotate encryption keys. This is added to a list in the `RotateUserKeyCommand` that the `UserRepository` calls so it doesn't have to know about all the domains.

## Before you submit

- Please check for formatting errors (`dotnet format --verify-no-changes`) (required)
- If making database changes - make sure you also update Entity Framework queries and/or migrations
- Please add **unit tests** where it makes sense to do so (encouraged but not required)
- If this change requires a **documentation update** - notify the documentation team
- If this change has particular **deployment requirements** - notify the DevOps team
2023-12-05 12:05:51 -05:00

140 lines
5.6 KiB
C#

using System.Data;
using Bit.Core.Entities;
using Bit.Core.Models.Data;
using Dapper;
namespace Bit.Infrastructure.Dapper;
public static class DapperHelpers
{
public static DataTable ToGuidIdArrayTVP(this IEnumerable<Guid> ids)
{
return ids.ToArrayTVP("GuidId");
}
public static DataTable ToArrayTVP<T>(this IEnumerable<T> values, string columnName)
{
var table = new DataTable();
table.SetTypeName($"[dbo].[{columnName}Array]");
table.Columns.Add(columnName, typeof(T));
if (values != null)
{
foreach (var value in values)
{
table.Rows.Add(value);
}
}
return table;
}
public static DataTable ToArrayTVP(this IEnumerable<CollectionAccessSelection> values)
{
var table = new DataTable();
table.SetTypeName("[dbo].[CollectionAccessSelectionType]");
var idColumn = new DataColumn("Id", typeof(Guid));
table.Columns.Add(idColumn);
var readOnlyColumn = new DataColumn("ReadOnly", typeof(bool));
table.Columns.Add(readOnlyColumn);
var hidePasswordsColumn = new DataColumn("HidePasswords", typeof(bool));
table.Columns.Add(hidePasswordsColumn);
var manageColumn = new DataColumn("Manage", typeof(bool));
table.Columns.Add(manageColumn);
if (values != null)
{
foreach (var value in values)
{
var row = table.NewRow();
row[idColumn] = value.Id;
row[readOnlyColumn] = value.ReadOnly;
row[hidePasswordsColumn] = value.HidePasswords;
row[manageColumn] = value.Manage;
table.Rows.Add(row);
}
}
return table;
}
public static DataTable ToTvp(this IEnumerable<OrganizationUser> orgUsers)
{
var table = new DataTable();
table.SetTypeName("[dbo].[OrganizationUserType2]");
var columnData = new List<(string name, Type type, Func<OrganizationUser, object> getter)>
{
(nameof(OrganizationUser.Id), typeof(Guid), ou => ou.Id),
(nameof(OrganizationUser.OrganizationId), typeof(Guid), ou => ou.OrganizationId),
(nameof(OrganizationUser.UserId), typeof(Guid), ou => ou.UserId),
(nameof(OrganizationUser.Email), typeof(string), ou => ou.Email),
(nameof(OrganizationUser.Key), typeof(string), ou => ou.Key),
(nameof(OrganizationUser.Status), typeof(byte), ou => ou.Status),
(nameof(OrganizationUser.Type), typeof(byte), ou => ou.Type),
(nameof(OrganizationUser.AccessAll), typeof(bool), ou => ou.AccessAll),
(nameof(OrganizationUser.ExternalId), typeof(string), ou => ou.ExternalId),
(nameof(OrganizationUser.CreationDate), typeof(DateTime), ou => ou.CreationDate),
(nameof(OrganizationUser.RevisionDate), typeof(DateTime), ou => ou.RevisionDate),
(nameof(OrganizationUser.Permissions), typeof(string), ou => ou.Permissions),
(nameof(OrganizationUser.ResetPasswordKey), typeof(string), ou => ou.ResetPasswordKey),
(nameof(OrganizationUser.AccessSecretsManager), typeof(bool), ou => ou.AccessSecretsManager),
};
return orgUsers.BuildTable(table, columnData);
}
public static DataTable ToTvp(this IEnumerable<OrganizationSponsorship> organizationSponsorships)
{
var table = new DataTable();
table.SetTypeName("[dbo].[OrganizationSponsorshipType]");
var columnData = new List<(string name, Type type, Func<OrganizationSponsorship, object> getter)>
{
(nameof(OrganizationSponsorship.Id), typeof(Guid), ou => ou.Id),
(nameof(OrganizationSponsorship.SponsoringOrganizationId), typeof(Guid), ou => ou.SponsoringOrganizationId),
(nameof(OrganizationSponsorship.SponsoringOrganizationUserId), typeof(Guid), ou => ou.SponsoringOrganizationUserId),
(nameof(OrganizationSponsorship.SponsoredOrganizationId), typeof(Guid), ou => ou.SponsoredOrganizationId),
(nameof(OrganizationSponsorship.FriendlyName), typeof(string), ou => ou.FriendlyName),
(nameof(OrganizationSponsorship.OfferedToEmail), typeof(string), ou => ou.OfferedToEmail),
(nameof(OrganizationSponsorship.PlanSponsorshipType), typeof(byte), ou => ou.PlanSponsorshipType),
(nameof(OrganizationSponsorship.LastSyncDate), typeof(DateTime), ou => ou.LastSyncDate),
(nameof(OrganizationSponsorship.ValidUntil), typeof(DateTime), ou => ou.ValidUntil),
(nameof(OrganizationSponsorship.ToDelete), typeof(bool), ou => ou.ToDelete),
};
return organizationSponsorships.BuildTable(table, columnData);
}
public static DataTable BuildTable<T>(this IEnumerable<T> entities, DataTable table,
List<(string name, Type type, Func<T, object> getter)> columnData)
{
foreach (var (name, type, getter) in columnData)
{
var column = new DataColumn(name, type);
table.Columns.Add(column);
}
foreach (var entity in entities ?? new T[] { })
{
var row = table.NewRow();
foreach (var (name, type, getter) in columnData)
{
var val = getter(entity);
if (val == null)
{
row[name] = DBNull.Value;
}
else
{
row[name] = val;
}
}
table.Rows.Add(row);
}
return table;
}
}