mirror of
https://github.com/bitwarden/server.git
synced 2025-06-18 01:53:49 -05:00
[PM-20112] Member access stored proc and splitting the query (#5943)
This commit is contained in:
parent
66d1c70dc6
commit
b8244908ec
@ -1,5 +1,6 @@
|
|||||||
using Bit.Api.Dirt.Models;
|
using Bit.Api.Dirt.Models;
|
||||||
using Bit.Api.Dirt.Models.Response;
|
using Bit.Api.Dirt.Models.Response;
|
||||||
|
using Bit.Api.Tools.Models.Response;
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Dirt.Reports.Entities;
|
using Bit.Core.Dirt.Reports.Entities;
|
||||||
using Bit.Core.Dirt.Reports.Models.Data;
|
using Bit.Core.Dirt.Reports.Models.Data;
|
||||||
@ -17,21 +18,24 @@ namespace Bit.Api.Dirt.Controllers;
|
|||||||
public class ReportsController : Controller
|
public class ReportsController : Controller
|
||||||
{
|
{
|
||||||
private readonly ICurrentContext _currentContext;
|
private readonly ICurrentContext _currentContext;
|
||||||
private readonly IMemberAccessCipherDetailsQuery _memberAccessCipherDetailsQuery;
|
private readonly IMemberAccessReportQuery _memberAccessReportQuery;
|
||||||
|
private readonly IRiskInsightsReportQuery _riskInsightsReportQuery;
|
||||||
private readonly IAddPasswordHealthReportApplicationCommand _addPwdHealthReportAppCommand;
|
private readonly IAddPasswordHealthReportApplicationCommand _addPwdHealthReportAppCommand;
|
||||||
private readonly IGetPasswordHealthReportApplicationQuery _getPwdHealthReportAppQuery;
|
private readonly IGetPasswordHealthReportApplicationQuery _getPwdHealthReportAppQuery;
|
||||||
private readonly IDropPasswordHealthReportApplicationCommand _dropPwdHealthReportAppCommand;
|
private readonly IDropPasswordHealthReportApplicationCommand _dropPwdHealthReportAppCommand;
|
||||||
|
|
||||||
public ReportsController(
|
public ReportsController(
|
||||||
ICurrentContext currentContext,
|
ICurrentContext currentContext,
|
||||||
IMemberAccessCipherDetailsQuery memberAccessCipherDetailsQuery,
|
IMemberAccessReportQuery memberAccessReportQuery,
|
||||||
|
IRiskInsightsReportQuery riskInsightsReportQuery,
|
||||||
IAddPasswordHealthReportApplicationCommand addPasswordHealthReportApplicationCommand,
|
IAddPasswordHealthReportApplicationCommand addPasswordHealthReportApplicationCommand,
|
||||||
IGetPasswordHealthReportApplicationQuery getPasswordHealthReportApplicationQuery,
|
IGetPasswordHealthReportApplicationQuery getPasswordHealthReportApplicationQuery,
|
||||||
IDropPasswordHealthReportApplicationCommand dropPwdHealthReportAppCommand
|
IDropPasswordHealthReportApplicationCommand dropPwdHealthReportAppCommand
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
_currentContext = currentContext;
|
_currentContext = currentContext;
|
||||||
_memberAccessCipherDetailsQuery = memberAccessCipherDetailsQuery;
|
_memberAccessReportQuery = memberAccessReportQuery;
|
||||||
|
_riskInsightsReportQuery = riskInsightsReportQuery;
|
||||||
_addPwdHealthReportAppCommand = addPasswordHealthReportApplicationCommand;
|
_addPwdHealthReportAppCommand = addPasswordHealthReportApplicationCommand;
|
||||||
_getPwdHealthReportAppQuery = getPasswordHealthReportApplicationQuery;
|
_getPwdHealthReportAppQuery = getPasswordHealthReportApplicationQuery;
|
||||||
_dropPwdHealthReportAppCommand = dropPwdHealthReportAppCommand;
|
_dropPwdHealthReportAppCommand = dropPwdHealthReportAppCommand;
|
||||||
@ -54,9 +58,9 @@ public class ReportsController : Controller
|
|||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var memberCipherDetails = await GetMemberCipherDetails(new MemberAccessCipherDetailsRequest { OrganizationId = orgId });
|
var riskDetails = await GetRiskInsightsReportDetails(new RiskInsightsReportRequest { OrganizationId = orgId });
|
||||||
|
|
||||||
var responses = memberCipherDetails.Select(x => new MemberCipherDetailsResponseModel(x));
|
var responses = riskDetails.Select(x => new MemberCipherDetailsResponseModel(x));
|
||||||
|
|
||||||
return responses;
|
return responses;
|
||||||
}
|
}
|
||||||
@ -69,16 +73,16 @@ public class ReportsController : Controller
|
|||||||
/// <returns>IEnumerable of MemberAccessReportResponseModel</returns>
|
/// <returns>IEnumerable of MemberAccessReportResponseModel</returns>
|
||||||
/// <exception cref="NotFoundException">If Access reports permission is not assigned</exception>
|
/// <exception cref="NotFoundException">If Access reports permission is not assigned</exception>
|
||||||
[HttpGet("member-access/{orgId}")]
|
[HttpGet("member-access/{orgId}")]
|
||||||
public async Task<IEnumerable<MemberAccessReportResponseModel>> GetMemberAccessReport(Guid orgId)
|
public async Task<IEnumerable<MemberAccessDetailReportResponseModel>> GetMemberAccessReport(Guid orgId)
|
||||||
{
|
{
|
||||||
if (!await _currentContext.AccessReports(orgId))
|
if (!await _currentContext.AccessReports(orgId))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var memberCipherDetails = await GetMemberCipherDetails(new MemberAccessCipherDetailsRequest { OrganizationId = orgId });
|
var accessDetails = await GetMemberAccessDetails(new MemberAccessReportRequest { OrganizationId = orgId });
|
||||||
|
|
||||||
var responses = memberCipherDetails.Select(x => new MemberAccessReportResponseModel(x));
|
var responses = accessDetails.Select(x => new MemberAccessDetailReportResponseModel(x));
|
||||||
|
|
||||||
return responses;
|
return responses;
|
||||||
}
|
}
|
||||||
@ -87,13 +91,28 @@ public class ReportsController : Controller
|
|||||||
/// Contains the organization member info, the cipher ids associated with the member,
|
/// Contains the organization member info, the cipher ids associated with the member,
|
||||||
/// and details on their collections, groups, and permissions
|
/// and details on their collections, groups, and permissions
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="request">Request to the MemberAccessCipherDetailsQuery</param>
|
/// <param name="request">Request parameters</param>
|
||||||
/// <returns>IEnumerable of MemberAccessCipherDetails</returns>
|
/// <returns>
|
||||||
private async Task<IEnumerable<MemberAccessCipherDetails>> GetMemberCipherDetails(MemberAccessCipherDetailsRequest request)
|
/// List of a user's permissions at a group and collection level as well as the number of ciphers
|
||||||
|
/// associated with that group/collection
|
||||||
|
/// </returns>
|
||||||
|
private async Task<IEnumerable<MemberAccessReportDetail>> GetMemberAccessDetails(
|
||||||
|
MemberAccessReportRequest request)
|
||||||
{
|
{
|
||||||
var memberCipherDetails =
|
var accessDetails = await _memberAccessReportQuery.GetMemberAccessReportsAsync(request);
|
||||||
await _memberAccessCipherDetailsQuery.GetMemberAccessCipherDetails(request);
|
return accessDetails;
|
||||||
return memberCipherDetails;
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the risk insights report details from the risk insights query. Associates a user to their cipher ids
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request">Request parameters</param>
|
||||||
|
/// <returns>A list of risk insights data associating the user to cipher ids</returns>
|
||||||
|
private async Task<IEnumerable<RiskInsightsReportDetail>> GetRiskInsightsReportDetails(
|
||||||
|
RiskInsightsReportRequest request)
|
||||||
|
{
|
||||||
|
var riskDetails = await _riskInsightsReportQuery.GetRiskInsightsReportDetails(request);
|
||||||
|
return riskDetails;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
using Bit.Core.Dirt.Reports.Models.Data;
|
||||||
|
|
||||||
|
namespace Bit.Api.Tools.Models.Response;
|
||||||
|
|
||||||
|
public class MemberAccessDetailReportResponseModel
|
||||||
|
{
|
||||||
|
public Guid? UserGuid { get; set; }
|
||||||
|
public string UserName { get; set; }
|
||||||
|
public string Email { get; set; }
|
||||||
|
public bool TwoFactorEnabled { get; set; }
|
||||||
|
public bool AccountRecoveryEnabled { get; set; }
|
||||||
|
public bool UsesKeyConnector { get; set; }
|
||||||
|
public Guid? CollectionId { get; set; }
|
||||||
|
public Guid? GroupId { get; set; }
|
||||||
|
public string GroupName { get; set; }
|
||||||
|
public string CollectionName { get; set; }
|
||||||
|
public bool? ReadOnly { get; set; }
|
||||||
|
public bool? HidePasswords { get; set; }
|
||||||
|
public bool? Manage { get; set; }
|
||||||
|
public IEnumerable<Guid> CipherIds { get; set; }
|
||||||
|
|
||||||
|
public MemberAccessDetailReportResponseModel(MemberAccessReportDetail reportDetail)
|
||||||
|
{
|
||||||
|
UserGuid = reportDetail.UserGuid;
|
||||||
|
UserName = reportDetail.UserName;
|
||||||
|
Email = reportDetail.Email;
|
||||||
|
TwoFactorEnabled = reportDetail.TwoFactorEnabled;
|
||||||
|
AccountRecoveryEnabled = reportDetail.AccountRecoveryEnabled;
|
||||||
|
UsesKeyConnector = reportDetail.UsesKeyConnector;
|
||||||
|
CollectionId = reportDetail.CollectionId;
|
||||||
|
GroupId = reportDetail.GroupId;
|
||||||
|
GroupName = reportDetail.GroupName;
|
||||||
|
CollectionName = reportDetail.CollectionName;
|
||||||
|
ReadOnly = reportDetail.ReadOnly;
|
||||||
|
HidePasswords = reportDetail.HidePasswords;
|
||||||
|
Manage = reportDetail.Manage;
|
||||||
|
CipherIds = reportDetail.CipherIds;
|
||||||
|
}
|
||||||
|
}
|
@ -15,12 +15,12 @@ public class MemberCipherDetailsResponseModel
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public IEnumerable<string> CipherIds { get; set; }
|
public IEnumerable<string> CipherIds { get; set; }
|
||||||
|
|
||||||
public MemberCipherDetailsResponseModel(MemberAccessCipherDetails memberAccessCipherDetails)
|
public MemberCipherDetailsResponseModel(RiskInsightsReportDetail reportDetail)
|
||||||
{
|
{
|
||||||
this.UserGuid = memberAccessCipherDetails.UserGuid;
|
this.UserGuid = reportDetail.UserGuid;
|
||||||
this.UserName = memberAccessCipherDetails.UserName;
|
this.UserName = reportDetail.UserName;
|
||||||
this.Email = memberAccessCipherDetails.Email;
|
this.Email = reportDetail.Email;
|
||||||
this.UsesKeyConnector = memberAccessCipherDetails.UsesKeyConnector;
|
this.UsesKeyConnector = reportDetail.UsesKeyConnector;
|
||||||
this.CipherIds = memberAccessCipherDetails.CipherIds;
|
this.CipherIds = reportDetail.CipherIds;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
namespace Bit.Core.Dirt.Reports.Models.Data;
|
||||||
|
|
||||||
|
public class MemberAccessReportDetail
|
||||||
|
{
|
||||||
|
public Guid? UserGuid { get; set; }
|
||||||
|
public string UserName { get; set; }
|
||||||
|
public string Email { get; set; }
|
||||||
|
public bool TwoFactorEnabled { get; set; }
|
||||||
|
public bool AccountRecoveryEnabled { get; set; }
|
||||||
|
public bool UsesKeyConnector { get; set; }
|
||||||
|
public Guid? CollectionId { get; set; }
|
||||||
|
public Guid? GroupId { get; set; }
|
||||||
|
public string GroupName { get; set; }
|
||||||
|
public string CollectionName { get; set; }
|
||||||
|
public bool? ReadOnly { get; set; }
|
||||||
|
public bool? HidePasswords { get; set; }
|
||||||
|
public bool? Manage { get; set; }
|
||||||
|
public IEnumerable<Guid> CipherIds { get; set; }
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
namespace Bit.Core.Dirt.Reports.Models.Data;
|
||||||
|
|
||||||
|
public class OrganizationMemberBaseDetail
|
||||||
|
{
|
||||||
|
public Guid? UserGuid { get; set; }
|
||||||
|
public string UserName { get; set; }
|
||||||
|
public string Email { get; set; }
|
||||||
|
public string TwoFactorProviders { get; set; }
|
||||||
|
public bool UsesKeyConnector { get; set; }
|
||||||
|
public string ResetPasswordKey { get; set; }
|
||||||
|
public Guid? CollectionId { get; set; }
|
||||||
|
public Guid? GroupId { get; set; }
|
||||||
|
public string GroupName { get; set; }
|
||||||
|
public string CollectionName { get; set; }
|
||||||
|
public bool? ReadOnly { get; set; }
|
||||||
|
public bool? HidePasswords { get; set; }
|
||||||
|
public bool? Manage { get; set; }
|
||||||
|
public Guid CipherId { get; set; }
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
namespace Bit.Core.Dirt.Reports.Models.Data;
|
||||||
|
|
||||||
|
public class RiskInsightsReportDetail
|
||||||
|
{
|
||||||
|
public Guid? UserGuid { get; set; }
|
||||||
|
public string UserName { get; set; }
|
||||||
|
public string Email { get; set; }
|
||||||
|
public bool UsesKeyConnector { get; set; }
|
||||||
|
public IEnumerable<string> CipherIds { get; set; }
|
||||||
|
}
|
@ -1,206 +0,0 @@
|
|||||||
using System.Collections.Concurrent;
|
|
||||||
using Bit.Core.AdminConsole.Entities;
|
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
|
||||||
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
|
||||||
using Bit.Core.Dirt.Reports.Models.Data;
|
|
||||||
using Bit.Core.Dirt.Reports.ReportFeatures.OrganizationReportMembers.Interfaces;
|
|
||||||
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
|
||||||
using Bit.Core.Entities;
|
|
||||||
using Bit.Core.Models.Data;
|
|
||||||
using Bit.Core.Models.Data.Organizations;
|
|
||||||
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
|
||||||
using Bit.Core.Repositories;
|
|
||||||
using Bit.Core.Services;
|
|
||||||
using Bit.Core.Vault.Models.Data;
|
|
||||||
using Bit.Core.Vault.Queries;
|
|
||||||
using Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
|
||||||
using Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Requests;
|
|
||||||
|
|
||||||
namespace Bit.Core.Dirt.Reports.ReportFeatures;
|
|
||||||
|
|
||||||
public class MemberAccessCipherDetailsQuery : IMemberAccessCipherDetailsQuery
|
|
||||||
{
|
|
||||||
private readonly IOrganizationUserUserDetailsQuery _organizationUserUserDetailsQuery;
|
|
||||||
private readonly IGroupRepository _groupRepository;
|
|
||||||
private readonly ICollectionRepository _collectionRepository;
|
|
||||||
private readonly IOrganizationCiphersQuery _organizationCiphersQuery;
|
|
||||||
private readonly IApplicationCacheService _applicationCacheService;
|
|
||||||
private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery;
|
|
||||||
|
|
||||||
public MemberAccessCipherDetailsQuery(
|
|
||||||
IOrganizationUserUserDetailsQuery organizationUserUserDetailsQuery,
|
|
||||||
IGroupRepository groupRepository,
|
|
||||||
ICollectionRepository collectionRepository,
|
|
||||||
IOrganizationCiphersQuery organizationCiphersQuery,
|
|
||||||
IApplicationCacheService applicationCacheService,
|
|
||||||
ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery
|
|
||||||
)
|
|
||||||
{
|
|
||||||
_organizationUserUserDetailsQuery = organizationUserUserDetailsQuery;
|
|
||||||
_groupRepository = groupRepository;
|
|
||||||
_collectionRepository = collectionRepository;
|
|
||||||
_organizationCiphersQuery = organizationCiphersQuery;
|
|
||||||
_applicationCacheService = applicationCacheService;
|
|
||||||
_twoFactorIsEnabledQuery = twoFactorIsEnabledQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<MemberAccessCipherDetails>> GetMemberAccessCipherDetails(MemberAccessCipherDetailsRequest request)
|
|
||||||
{
|
|
||||||
var orgUsers = await _organizationUserUserDetailsQuery.GetOrganizationUserUserDetails(
|
|
||||||
new OrganizationUserUserDetailsQueryRequest
|
|
||||||
{
|
|
||||||
OrganizationId = request.OrganizationId,
|
|
||||||
IncludeCollections = true,
|
|
||||||
IncludeGroups = true
|
|
||||||
});
|
|
||||||
|
|
||||||
var orgGroups = await _groupRepository.GetManyByOrganizationIdAsync(request.OrganizationId);
|
|
||||||
var orgAbility = await _applicationCacheService.GetOrganizationAbilityAsync(request.OrganizationId);
|
|
||||||
var orgCollectionsWithAccess = await _collectionRepository.GetManyByOrganizationIdWithAccessAsync(request.OrganizationId);
|
|
||||||
var orgItems = await _organizationCiphersQuery.GetAllOrganizationCiphers(request.OrganizationId);
|
|
||||||
var organizationUsersTwoFactorEnabled = await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(orgUsers);
|
|
||||||
|
|
||||||
var memberAccessCipherDetails = GenerateAccessDataParallel(
|
|
||||||
orgGroups,
|
|
||||||
orgCollectionsWithAccess,
|
|
||||||
orgItems,
|
|
||||||
organizationUsersTwoFactorEnabled,
|
|
||||||
orgAbility);
|
|
||||||
|
|
||||||
return memberAccessCipherDetails;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Generates a report for all members of an organization. Containing summary information
|
|
||||||
/// such as item, collection, and group counts. Including the cipherIds a member is assigned.
|
|
||||||
/// Child collection includes detailed information on the user and group collections along
|
|
||||||
/// with their permissions.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="orgGroups">Organization groups collection</param>
|
|
||||||
/// <param name="orgCollectionsWithAccess">Collections for the organization and the groups/users and permissions</param>
|
|
||||||
/// <param name="orgItems">Cipher items for the organization with the collections associated with them</param>
|
|
||||||
/// <param name="organizationUsersTwoFactorEnabled">Organization users and two factor status</param>
|
|
||||||
/// <param name="orgAbility">Organization ability for account recovery status</param>
|
|
||||||
/// <returns>List of the MemberAccessCipherDetailsModel</returns>;
|
|
||||||
private IEnumerable<MemberAccessCipherDetails> GenerateAccessDataParallel(
|
|
||||||
ICollection<Group> orgGroups,
|
|
||||||
ICollection<Tuple<Collection, CollectionAccessDetails>> orgCollectionsWithAccess,
|
|
||||||
IEnumerable<CipherOrganizationDetailsWithCollections> orgItems,
|
|
||||||
IEnumerable<(OrganizationUserUserDetails user, bool twoFactorIsEnabled)> organizationUsersTwoFactorEnabled,
|
|
||||||
OrganizationAbility orgAbility)
|
|
||||||
{
|
|
||||||
var orgUsers = organizationUsersTwoFactorEnabled.Select(x => x.user).ToList();
|
|
||||||
var groupNameDictionary = orgGroups.ToDictionary(x => x.Id, x => x.Name);
|
|
||||||
var collectionItems = orgItems
|
|
||||||
.SelectMany(x => x.CollectionIds,
|
|
||||||
(cipher, collectionId) => new { Cipher = cipher, CollectionId = collectionId })
|
|
||||||
.GroupBy(y => y.CollectionId,
|
|
||||||
(key, ciphers) => new { CollectionId = key, Ciphers = ciphers });
|
|
||||||
var itemLookup = collectionItems.ToDictionary(x => x.CollectionId.ToString(), x => x.Ciphers.Select(c => c.Cipher.Id.ToString()).ToList());
|
|
||||||
|
|
||||||
var memberAccessCipherDetails = new ConcurrentBag<MemberAccessCipherDetails>();
|
|
||||||
|
|
||||||
Parallel.ForEach(orgUsers, user =>
|
|
||||||
{
|
|
||||||
var groupAccessDetails = new List<MemberAccessDetails>();
|
|
||||||
var userCollectionAccessDetails = new List<MemberAccessDetails>();
|
|
||||||
|
|
||||||
foreach (var tCollect in orgCollectionsWithAccess)
|
|
||||||
{
|
|
||||||
if (itemLookup.TryGetValue(tCollect.Item1.Id.ToString(), out var items))
|
|
||||||
{
|
|
||||||
var itemCounts = items.Count;
|
|
||||||
|
|
||||||
if (tCollect.Item2.Groups.Any())
|
|
||||||
{
|
|
||||||
var groupDetails = tCollect.Item2.Groups
|
|
||||||
.Where(tCollectGroups => user.Groups.Contains(tCollectGroups.Id))
|
|
||||||
.Select(x => new MemberAccessDetails
|
|
||||||
{
|
|
||||||
CollectionId = tCollect.Item1.Id,
|
|
||||||
CollectionName = tCollect.Item1.Name,
|
|
||||||
GroupId = x.Id,
|
|
||||||
GroupName = groupNameDictionary[x.Id],
|
|
||||||
ReadOnly = x.ReadOnly,
|
|
||||||
HidePasswords = x.HidePasswords,
|
|
||||||
Manage = x.Manage,
|
|
||||||
ItemCount = itemCounts,
|
|
||||||
CollectionCipherIds = items
|
|
||||||
});
|
|
||||||
|
|
||||||
groupAccessDetails.AddRange(groupDetails);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tCollect.Item2.Users.Any())
|
|
||||||
{
|
|
||||||
var userCollectionDetails = tCollect.Item2.Users
|
|
||||||
.Where(tCollectUser => tCollectUser.Id == user.Id)
|
|
||||||
.Select(x => new MemberAccessDetails
|
|
||||||
{
|
|
||||||
CollectionId = tCollect.Item1.Id,
|
|
||||||
CollectionName = tCollect.Item1.Name,
|
|
||||||
ReadOnly = x.ReadOnly,
|
|
||||||
HidePasswords = x.HidePasswords,
|
|
||||||
Manage = x.Manage,
|
|
||||||
ItemCount = itemCounts,
|
|
||||||
CollectionCipherIds = items
|
|
||||||
});
|
|
||||||
|
|
||||||
userCollectionAccessDetails.AddRange(userCollectionDetails);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var report = new MemberAccessCipherDetails
|
|
||||||
{
|
|
||||||
UserName = user.Name,
|
|
||||||
Email = user.Email,
|
|
||||||
TwoFactorEnabled = organizationUsersTwoFactorEnabled.FirstOrDefault(u => u.user.Id == user.Id).twoFactorIsEnabled,
|
|
||||||
AccountRecoveryEnabled = !string.IsNullOrEmpty(user.ResetPasswordKey) && orgAbility.UseResetPassword,
|
|
||||||
UserGuid = user.Id,
|
|
||||||
UsesKeyConnector = user.UsesKeyConnector
|
|
||||||
};
|
|
||||||
|
|
||||||
var userAccessDetails = new List<MemberAccessDetails>();
|
|
||||||
if (user.Groups.Any())
|
|
||||||
{
|
|
||||||
var userGroups = groupAccessDetails.Where(x => user.Groups.Contains(x.GroupId.GetValueOrDefault()));
|
|
||||||
userAccessDetails.AddRange(userGroups);
|
|
||||||
}
|
|
||||||
|
|
||||||
var groupsWithoutCollections = user.Groups.Where(x => !userAccessDetails.Any(y => x == y.GroupId));
|
|
||||||
if (groupsWithoutCollections.Any())
|
|
||||||
{
|
|
||||||
var emptyGroups = groupsWithoutCollections.Select(x => new MemberAccessDetails
|
|
||||||
{
|
|
||||||
GroupId = x,
|
|
||||||
GroupName = groupNameDictionary[x],
|
|
||||||
ItemCount = 0
|
|
||||||
});
|
|
||||||
userAccessDetails.AddRange(emptyGroups);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user.Collections.Any())
|
|
||||||
{
|
|
||||||
var userCollections = userCollectionAccessDetails.Where(x => user.Collections.Any(y => x.CollectionId == y.Id));
|
|
||||||
userAccessDetails.AddRange(userCollections);
|
|
||||||
}
|
|
||||||
report.AccessDetails = userAccessDetails;
|
|
||||||
|
|
||||||
var userCiphers = report.AccessDetails
|
|
||||||
.Where(x => x.ItemCount > 0)
|
|
||||||
.SelectMany(y => y.CollectionCipherIds)
|
|
||||||
.Distinct();
|
|
||||||
report.CipherIds = userCiphers;
|
|
||||||
report.TotalItemCount = userCiphers.Count();
|
|
||||||
|
|
||||||
var distinctItems = report.AccessDetails.Where(x => x.CollectionId.HasValue).Select(x => x.CollectionId).Distinct();
|
|
||||||
report.CollectionsCount = distinctItems.Count();
|
|
||||||
report.GroupsCount = report.AccessDetails.Select(x => x.GroupId).Where(y => y.HasValue).Distinct().Count();
|
|
||||||
|
|
||||||
memberAccessCipherDetails.Add(report);
|
|
||||||
});
|
|
||||||
|
|
||||||
return memberAccessCipherDetails;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,65 @@
|
|||||||
|
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
||||||
|
using Bit.Core.Dirt.Reports.Models.Data;
|
||||||
|
using Bit.Core.Dirt.Reports.ReportFeatures.OrganizationReportMembers.Interfaces;
|
||||||
|
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
||||||
|
using Bit.Core.Dirt.Reports.Repositories;
|
||||||
|
using Bit.Core.Services;
|
||||||
|
|
||||||
|
namespace Bit.Core.Dirt.Reports.ReportFeatures;
|
||||||
|
|
||||||
|
public class MemberAccessReportQuery(
|
||||||
|
IOrganizationMemberBaseDetailRepository organizationMemberBaseDetailRepository,
|
||||||
|
ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery,
|
||||||
|
IApplicationCacheService applicationCacheService)
|
||||||
|
: IMemberAccessReportQuery
|
||||||
|
{
|
||||||
|
public async Task<IEnumerable<MemberAccessReportDetail>> GetMemberAccessReportsAsync(
|
||||||
|
MemberAccessReportRequest request)
|
||||||
|
{
|
||||||
|
var baseDetails =
|
||||||
|
await organizationMemberBaseDetailRepository.GetOrganizationMemberBaseDetailsByOrganizationId(
|
||||||
|
request.OrganizationId);
|
||||||
|
|
||||||
|
var orgUsers = baseDetails.Select(x => x.UserGuid.GetValueOrDefault()).Distinct();
|
||||||
|
var orgUsersTwoFactorEnabled = await twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(orgUsers);
|
||||||
|
|
||||||
|
var orgAbility = await applicationCacheService.GetOrganizationAbilityAsync(request.OrganizationId);
|
||||||
|
|
||||||
|
var accessDetails = baseDetails
|
||||||
|
.GroupBy(b => new
|
||||||
|
{
|
||||||
|
b.UserGuid,
|
||||||
|
b.UserName,
|
||||||
|
b.Email,
|
||||||
|
b.TwoFactorProviders,
|
||||||
|
b.ResetPasswordKey,
|
||||||
|
b.UsesKeyConnector,
|
||||||
|
b.GroupId,
|
||||||
|
b.GroupName,
|
||||||
|
b.CollectionId,
|
||||||
|
b.CollectionName,
|
||||||
|
b.ReadOnly,
|
||||||
|
b.HidePasswords,
|
||||||
|
b.Manage
|
||||||
|
})
|
||||||
|
.Select(g => new MemberAccessReportDetail
|
||||||
|
{
|
||||||
|
UserGuid = g.Key.UserGuid,
|
||||||
|
UserName = g.Key.UserName,
|
||||||
|
Email = g.Key.Email,
|
||||||
|
TwoFactorEnabled = orgUsersTwoFactorEnabled.FirstOrDefault(x => x.userId == g.Key.UserGuid).twoFactorIsEnabled,
|
||||||
|
AccountRecoveryEnabled = !string.IsNullOrWhiteSpace(g.Key.ResetPasswordKey) && orgAbility.UseResetPassword,
|
||||||
|
UsesKeyConnector = g.Key.UsesKeyConnector,
|
||||||
|
GroupId = g.Key.GroupId,
|
||||||
|
GroupName = g.Key.GroupName,
|
||||||
|
CollectionId = g.Key.CollectionId,
|
||||||
|
CollectionName = g.Key.CollectionName,
|
||||||
|
ReadOnly = g.Key.ReadOnly,
|
||||||
|
HidePasswords = g.Key.HidePasswords,
|
||||||
|
Manage = g.Key.Manage,
|
||||||
|
CipherIds = g.Select(c => c.CipherId)
|
||||||
|
});
|
||||||
|
|
||||||
|
return accessDetails;
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,7 @@ using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
|||||||
|
|
||||||
namespace Bit.Core.Dirt.Reports.ReportFeatures.OrganizationReportMembers.Interfaces;
|
namespace Bit.Core.Dirt.Reports.ReportFeatures.OrganizationReportMembers.Interfaces;
|
||||||
|
|
||||||
public interface IMemberAccessCipherDetailsQuery
|
public interface IMemberAccessReportQuery
|
||||||
{
|
{
|
||||||
Task<IEnumerable<MemberAccessCipherDetails>> GetMemberAccessCipherDetails(MemberAccessCipherDetailsRequest request);
|
Task<IEnumerable<MemberAccessReportDetail>> GetMemberAccessReportsAsync(MemberAccessReportRequest request);
|
||||||
}
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
using Bit.Core.Dirt.Reports.Models.Data;
|
||||||
|
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
||||||
|
|
||||||
|
namespace Bit.Core.Dirt.Reports.ReportFeatures.OrganizationReportMembers.Interfaces;
|
||||||
|
|
||||||
|
public interface IRiskInsightsReportQuery
|
||||||
|
{
|
||||||
|
Task<IEnumerable<RiskInsightsReportDetail>> GetRiskInsightsReportDetails(RiskInsightsReportRequest request);
|
||||||
|
}
|
@ -8,7 +8,8 @@ public static class ReportingServiceCollectionExtensions
|
|||||||
{
|
{
|
||||||
public static void AddReportingServices(this IServiceCollection services)
|
public static void AddReportingServices(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddScoped<IMemberAccessCipherDetailsQuery, MemberAccessCipherDetailsQuery>();
|
services.AddScoped<IRiskInsightsReportQuery, RiskInsightsReportQuery>();
|
||||||
|
services.AddScoped<IMemberAccessReportQuery, MemberAccessReportQuery>();
|
||||||
services.AddScoped<IAddPasswordHealthReportApplicationCommand, AddPasswordHealthReportApplicationCommand>();
|
services.AddScoped<IAddPasswordHealthReportApplicationCommand, AddPasswordHealthReportApplicationCommand>();
|
||||||
services.AddScoped<IGetPasswordHealthReportApplicationQuery, GetPasswordHealthReportApplicationQuery>();
|
services.AddScoped<IGetPasswordHealthReportApplicationQuery, GetPasswordHealthReportApplicationQuery>();
|
||||||
services.AddScoped<IDropPasswordHealthReportApplicationCommand, DropPasswordHealthReportApplicationCommand>();
|
services.AddScoped<IDropPasswordHealthReportApplicationCommand, DropPasswordHealthReportApplicationCommand>();
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
namespace Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
namespace Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
||||||
|
|
||||||
public class MemberAccessCipherDetailsRequest
|
public class MemberAccessReportRequest
|
||||||
{
|
{
|
||||||
public Guid OrganizationId { get; set; }
|
public Guid OrganizationId { get; set; }
|
||||||
}
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
namespace Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
||||||
|
|
||||||
|
public class RiskInsightsReportRequest
|
||||||
|
{
|
||||||
|
public Guid OrganizationId { get; set; }
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
using Bit.Core.Dirt.Reports.Models.Data;
|
||||||
|
using Bit.Core.Dirt.Reports.ReportFeatures.OrganizationReportMembers.Interfaces;
|
||||||
|
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
||||||
|
using Bit.Core.Dirt.Reports.Repositories;
|
||||||
|
|
||||||
|
namespace Bit.Core.Dirt.Reports.ReportFeatures;
|
||||||
|
|
||||||
|
public class RiskInsightsReportQuery : IRiskInsightsReportQuery
|
||||||
|
{
|
||||||
|
private readonly IOrganizationMemberBaseDetailRepository _organizationMemberBaseDetailRepository;
|
||||||
|
|
||||||
|
public RiskInsightsReportQuery(IOrganizationMemberBaseDetailRepository repository)
|
||||||
|
{
|
||||||
|
_organizationMemberBaseDetailRepository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<RiskInsightsReportDetail>> GetRiskInsightsReportDetails(
|
||||||
|
RiskInsightsReportRequest request)
|
||||||
|
{
|
||||||
|
var baseDetails =
|
||||||
|
await _organizationMemberBaseDetailRepository.GetOrganizationMemberBaseDetailsByOrganizationId(
|
||||||
|
request.OrganizationId);
|
||||||
|
|
||||||
|
var insightsDetails = baseDetails
|
||||||
|
.GroupBy(b => new { b.UserGuid, b.UserName, b.Email, b.UsesKeyConnector })
|
||||||
|
.Select(g => new RiskInsightsReportDetail
|
||||||
|
{
|
||||||
|
UserGuid = g.Key.UserGuid,
|
||||||
|
UserName = g.Key.UserName,
|
||||||
|
Email = g.Key.Email,
|
||||||
|
UsesKeyConnector = g.Key.UsesKeyConnector,
|
||||||
|
CipherIds = g
|
||||||
|
.Select(x => x.CipherId.ToString())
|
||||||
|
.Distinct()
|
||||||
|
});
|
||||||
|
|
||||||
|
return insightsDetails;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
using Bit.Core.Dirt.Reports.Models.Data;
|
||||||
|
|
||||||
|
namespace Bit.Core.Dirt.Reports.Repositories;
|
||||||
|
|
||||||
|
public interface IOrganizationMemberBaseDetailRepository
|
||||||
|
{
|
||||||
|
Task<IEnumerable<OrganizationMemberBaseDetail>> GetOrganizationMemberBaseDetailsByOrganizationId(Guid organizationId);
|
||||||
|
}
|
@ -70,6 +70,7 @@ public static class DapperServiceCollectionExtensions
|
|||||||
services.AddSingleton<ISecurityTaskRepository, SecurityTaskRepository>();
|
services.AddSingleton<ISecurityTaskRepository, SecurityTaskRepository>();
|
||||||
services.AddSingleton<IUserAsymmetricKeysRepository, UserAsymmetricKeysRepository>();
|
services.AddSingleton<IUserAsymmetricKeysRepository, UserAsymmetricKeysRepository>();
|
||||||
services.AddSingleton<IOrganizationInstallationRepository, OrganizationInstallationRepository>();
|
services.AddSingleton<IOrganizationInstallationRepository, OrganizationInstallationRepository>();
|
||||||
|
services.AddSingleton<IOrganizationMemberBaseDetailRepository, OrganizationMemberBaseDetailRepository>();
|
||||||
|
|
||||||
if (selfHosted)
|
if (selfHosted)
|
||||||
{
|
{
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
using System.Data;
|
||||||
|
using Bit.Core.Dirt.Reports.Models.Data;
|
||||||
|
using Bit.Core.Dirt.Reports.Repositories;
|
||||||
|
using Bit.Core.Settings;
|
||||||
|
using Bit.Infrastructure.Dapper.Repositories;
|
||||||
|
using Dapper;
|
||||||
|
using Microsoft.Data.SqlClient;
|
||||||
|
|
||||||
|
namespace Bit.Infrastructure.Dapper.Dirt;
|
||||||
|
|
||||||
|
public class OrganizationMemberBaseDetailRepository : BaseRepository, IOrganizationMemberBaseDetailRepository
|
||||||
|
{
|
||||||
|
public OrganizationMemberBaseDetailRepository(GlobalSettings globalSettings)
|
||||||
|
: this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public OrganizationMemberBaseDetailRepository(string connectionString, string readOnlyConnectionString) : base(
|
||||||
|
connectionString, readOnlyConnectionString)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<OrganizationMemberBaseDetail>> GetOrganizationMemberBaseDetailsByOrganizationId(
|
||||||
|
Guid organizationId)
|
||||||
|
{
|
||||||
|
await using var connection = new SqlConnection(ConnectionString);
|
||||||
|
|
||||||
|
|
||||||
|
var result = await connection.QueryAsync<OrganizationMemberBaseDetail>(
|
||||||
|
"[dbo].[MemberAccessReport_GetMemberAccessCipherDetailsByOrganizationId]",
|
||||||
|
new
|
||||||
|
{
|
||||||
|
OrganizationId = organizationId
|
||||||
|
|
||||||
|
}, commandType: CommandType.StoredProcedure);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
using AutoMapper;
|
||||||
|
using Bit.Core.Dirt.Reports.Models.Data;
|
||||||
|
using Bit.Core.Dirt.Reports.Repositories;
|
||||||
|
using Bit.Infrastructure.EntityFramework.Repositories;
|
||||||
|
using Microsoft.Data.SqlClient;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace Bit.Infrastructure.EntityFramework.Dirt;
|
||||||
|
|
||||||
|
public class OrganizationMemberBaseDetailRepository : BaseEntityFrameworkRepository, IOrganizationMemberBaseDetailRepository
|
||||||
|
{
|
||||||
|
public OrganizationMemberBaseDetailRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper) : base(
|
||||||
|
serviceScopeFactory,
|
||||||
|
mapper)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<OrganizationMemberBaseDetail>> GetOrganizationMemberBaseDetailsByOrganizationId(
|
||||||
|
Guid organizationId)
|
||||||
|
{
|
||||||
|
await using var scope = ServiceScopeFactory.CreateAsyncScope();
|
||||||
|
var dbContext = GetDatabaseContext(scope);
|
||||||
|
|
||||||
|
var result = await dbContext.Set<OrganizationMemberBaseDetail>()
|
||||||
|
.FromSqlRaw("EXEC [dbo].[MemberAccessReport_GetMemberAccessCipherDetailsByOrganizationId] @OrganizationId",
|
||||||
|
new SqlParameter("@OrganizationId", organizationId))
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
@ -14,6 +14,7 @@ using Bit.Core.Vault.Repositories;
|
|||||||
using Bit.Infrastructure.EntityFramework.AdminConsole.Repositories;
|
using Bit.Infrastructure.EntityFramework.AdminConsole.Repositories;
|
||||||
using Bit.Infrastructure.EntityFramework.Auth.Repositories;
|
using Bit.Infrastructure.EntityFramework.Auth.Repositories;
|
||||||
using Bit.Infrastructure.EntityFramework.Billing.Repositories;
|
using Bit.Infrastructure.EntityFramework.Billing.Repositories;
|
||||||
|
using Bit.Infrastructure.EntityFramework.Dirt;
|
||||||
using Bit.Infrastructure.EntityFramework.Dirt.Repositories;
|
using Bit.Infrastructure.EntityFramework.Dirt.Repositories;
|
||||||
using Bit.Infrastructure.EntityFramework.KeyManagement.Repositories;
|
using Bit.Infrastructure.EntityFramework.KeyManagement.Repositories;
|
||||||
using Bit.Infrastructure.EntityFramework.NotificationCenter.Repositories;
|
using Bit.Infrastructure.EntityFramework.NotificationCenter.Repositories;
|
||||||
@ -107,6 +108,7 @@ public static class EntityFrameworkServiceCollectionExtensions
|
|||||||
services.AddSingleton<ISecurityTaskRepository, SecurityTaskRepository>();
|
services.AddSingleton<ISecurityTaskRepository, SecurityTaskRepository>();
|
||||||
services.AddSingleton<IUserAsymmetricKeysRepository, UserAsymmetricKeysRepository>();
|
services.AddSingleton<IUserAsymmetricKeysRepository, UserAsymmetricKeysRepository>();
|
||||||
services.AddSingleton<IOrganizationInstallationRepository, OrganizationInstallationRepository>();
|
services.AddSingleton<IOrganizationInstallationRepository, OrganizationInstallationRepository>();
|
||||||
|
services.AddSingleton<IOrganizationMemberBaseDetailRepository, OrganizationMemberBaseDetailRepository>();
|
||||||
|
|
||||||
if (selfHosted)
|
if (selfHosted)
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Bit.Core;
|
using Bit.Core;
|
||||||
|
using Bit.Core.Dirt.Reports.Models.Data;
|
||||||
using Bit.Infrastructure.EntityFramework.AdminConsole.Models;
|
using Bit.Infrastructure.EntityFramework.AdminConsole.Models;
|
||||||
using Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider;
|
using Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider;
|
||||||
using Bit.Infrastructure.EntityFramework.Auth.Models;
|
using Bit.Infrastructure.EntityFramework.Auth.Models;
|
||||||
@ -80,6 +81,7 @@ public class DatabaseContext : DbContext
|
|||||||
public DbSet<NotificationStatus> NotificationStatuses { get; set; }
|
public DbSet<NotificationStatus> NotificationStatuses { get; set; }
|
||||||
public DbSet<ClientOrganizationMigrationRecord> ClientOrganizationMigrationRecords { get; set; }
|
public DbSet<ClientOrganizationMigrationRecord> ClientOrganizationMigrationRecords { get; set; }
|
||||||
public DbSet<PasswordHealthReportApplication> PasswordHealthReportApplications { get; set; }
|
public DbSet<PasswordHealthReportApplication> PasswordHealthReportApplications { get; set; }
|
||||||
|
public DbSet<OrganizationMemberBaseDetail> OrganizationMemberBaseDetails { get; set; }
|
||||||
public DbSet<SecurityTask> SecurityTasks { get; set; }
|
public DbSet<SecurityTask> SecurityTasks { get; set; }
|
||||||
public DbSet<OrganizationInstallation> OrganizationInstallations { get; set; }
|
public DbSet<OrganizationInstallation> OrganizationInstallations { get; set; }
|
||||||
|
|
||||||
@ -112,6 +114,7 @@ public class DatabaseContext : DbContext
|
|||||||
var eOrganizationConnection = builder.Entity<OrganizationConnection>();
|
var eOrganizationConnection = builder.Entity<OrganizationConnection>();
|
||||||
var eOrganizationDomain = builder.Entity<OrganizationDomain>();
|
var eOrganizationDomain = builder.Entity<OrganizationDomain>();
|
||||||
var aWebAuthnCredential = builder.Entity<WebAuthnCredential>();
|
var aWebAuthnCredential = builder.Entity<WebAuthnCredential>();
|
||||||
|
var eOrganizationMemberBaseDetail = builder.Entity<OrganizationMemberBaseDetail>();
|
||||||
|
|
||||||
// Shadow property configurations go here
|
// Shadow property configurations go here
|
||||||
|
|
||||||
@ -134,6 +137,8 @@ public class DatabaseContext : DbContext
|
|||||||
eCollectionGroup.HasKey(cg => new { cg.CollectionId, cg.GroupId });
|
eCollectionGroup.HasKey(cg => new { cg.CollectionId, cg.GroupId });
|
||||||
eGroupUser.HasKey(gu => new { gu.GroupId, gu.OrganizationUserId });
|
eGroupUser.HasKey(gu => new { gu.GroupId, gu.OrganizationUserId });
|
||||||
|
|
||||||
|
eOrganizationMemberBaseDetail.HasNoKey();
|
||||||
|
|
||||||
var dataProtector = this.GetService<DP.IDataProtectionProvider>().CreateProtector(
|
var dataProtector = this.GetService<DP.IDataProtectionProvider>().CreateProtector(
|
||||||
Constants.DatabaseFieldProtectorPurpose);
|
Constants.DatabaseFieldProtectorPurpose);
|
||||||
var dataProtectionConverter = new DataProtectionConverter(dataProtector);
|
var dataProtectionConverter = new DataProtectionConverter(dataProtector);
|
||||||
|
@ -0,0 +1,92 @@
|
|||||||
|
CREATE PROCEDURE dbo.MemberAccessReport_GetMemberAccessCipherDetailsByOrganizationId
|
||||||
|
@OrganizationId UNIQUEIDENTIFIER
|
||||||
|
AS
|
||||||
|
SET NOCOUNT ON;
|
||||||
|
|
||||||
|
IF @OrganizationId IS NULL
|
||||||
|
THROW 50000, 'OrganizationId cannot be null', 1;
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
OU.Id AS UserGuid,
|
||||||
|
U.Name AS UserName,
|
||||||
|
ISNULL(U.Email, OU.Email) as 'Email',
|
||||||
|
U.TwoFactorProviders,
|
||||||
|
U.UsesKeyConnector,
|
||||||
|
OU.ResetPasswordKey,
|
||||||
|
CC.CollectionId,
|
||||||
|
C.Name AS CollectionName,
|
||||||
|
NULL AS GroupId,
|
||||||
|
NULL AS GroupName,
|
||||||
|
CU.ReadOnly,
|
||||||
|
CU.HidePasswords,
|
||||||
|
CU.Manage,
|
||||||
|
Cipher.Id AS CipherId
|
||||||
|
FROM dbo.OrganizationUser OU
|
||||||
|
LEFT JOIN dbo.[User] U ON U.Id = OU.UserId
|
||||||
|
INNER JOIN dbo.Organization O ON O.Id = OU.OrganizationId
|
||||||
|
AND O.Id = @OrganizationId
|
||||||
|
AND O.Enabled = 1
|
||||||
|
INNER JOIN dbo.CollectionUser CU ON CU.OrganizationUserId = OU.Id
|
||||||
|
INNER JOIN dbo.Collection C ON C.Id = CU.CollectionId and C.OrganizationId = @OrganizationId
|
||||||
|
INNER JOIN dbo.CollectionCipher CC ON CC.CollectionId = C.Id
|
||||||
|
INNER JOIN dbo.Cipher Cipher ON Cipher.Id = CC.CipherId AND Cipher.OrganizationId = @OrganizationId
|
||||||
|
WHERE OU.Status IN (0,1,2) -- Invited, Accepted and Confirmed Users
|
||||||
|
AND Cipher.DeletedDate IS NULL
|
||||||
|
UNION ALL
|
||||||
|
-- Group-based collection permissions
|
||||||
|
SELECT
|
||||||
|
OU.Id AS UserGuid,
|
||||||
|
U.Name AS UserName,
|
||||||
|
ISNULL(U.Email, OU.Email) as 'Email',
|
||||||
|
U.TwoFactorProviders,
|
||||||
|
U.UsesKeyConnector,
|
||||||
|
OU.ResetPasswordKey,
|
||||||
|
CC.CollectionId,
|
||||||
|
C.Name AS CollectionName,
|
||||||
|
G.Id AS GroupId,
|
||||||
|
G.Name AS GroupName,
|
||||||
|
CG.ReadOnly,
|
||||||
|
CG.HidePasswords,
|
||||||
|
CG.Manage,
|
||||||
|
Cipher.Id AS CipherId
|
||||||
|
FROM dbo.OrganizationUser OU
|
||||||
|
LEFT JOIN dbo.[User] U ON U.Id = OU.UserId
|
||||||
|
INNER JOIN dbo.Organization O ON O.Id = OU.OrganizationId
|
||||||
|
AND O.Id = @OrganizationId
|
||||||
|
AND O.Enabled = 1
|
||||||
|
INNER JOIN dbo.GroupUser GU ON GU.OrganizationUserId = OU.Id
|
||||||
|
INNER JOIN dbo.[Group] G ON G.Id = GU.GroupId
|
||||||
|
INNER JOIN dbo.CollectionGroup CG ON CG.GroupId = G.Id
|
||||||
|
INNER JOIN dbo.Collection C ON C.Id = CG.CollectionId AND C.OrganizationId = @OrganizationId
|
||||||
|
INNER JOIN dbo.CollectionCipher CC ON CC.CollectionId = C.Id
|
||||||
|
INNER JOIN dbo.Cipher Cipher ON Cipher.Id = CC.CipherId and Cipher.OrganizationId = @OrganizationId
|
||||||
|
WHERE OU.Status IN (0,1,2) -- Invited, Accepted and Confirmed Users
|
||||||
|
AND Cipher.DeletedDate IS NULL
|
||||||
|
UNION ALL
|
||||||
|
-- Users without collection access (invited users)
|
||||||
|
-- typically invited users who have not yet accepted the invitation
|
||||||
|
-- and not yet assigned to any collection
|
||||||
|
SELECT
|
||||||
|
OU.Id AS UserGuid,
|
||||||
|
U.Name AS UserName,
|
||||||
|
ISNULL(U.Email, OU.Email) as 'Email',
|
||||||
|
U.TwoFactorProviders,
|
||||||
|
U.UsesKeyConnector,
|
||||||
|
OU.ResetPasswordKey,
|
||||||
|
null as CollectionId,
|
||||||
|
null AS CollectionName,
|
||||||
|
NULL AS GroupId,
|
||||||
|
NULL AS GroupName,
|
||||||
|
null as [ReadOnly],
|
||||||
|
null as HidePasswords,
|
||||||
|
null as Manage,
|
||||||
|
null AS CipherId
|
||||||
|
FROM dbo.OrganizationUser OU
|
||||||
|
LEFT JOIN dbo.[User] U ON U.Id = OU.UserId
|
||||||
|
INNER JOIN dbo.Organization O ON O.Id = OU.OrganizationId AND O.Id = @OrganizationId AND O.Enabled = 1
|
||||||
|
WHERE OU.Status IN (0,1,2) -- Invited, Accepted and Confirmed Users
|
||||||
|
AND OU.Id not in (
|
||||||
|
select OU1.Id from dbo.OrganizationUser OU1
|
||||||
|
inner join dbo.CollectionUser CU1 on CU1.OrganizationUserId = OU1.Id
|
||||||
|
WHERE OU1.OrganizationId = @organizationId
|
||||||
|
)
|
@ -0,0 +1,70 @@
|
|||||||
|
CREATE OR ALTER PROC dbo.MemberAccessReport_GetMemberAccessCipherDetailsByOrganizationId
|
||||||
|
@OrganizationId UNIQUEIDENTIFIER
|
||||||
|
AS
|
||||||
|
SET NOCOUNT ON;
|
||||||
|
|
||||||
|
IF @OrganizationId IS NULL
|
||||||
|
THROW 50000, 'OrganizationId cannot be null', 1;
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
U.Id AS UserGuid,
|
||||||
|
U.Name AS UserName,
|
||||||
|
U.Email,
|
||||||
|
U.TwoFactorProviders,
|
||||||
|
U.UsesKeyConnector,
|
||||||
|
OU.ResetPasswordKey,
|
||||||
|
CC.CollectionId,
|
||||||
|
C.Name AS CollectionName,
|
||||||
|
NULL AS GroupId,
|
||||||
|
NULL AS GroupName,
|
||||||
|
CU.ReadOnly,
|
||||||
|
CU.HidePasswords,
|
||||||
|
CU.Manage,
|
||||||
|
Cipher.Id AS CipherId
|
||||||
|
FROM dbo.OrganizationUser OU
|
||||||
|
INNER JOIN dbo.[User] U ON U.Id = OU.UserId
|
||||||
|
INNER JOIN dbo.Organization O ON O.Id = OU.OrganizationId
|
||||||
|
AND O.Id = @OrganizationId
|
||||||
|
AND O.Enabled = 1
|
||||||
|
INNER JOIN dbo.CollectionUser CU ON CU.OrganizationUserId = OU.Id
|
||||||
|
INNER JOIN dbo.Collection C ON C.Id = CU.CollectionId
|
||||||
|
INNER JOIN dbo.CollectionCipher CC ON CC.CollectionId = C.Id
|
||||||
|
INNER JOIN dbo.Cipher Cipher ON Cipher.Id = CC.CipherId
|
||||||
|
AND Cipher.OrganizationId = @OrganizationId
|
||||||
|
WHERE OU.Status in (0,1,2)
|
||||||
|
AND Cipher.DeletedDate IS NULL
|
||||||
|
|
||||||
|
UNION ALL
|
||||||
|
|
||||||
|
-- Group-based collection permissions
|
||||||
|
SELECT
|
||||||
|
U.Id AS UserGuid,
|
||||||
|
U.Name AS UserName,
|
||||||
|
U.Email,
|
||||||
|
U.TwoFactorProviders,
|
||||||
|
U.UsesKeyConnector,
|
||||||
|
OU.ResetPasswordKey,
|
||||||
|
CC.CollectionId,
|
||||||
|
C.Name AS CollectionName,
|
||||||
|
G.Id AS GroupId,
|
||||||
|
G.Name AS GroupName,
|
||||||
|
CG.ReadOnly,
|
||||||
|
CG.HidePasswords,
|
||||||
|
CG.Manage,
|
||||||
|
Cipher.Id AS CipherId
|
||||||
|
FROM dbo.OrganizationUser OU
|
||||||
|
INNER JOIN dbo.[User] U ON U.Id = OU.UserId
|
||||||
|
INNER JOIN dbo.Organization O ON O.Id = OU.OrganizationId
|
||||||
|
AND O.Id = @OrganizationId
|
||||||
|
AND O.Enabled = 1
|
||||||
|
INNER JOIN dbo.GroupUser GU ON GU.OrganizationUserId = OU.Id
|
||||||
|
INNER JOIN dbo.[Group] G ON G.Id = GU.GroupId
|
||||||
|
INNER JOIN dbo.CollectionGroup CG ON CG.GroupId = G.Id
|
||||||
|
INNER JOIN dbo.Collection C ON C.Id = CG.CollectionId
|
||||||
|
INNER JOIN dbo.CollectionCipher CC ON CC.CollectionId = C.Id
|
||||||
|
INNER JOIN dbo.Cipher Cipher ON Cipher.Id = CC.CipherId
|
||||||
|
AND Cipher.OrganizationId = @OrganizationId
|
||||||
|
WHERE OU.Status in (0,1,2)
|
||||||
|
AND Cipher.DeletedDate IS NULL
|
||||||
|
|
||||||
|
GO
|
@ -0,0 +1,94 @@
|
|||||||
|
CREATE OR ALTER PROC dbo.MemberAccessReport_GetMemberAccessCipherDetailsByOrganizationId
|
||||||
|
@OrganizationId UNIQUEIDENTIFIER
|
||||||
|
AS
|
||||||
|
SET NOCOUNT ON;
|
||||||
|
|
||||||
|
IF @OrganizationId IS NULL
|
||||||
|
THROW 50000, 'OrganizationId cannot be null', 1;
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
OU.Id AS UserGuid,
|
||||||
|
U.Name AS UserName,
|
||||||
|
ISNULL(U.Email, OU.Email) as 'Email',
|
||||||
|
U.TwoFactorProviders,
|
||||||
|
U.UsesKeyConnector,
|
||||||
|
OU.ResetPasswordKey,
|
||||||
|
CC.CollectionId,
|
||||||
|
C.Name AS CollectionName,
|
||||||
|
NULL AS GroupId,
|
||||||
|
NULL AS GroupName,
|
||||||
|
CU.ReadOnly,
|
||||||
|
CU.HidePasswords,
|
||||||
|
CU.Manage,
|
||||||
|
Cipher.Id AS CipherId
|
||||||
|
FROM dbo.OrganizationUser OU
|
||||||
|
LEFT JOIN dbo.[User] U ON U.Id = OU.UserId
|
||||||
|
INNER JOIN dbo.Organization O ON O.Id = OU.OrganizationId
|
||||||
|
AND O.Id = @OrganizationId
|
||||||
|
AND O.Enabled = 1
|
||||||
|
INNER JOIN dbo.CollectionUser CU ON CU.OrganizationUserId = OU.Id
|
||||||
|
INNER JOIN dbo.Collection C ON C.Id = CU.CollectionId and C.OrganizationId = @OrganizationId
|
||||||
|
INNER JOIN dbo.CollectionCipher CC ON CC.CollectionId = C.Id
|
||||||
|
INNER JOIN dbo.Cipher Cipher ON Cipher.Id = CC.CipherId AND Cipher.OrganizationId = @OrganizationId
|
||||||
|
WHERE OU.Status IN (0,1,2) -- Invited, Accepted and Confirmed Users
|
||||||
|
AND Cipher.DeletedDate IS NULL
|
||||||
|
UNION ALL
|
||||||
|
-- Group-based collection permissions
|
||||||
|
SELECT
|
||||||
|
OU.Id AS UserGuid,
|
||||||
|
U.Name AS UserName,
|
||||||
|
ISNULL(U.Email, OU.Email) as 'Email',
|
||||||
|
U.TwoFactorProviders,
|
||||||
|
U.UsesKeyConnector,
|
||||||
|
OU.ResetPasswordKey,
|
||||||
|
CC.CollectionId,
|
||||||
|
C.Name AS CollectionName,
|
||||||
|
G.Id AS GroupId,
|
||||||
|
G.Name AS GroupName,
|
||||||
|
CG.ReadOnly,
|
||||||
|
CG.HidePasswords,
|
||||||
|
CG.Manage,
|
||||||
|
Cipher.Id AS CipherId
|
||||||
|
FROM dbo.OrganizationUser OU
|
||||||
|
LEFT JOIN dbo.[User] U ON U.Id = OU.UserId
|
||||||
|
INNER JOIN dbo.Organization O ON O.Id = OU.OrganizationId
|
||||||
|
AND O.Id = @OrganizationId
|
||||||
|
AND O.Enabled = 1
|
||||||
|
INNER JOIN dbo.GroupUser GU ON GU.OrganizationUserId = OU.Id
|
||||||
|
INNER JOIN dbo.[Group] G ON G.Id = GU.GroupId
|
||||||
|
INNER JOIN dbo.CollectionGroup CG ON CG.GroupId = G.Id
|
||||||
|
INNER JOIN dbo.Collection C ON C.Id = CG.CollectionId AND C.OrganizationId = @OrganizationId
|
||||||
|
INNER JOIN dbo.CollectionCipher CC ON CC.CollectionId = C.Id
|
||||||
|
INNER JOIN dbo.Cipher Cipher ON Cipher.Id = CC.CipherId and Cipher.OrganizationId = @OrganizationId
|
||||||
|
WHERE OU.Status IN (0,1,2) -- Invited, Accepted and Confirmed Users
|
||||||
|
AND Cipher.DeletedDate IS NULL
|
||||||
|
UNION ALL
|
||||||
|
-- Users without collection access (invited users)
|
||||||
|
-- typically invited users who have not yet accepted the invitation
|
||||||
|
-- and not yet assigned to any collection
|
||||||
|
SELECT
|
||||||
|
OU.Id AS UserGuid,
|
||||||
|
U.Name AS UserName,
|
||||||
|
ISNULL(U.Email, OU.Email) as 'Email',
|
||||||
|
U.TwoFactorProviders,
|
||||||
|
U.UsesKeyConnector,
|
||||||
|
OU.ResetPasswordKey,
|
||||||
|
null as CollectionId,
|
||||||
|
null AS CollectionName,
|
||||||
|
NULL AS GroupId,
|
||||||
|
NULL AS GroupName,
|
||||||
|
null as [ReadOnly],
|
||||||
|
null as HidePasswords,
|
||||||
|
null as Manage,
|
||||||
|
null AS CipherId
|
||||||
|
FROM dbo.OrganizationUser OU
|
||||||
|
LEFT JOIN dbo.[User] U ON U.Id = OU.UserId
|
||||||
|
INNER JOIN dbo.Organization O ON O.Id = OU.OrganizationId AND O.Id = @OrganizationId AND O.Enabled = 1
|
||||||
|
WHERE OU.Status IN (0,1,2) -- Invited, Accepted and Confirmed Users
|
||||||
|
AND OU.Id not in (
|
||||||
|
select OU1.Id from dbo.OrganizationUser OU1
|
||||||
|
inner join dbo.CollectionUser CU1 on CU1.OrganizationUserId = OU1.Id
|
||||||
|
WHERE OU1.OrganizationId = @organizationId
|
||||||
|
)
|
||||||
|
|
||||||
|
GO
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,50 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Bit.MySqlMigrations.Migrations;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class _20250609_00_AddMemberAccessReportStoreProceduresql : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "OrganizationMemberBaseDetails",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
UserGuid = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
|
||||||
|
UserName = table.Column<string>(type: "longtext", nullable: true)
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||||
|
Email = table.Column<string>(type: "longtext", nullable: true)
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||||
|
TwoFactorProviders = table.Column<string>(type: "longtext", nullable: true)
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||||
|
UsesKeyConnector = table.Column<bool>(type: "tinyint(1)", nullable: false),
|
||||||
|
ResetPasswordKey = table.Column<string>(type: "longtext", nullable: true)
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||||
|
CollectionId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
|
||||||
|
GroupId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
|
||||||
|
GroupName = table.Column<string>(type: "longtext", nullable: true)
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||||
|
CollectionName = table.Column<string>(type: "longtext", nullable: true)
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||||
|
ReadOnly = table.Column<bool>(type: "tinyint(1)", nullable: true),
|
||||||
|
HidePasswords = table.Column<bool>(type: "tinyint(1)", nullable: true),
|
||||||
|
Manage = table.Column<bool>(type: "tinyint(1)", nullable: true),
|
||||||
|
CipherId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci")
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
})
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "OrganizationMemberBaseDetails");
|
||||||
|
}
|
||||||
|
}
|
@ -22,6 +22,53 @@ namespace Bit.MySqlMigrations.Migrations
|
|||||||
|
|
||||||
MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder);
|
MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Bit.Core.Dirt.Reports.Models.Data.OrganizationMemberBaseDetail", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("CipherId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<Guid?>("CollectionId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<string>("CollectionName")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<Guid?>("GroupId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<string>("GroupName")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<bool?>("HidePasswords")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<bool?>("Manage")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<bool?>("ReadOnly")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<string>("ResetPasswordKey")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("TwoFactorProviders")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<Guid?>("UserGuid")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<bool>("UsesKeyConnector")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.ToTable("OrganizationMemberBaseDetails");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", b =>
|
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
@ -920,6 +967,34 @@ namespace Bit.MySqlMigrations.Migrations
|
|||||||
b.ToTable("ProviderPlan", (string)null);
|
b.ToTable("ProviderPlan", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.PasswordHealthReportApplication", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreationDate")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<Guid>("OrganizationId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("RevisionDate")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<string>("Uri")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Id")
|
||||||
|
.HasAnnotation("SqlServer:Clustered", true);
|
||||||
|
|
||||||
|
b.HasIndex("OrganizationId")
|
||||||
|
.HasAnnotation("SqlServer:Clustered", false);
|
||||||
|
|
||||||
|
b.ToTable("PasswordHealthReportApplication", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cache", b =>
|
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cache", b =>
|
||||||
{
|
{
|
||||||
b.Property<string>("Id")
|
b.Property<string>("Id")
|
||||||
@ -2023,34 +2098,6 @@ namespace Bit.MySqlMigrations.Migrations
|
|||||||
b.ToTable("ServiceAccount", (string)null);
|
b.ToTable("ServiceAccount", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Tools.Models.PasswordHealthReportApplication", b =>
|
|
||||||
{
|
|
||||||
b.Property<Guid>("Id")
|
|
||||||
.HasColumnType("char(36)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreationDate")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<Guid>("OrganizationId")
|
|
||||||
.HasColumnType("char(36)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("RevisionDate")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<string>("Uri")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("Id")
|
|
||||||
.HasAnnotation("SqlServer:Clustered", true);
|
|
||||||
|
|
||||||
b.HasIndex("OrganizationId")
|
|
||||||
.HasAnnotation("SqlServer:Clustered", false);
|
|
||||||
|
|
||||||
b.ToTable("PasswordHealthReportApplication", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b =>
|
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
@ -2532,6 +2579,17 @@ namespace Bit.MySqlMigrations.Migrations
|
|||||||
b.Navigation("Provider");
|
b.Navigation("Provider");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.PasswordHealthReportApplication", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("OrganizationId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Organization");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b =>
|
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization")
|
b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization")
|
||||||
@ -2825,17 +2883,6 @@ namespace Bit.MySqlMigrations.Migrations
|
|||||||
b.Navigation("Organization");
|
b.Navigation("Organization");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Tools.Models.PasswordHealthReportApplication", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization")
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("OrganizationId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Organization");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b =>
|
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization")
|
b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization")
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,43 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Bit.PostgresMigrations.Migrations;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class _20250609_00_AddMemberAccessReportStoreProceduresql : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "OrganizationMemberBaseDetails",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
UserGuid = table.Column<Guid>(type: "uuid", nullable: true),
|
||||||
|
UserName = table.Column<string>(type: "text", nullable: true),
|
||||||
|
Email = table.Column<string>(type: "text", nullable: true),
|
||||||
|
TwoFactorProviders = table.Column<string>(type: "text", nullable: true),
|
||||||
|
UsesKeyConnector = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
ResetPasswordKey = table.Column<string>(type: "text", nullable: true),
|
||||||
|
CollectionId = table.Column<Guid>(type: "uuid", nullable: true),
|
||||||
|
GroupId = table.Column<Guid>(type: "uuid", nullable: true),
|
||||||
|
GroupName = table.Column<string>(type: "text", nullable: true),
|
||||||
|
CollectionName = table.Column<string>(type: "text", nullable: true),
|
||||||
|
ReadOnly = table.Column<bool>(type: "boolean", nullable: true),
|
||||||
|
HidePasswords = table.Column<bool>(type: "boolean", nullable: true),
|
||||||
|
Manage = table.Column<bool>(type: "boolean", nullable: true),
|
||||||
|
CipherId = table.Column<Guid>(type: "uuid", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "OrganizationMemberBaseDetails");
|
||||||
|
}
|
||||||
|
}
|
@ -23,6 +23,53 @@ namespace Bit.PostgresMigrations.Migrations
|
|||||||
|
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Bit.Core.Dirt.Reports.Models.Data.OrganizationMemberBaseDetail", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("CipherId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<Guid?>("CollectionId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("CollectionName")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<Guid?>("GroupId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("GroupName")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<bool?>("HidePasswords")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<bool?>("Manage")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<bool?>("ReadOnly")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("ResetPasswordKey")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("TwoFactorProviders")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<Guid?>("UserGuid")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<bool>("UsesKeyConnector")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.ToTable("OrganizationMemberBaseDetails");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", b =>
|
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
@ -925,6 +972,34 @@ namespace Bit.PostgresMigrations.Migrations
|
|||||||
b.ToTable("ProviderPlan", (string)null);
|
b.ToTable("ProviderPlan", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.PasswordHealthReportApplication", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreationDate")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<Guid>("OrganizationId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<DateTime>("RevisionDate")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("Uri")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Id")
|
||||||
|
.HasAnnotation("SqlServer:Clustered", true);
|
||||||
|
|
||||||
|
b.HasIndex("OrganizationId")
|
||||||
|
.HasAnnotation("SqlServer:Clustered", false);
|
||||||
|
|
||||||
|
b.ToTable("PasswordHealthReportApplication", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cache", b =>
|
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cache", b =>
|
||||||
{
|
{
|
||||||
b.Property<string>("Id")
|
b.Property<string>("Id")
|
||||||
@ -2029,34 +2104,6 @@ namespace Bit.PostgresMigrations.Migrations
|
|||||||
b.ToTable("ServiceAccount", (string)null);
|
b.ToTable("ServiceAccount", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Tools.Models.PasswordHealthReportApplication", b =>
|
|
||||||
{
|
|
||||||
b.Property<Guid>("Id")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreationDate")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<Guid>("OrganizationId")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<DateTime>("RevisionDate")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Uri")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("Id")
|
|
||||||
.HasAnnotation("SqlServer:Clustered", true);
|
|
||||||
|
|
||||||
b.HasIndex("OrganizationId")
|
|
||||||
.HasAnnotation("SqlServer:Clustered", false);
|
|
||||||
|
|
||||||
b.ToTable("PasswordHealthReportApplication", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b =>
|
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
@ -2538,6 +2585,17 @@ namespace Bit.PostgresMigrations.Migrations
|
|||||||
b.Navigation("Provider");
|
b.Navigation("Provider");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.PasswordHealthReportApplication", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("OrganizationId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Organization");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b =>
|
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization")
|
b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization")
|
||||||
@ -2831,17 +2889,6 @@ namespace Bit.PostgresMigrations.Migrations
|
|||||||
b.Navigation("Organization");
|
b.Navigation("Organization");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Tools.Models.PasswordHealthReportApplication", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization")
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("OrganizationId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Organization");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b =>
|
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization")
|
b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization")
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,43 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Bit.SqliteMigrations.Migrations;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class _20250609_00_AddMemberAccessReportStoreProceduresql : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "OrganizationMemberBaseDetails",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
UserGuid = table.Column<Guid>(type: "TEXT", nullable: true),
|
||||||
|
UserName = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
Email = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
TwoFactorProviders = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
UsesKeyConnector = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||||
|
ResetPasswordKey = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
CollectionId = table.Column<Guid>(type: "TEXT", nullable: true),
|
||||||
|
GroupId = table.Column<Guid>(type: "TEXT", nullable: true),
|
||||||
|
GroupName = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
CollectionName = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
ReadOnly = table.Column<bool>(type: "INTEGER", nullable: true),
|
||||||
|
HidePasswords = table.Column<bool>(type: "INTEGER", nullable: true),
|
||||||
|
Manage = table.Column<bool>(type: "INTEGER", nullable: true),
|
||||||
|
CipherId = table.Column<Guid>(type: "TEXT", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "OrganizationMemberBaseDetails");
|
||||||
|
}
|
||||||
|
}
|
@ -17,6 +17,53 @@ namespace Bit.SqliteMigrations.Migrations
|
|||||||
#pragma warning disable 612, 618
|
#pragma warning disable 612, 618
|
||||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.8");
|
modelBuilder.HasAnnotation("ProductVersion", "8.0.8");
|
||||||
|
|
||||||
|
modelBuilder.Entity("Bit.Core.Dirt.Reports.Models.Data.OrganizationMemberBaseDetail", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("CipherId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid?>("CollectionId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("CollectionName")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid?>("GroupId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("GroupName")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool?>("HidePasswords")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool?>("Manage")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool?>("ReadOnly")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("ResetPasswordKey")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("TwoFactorProviders")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid?>("UserGuid")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("UsesKeyConnector")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.ToTable("OrganizationMemberBaseDetails");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", b =>
|
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
@ -909,6 +956,34 @@ namespace Bit.SqliteMigrations.Migrations
|
|||||||
b.ToTable("ProviderPlan", (string)null);
|
b.ToTable("ProviderPlan", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.PasswordHealthReportApplication", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreationDate")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("OrganizationId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("RevisionDate")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Uri")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Id")
|
||||||
|
.HasAnnotation("SqlServer:Clustered", true);
|
||||||
|
|
||||||
|
b.HasIndex("OrganizationId")
|
||||||
|
.HasAnnotation("SqlServer:Clustered", false);
|
||||||
|
|
||||||
|
b.ToTable("PasswordHealthReportApplication", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cache", b =>
|
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cache", b =>
|
||||||
{
|
{
|
||||||
b.Property<string>("Id")
|
b.Property<string>("Id")
|
||||||
@ -2012,34 +2087,6 @@ namespace Bit.SqliteMigrations.Migrations
|
|||||||
b.ToTable("ServiceAccount", (string)null);
|
b.ToTable("ServiceAccount", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Tools.Models.PasswordHealthReportApplication", b =>
|
|
||||||
{
|
|
||||||
b.Property<Guid>("Id")
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreationDate")
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.Property<Guid>("OrganizationId")
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.Property<DateTime>("RevisionDate")
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.Property<string>("Uri")
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("Id")
|
|
||||||
.HasAnnotation("SqlServer:Clustered", true);
|
|
||||||
|
|
||||||
b.HasIndex("OrganizationId")
|
|
||||||
.HasAnnotation("SqlServer:Clustered", false);
|
|
||||||
|
|
||||||
b.ToTable("PasswordHealthReportApplication", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b =>
|
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
@ -2521,6 +2568,17 @@ namespace Bit.SqliteMigrations.Migrations
|
|||||||
b.Navigation("Provider");
|
b.Navigation("Provider");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.PasswordHealthReportApplication", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("OrganizationId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Organization");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b =>
|
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization")
|
b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization")
|
||||||
@ -2814,17 +2872,6 @@ namespace Bit.SqliteMigrations.Migrations
|
|||||||
b.Navigation("Organization");
|
b.Navigation("Organization");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Tools.Models.PasswordHealthReportApplication", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization")
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("OrganizationId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Organization");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b =>
|
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization")
|
b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user