1
0
mirror of https://github.com/bitwarden/server.git synced 2025-01-21 21:41:21 +01:00

[Provider] Add support for events (#1447)

This commit is contained in:
Oscar Hinton 2021-07-15 16:37:27 +02:00 committed by GitHub
parent 8ac2dc50af
commit f6ebb20847
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
74 changed files with 4007 additions and 635 deletions

View File

@ -354,13 +354,19 @@ namespace Bit.CommCore.Services
public async Task AddOrganization(Guid providerId, Guid organizationId, Guid addingUserId, string key)
{
var po = await _providerOrganizationRepository.GetByOrganizationId(organizationId);
if (po != null)
{
throw new BadRequestException("Organization already belongs to a provider.");
}
var providerOrganization = new ProviderOrganization
{
ProviderId = providerId,
OrganizationId = organizationId,
Key = key,
};
await _providerOrganizationRepository.CreateAsync(providerOrganization);
}
@ -379,9 +385,23 @@ namespace Bit.CommCore.Services
return providerOrganization;
}
// TODO: Implement this
public Task RemoveOrganization(Guid providerOrganizationId, Guid removingUserId) => throw new NotImplementedException();
public async Task RemoveOrganization(Guid providerId, Guid providerOrganizationId, Guid removingUserId)
{
var providerOrganization = await _providerOrganizationRepository.GetByIdAsync(providerOrganizationId);
if (providerOrganization == null || providerOrganization.ProviderId != providerId)
{
throw new BadRequestException("Invalid organization");
}
if (!await _organizationService.HasConfirmedOwnersExceptAsync(providerOrganization.OrganizationId, new Guid[] {}))
{
throw new BadRequestException("Organization needs to have at least one confirmed owner");
}
await _providerOrganizationRepository.DeleteAsync(providerOrganization);
}
private async Task SendInviteAsync(ProviderUser providerUser, Provider provider)
{
var nowMillis = CoreHelpers.ToEpocMilliseconds(DateTime.UtcNow);

View File

@ -15,8 +15,8 @@ namespace Bit.Portal
{
private readonly IServiceProvider _serviceProvider;
public EnterprisePortalCurrentContext(IProviderOrganizationRepository providerOrganizationRepository,
IServiceProvider serviceProvider) : base(providerOrganizationRepository)
public EnterprisePortalCurrentContext(IProviderUserRepository providerUserRepository,
IServiceProvider serviceProvider) : base(providerUserRepository)
{
_serviceProvider = serviceProvider;
}

View File

@ -19,6 +19,7 @@ namespace Bit.Api.Controllers
private readonly IUserService _userService;
private readonly ICipherRepository _cipherRepository;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IProviderUserRepository _providerUserRepository;
private readonly IEventRepository _eventRepository;
private readonly ICurrentContext _currentContext;
@ -26,12 +27,14 @@ namespace Bit.Api.Controllers
IUserService userService,
ICipherRepository cipherRepository,
IOrganizationUserRepository organizationUserRepository,
IProviderUserRepository providerUserRepository,
IEventRepository eventRepository,
ICurrentContext currentContext)
{
_userService = userService;
_cipherRepository = cipherRepository;
_organizationUserRepository = organizationUserRepository;
_providerUserRepository = providerUserRepository;
_eventRepository = eventRepository;
_currentContext = currentContext;
}
@ -117,6 +120,41 @@ namespace Bit.Api.Controllers
return new ListResponseModel<EventResponseModel>(responses, result.ContinuationToken);
}
[HttpGet("~/providers/{providerId:guid}/events")]
public async Task<ListResponseModel<EventResponseModel>> GetProvider(Guid providerId,
[FromQuery]DateTime? start = null, [FromQuery]DateTime? end = null, [FromQuery]string continuationToken = null)
{
if (!_currentContext.ProviderAccessEventLogs(providerId))
{
throw new NotFoundException();
}
var dateRange = GetDateRange(start, end);
var result = await _eventRepository.GetManyByProviderAsync(providerId, dateRange.Item1, dateRange.Item2,
new PageOptions { ContinuationToken = continuationToken });
var responses = result.Data.Select(e => new EventResponseModel(e));
return new ListResponseModel<EventResponseModel>(responses, result.ContinuationToken);
}
[HttpGet("~/providers/{providerId:guid}/users/{id:guid}/events")]
public async Task<ListResponseModel<EventResponseModel>> GetProviderUser(Guid providerId, Guid id,
[FromQuery]DateTime? start = null, [FromQuery]DateTime? end = null, [FromQuery]string continuationToken = null)
{
var providerUser = await _providerUserRepository.GetByIdAsync(id);
if (providerUser == null || !providerUser.UserId.HasValue ||
!_currentContext.ProviderAccessEventLogs(providerUser.ProviderId))
{
throw new NotFoundException();
}
var dateRange = GetDateRange(start, end);
var result = await _eventRepository.GetManyByProviderActingUserAsync(providerUser.ProviderId,
providerUser.UserId.Value, dateRange.Item1, dateRange.Item2,
new PageOptions { ContinuationToken = continuationToken });
var responses = result.Data.Select(e => new EventResponseModel(e));
return new ListResponseModel<EventResponseModel>(responses, result.ContinuationToken);
}
private Tuple<DateTime, DateTime> GetDateRange(DateTime? start, DateTime? end)
{
if (!end.HasValue || !start.HasValue)

View File

@ -79,5 +79,18 @@ namespace Bit.Api.Controllers
var result = await _providerService.CreateOrganizationAsync(providerId, organizationSignup, user);
return new ProviderOrganizationResponseModel(result);
}
[HttpDelete("{id:guid}")]
[HttpPost("{id:guid}/delete")]
public async Task Delete(Guid providerId, Guid id)
{
if (!_currentContext.ManageProviderOrganizations(providerId))
{
throw new NotFoundException();
}
var userId = _userService.GetProperUserId(User);
await _providerService.RemoveOrganization(providerId, id, userId.Value);
}
}
}

View File

@ -38,7 +38,7 @@ namespace Bit.Api.Controllers
public async Task<ProviderUserResponseModel> Get(Guid providerId, Guid id)
{
var providerUser = await _providerUserRepository.GetByIdAsync(id);
if (providerUser == null || !_currentContext.ManageProviderUsers(providerUser.ProviderId))
if (providerUser == null || !_currentContext.ProviderManageUsers(providerUser.ProviderId))
{
throw new NotFoundException();
}
@ -49,7 +49,7 @@ namespace Bit.Api.Controllers
[HttpGet("")]
public async Task<ListResponseModel<ProviderUserUserDetailsResponseModel>> Get(Guid providerId)
{
if (!_currentContext.ManageProviderUsers(providerId))
if (!_currentContext.ProviderManageUsers(providerId))
{
throw new NotFoundException();
}
@ -62,7 +62,7 @@ namespace Bit.Api.Controllers
[HttpPost("invite")]
public async Task Invite(Guid providerId, [FromBody]ProviderUserInviteRequestModel model)
{
if (!_currentContext.ManageProviderUsers(providerId))
if (!_currentContext.ProviderManageUsers(providerId))
{
throw new NotFoundException();
}
@ -74,7 +74,7 @@ namespace Bit.Api.Controllers
[HttpPost("reinvite")]
public async Task<ListResponseModel<ProviderUserBulkResponseModel>> BulkReinvite(Guid providerId, [FromBody]ProviderUserBulkRequestModel model)
{
if (!_currentContext.ManageProviderUsers(providerId))
if (!_currentContext.ProviderManageUsers(providerId))
{
throw new NotFoundException();
}
@ -88,7 +88,7 @@ namespace Bit.Api.Controllers
[HttpPost("{id:guid}/reinvite")]
public async Task Reinvite(Guid providerId, Guid id)
{
if (!_currentContext.ManageProviderUsers(providerId))
if (!_currentContext.ProviderManageUsers(providerId))
{
throw new NotFoundException();
}
@ -112,7 +112,7 @@ namespace Bit.Api.Controllers
[HttpPost("{id:guid}/confirm")]
public async Task Confirm(Guid providerId, Guid id, [FromBody]ProviderUserConfirmRequestModel model)
{
if (!_currentContext.ManageProviderUsers(providerId))
if (!_currentContext.ProviderManageUsers(providerId))
{
throw new NotFoundException();
}
@ -125,7 +125,7 @@ namespace Bit.Api.Controllers
public async Task<ListResponseModel<ProviderUserBulkResponseModel>> BulkConfirm(Guid providerId,
[FromBody]ProviderUserBulkConfirmRequestModel model)
{
if (!_currentContext.ManageProviderUsers(providerId))
if (!_currentContext.ProviderManageUsers(providerId))
{
throw new NotFoundException();
}
@ -140,13 +140,13 @@ namespace Bit.Api.Controllers
[HttpPost("public-keys")]
public async Task<ListResponseModel<ProviderUserPublicKeyResponseModel>> UserPublicKeys(Guid providerId, [FromBody]ProviderUserBulkRequestModel model)
{
if (!_currentContext.ManageProviderUsers(providerId))
if (!_currentContext.ProviderManageUsers(providerId))
{
throw new NotFoundException();
}
var result = await _providerUserRepository.GetManyPublicKeysByProviderUserAsync(providerId, model.Ids);
var responses = result.Select(r => new ProviderUserPublicKeyResponseModel(r.Id, r.PublicKey)).ToList();
var responses = result.Select(r => new ProviderUserPublicKeyResponseModel(r.Id, r.UserId, r.PublicKey)).ToList();
return new ListResponseModel<ProviderUserPublicKeyResponseModel>(responses);
}
@ -154,7 +154,7 @@ namespace Bit.Api.Controllers
[HttpPost("{id:guid}")]
public async Task Put(Guid providerId, Guid id, [FromBody]ProviderUserUpdateRequestModel model)
{
if (!_currentContext.ManageProviderUsers(providerId))
if (!_currentContext.ProviderManageUsers(providerId))
{
throw new NotFoundException();
}
@ -173,7 +173,7 @@ namespace Bit.Api.Controllers
[HttpPost("{id:guid}/delete")]
public async Task Delete(Guid providerId, Guid id)
{
if (!_currentContext.ManageProviderUsers(providerId))
if (!_currentContext.ProviderManageUsers(providerId))
{
throw new NotFoundException();
}
@ -186,7 +186,7 @@ namespace Bit.Api.Controllers
[HttpPost("delete")]
public async Task<ListResponseModel<ProviderUserBulkResponseModel>> BulkDelete(Guid providerId, [FromBody]ProviderUserBulkRequestModel model)
{
if (!_currentContext.ManageProviderUsers(providerId))
if (!_currentContext.ProviderManageUsers(providerId))
{
throw new NotFoundException();
}

View File

@ -5,6 +5,7 @@ using Bit.Core.Exceptions;
using Bit.Core.Models.Api;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Settings;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
@ -18,14 +19,16 @@ namespace Bit.Api.Controllers
private readonly IProviderRepository _providerRepository;
private readonly IProviderService _providerService;
private readonly ICurrentContext _currentContext;
private readonly GlobalSettings _globalSettings;
public ProvidersController(IUserService userService, IProviderRepository providerRepository,
IProviderService providerService, ICurrentContext currentContext)
IProviderService providerService, ICurrentContext currentContext, GlobalSettings globalSettings)
{
_userService = userService;
_providerRepository = providerRepository;
_providerService = providerService;
_currentContext = currentContext;
_globalSettings = globalSettings;
}
[HttpGet("{id:guid}")]
@ -45,6 +48,25 @@ namespace Bit.Api.Controllers
return new ProviderResponseModel(provider);
}
[HttpPut("{id:guid}")]
[HttpPost("{id:guid}")]
public async Task<ProviderResponseModel> Put(Guid id, [FromBody]ProviderUpdateRequestModel model)
{
if (!_currentContext.ProviderProviderAdmin(id))
{
throw new NotFoundException();
}
var provider = await _providerRepository.GetByIdAsync(id);
if (provider == null)
{
throw new NotFoundException();
}
await _providerService.UpdateAsync(model.ToProvider(provider, _globalSettings));
return new ProviderResponseModel(provider);
}
[HttpPost("{id:guid}/setup")]
public async Task<ProviderResponseModel> Setup(Guid id, [FromBody]ProviderSetupRequestModel model)
{

View File

@ -12,15 +12,16 @@ using Bit.Core.Utilities;
using Bit.Core.Models.Data;
using Bit.Core.Models.Table.Provider;
using Bit.Core.Settings;
using Microsoft.EntityFrameworkCore.Internal;
namespace Bit.Core.Context
{
public class CurrentContext : ICurrentContext
{
private readonly IProviderOrganizationRepository _providerOrganizationRepository;
private readonly IProviderUserRepository _providerUserRepository;
private bool _builtHttpContext;
private bool _builtClaimsPrincipal;
private ICollection<ProviderOrganization> _providerOrganizations;
private IEnumerable<ProviderUserOrganizationDetails> _providerUserOrganizations;
public virtual HttpContext HttpContext { get; set; }
public virtual Guid? UserId { get; set; }
@ -37,9 +38,9 @@ namespace Bit.Core.Context
public virtual bool MaybeBot { get; set; }
public virtual int? BotScore { get; set; }
public CurrentContext(IProviderOrganizationRepository providerOrganizationRepository)
public CurrentContext(IProviderUserRepository providerUserRepository)
{
_providerOrganizationRepository = providerOrganizationRepository;
_providerUserRepository = providerUserRepository;
}
public async virtual Task BuildAsync(HttpContext httpContext, GlobalSettings globalSettings)
@ -343,7 +344,12 @@ namespace Bit.Core.Context
return Providers?.Any(o => o.Id == providerId && o.Type == ProviderUserType.ProviderAdmin) ?? false;
}
public bool ManageProviderUsers(Guid providerId)
public bool ProviderManageUsers(Guid providerId)
{
return ProviderProviderAdmin(providerId);
}
public bool ProviderAccessEventLogs(Guid providerId)
{
return ProviderProviderAdmin(providerId);
}
@ -363,6 +369,19 @@ namespace Bit.Core.Context
return Providers?.Any(o => o.Id == providerId) ?? false;
}
public async Task<Guid?> ProviderIdForOrg(Guid orgId)
{
if (Organizations.Any(org => org.Id == orgId))
{
return null;
}
var po = (await GetProviderOrganizations())
.FirstOrDefault(po => po.OrganizationId == orgId);
return po?.ProviderId;
}
public async Task<ICollection<CurrentContentOrganization>> OrganizationMembershipAsync(
IOrganizationUserRepository organizationUserRepository, Guid userId)
{
@ -421,14 +440,14 @@ namespace Bit.Core.Context
};
}
private async Task<ICollection<ProviderOrganization>> GetProviderOrganizations()
private async Task<IEnumerable<ProviderUserOrganizationDetails>> GetProviderOrganizations()
{
if (_providerOrganizations == null)
if (_providerUserOrganizations == null)
{
_providerOrganizations = await _providerOrganizationRepository.GetManyByUserIdAsync(UserId.Value);
_providerUserOrganizations = await _providerUserRepository.GetManyOrganizationDetailsByUserAsync(UserId.Value, ProviderUserStatusType.Confirmed);
}
return _providerOrganizations;
return _providerUserOrganizations;
}
}
}

View File

@ -49,7 +49,8 @@ namespace Bit.Core.Context
Task<bool> ManageResetPassword(Guid orgId);
bool ProviderProviderAdmin(Guid providerId);
bool ProviderUser(Guid providerId);
bool ManageProviderUsers(Guid providerId);
bool ProviderManageUsers(Guid providerId);
bool ProviderAccessEventLogs(Guid providerId);
bool AccessProviderOrganizations(Guid providerId);
bool ManageProviderOrganizations(Guid providerId);
@ -58,5 +59,7 @@ namespace Bit.Core.Context
Task<ICollection<CurrentContentProvider>> ProviderMembershipAsync(
IProviderUserRepository providerUserRepository, Guid userId);
Task<Guid?> ProviderIdForOrg(Guid orgId);
}
}

View File

@ -1,8 +0,0 @@
namespace Bit.Core.Enums.Provider
{
public enum ProviderOrganizationProviderUserType : byte
{
Administrator = 0,
ServiceAdmin = 1,
}
}

View File

@ -0,0 +1,31 @@
using Bit.Core.Settings;
using System.ComponentModel.DataAnnotations;
using Bit.Core.Models.Table.Provider;
namespace Bit.Core.Models.Api
{
public class ProviderUpdateRequestModel
{
[Required]
[StringLength(50)]
public string Name { get; set; }
[StringLength(50)]
public string BusinessName { get; set; }
[EmailAddress]
[Required]
[StringLength(256)]
public string BillingEmail { get; set; }
public virtual Provider ToProvider(Provider existingProvider, GlobalSettings globalSettings)
{
if (!globalSettings.SelfHosted)
{
// These items come from the license file
existingProvider.Name = Name;
existingProvider.BusinessName = BusinessName;
existingProvider.BillingEmail = BillingEmail?.ToLowerInvariant()?.Trim();
}
return existingProvider;
}
}
}

View File

@ -17,11 +17,13 @@ namespace Bit.Core.Models.Api
Type = ev.Type;
UserId = ev.UserId;
OrganizationId = ev.OrganizationId;
ProviderId = ev.ProviderId;
CipherId = ev.CipherId;
CollectionId = ev.CollectionId;
GroupId = ev.GroupId;
PolicyId = ev.PolicyId;
OrganizationUserId = ev.OrganizationUserId;
ProviderUserId = ev.ProviderUserId;
ActingUserId = ev.ActingUserId;
Date = ev.Date;
DeviceType = ev.DeviceType;
@ -31,11 +33,13 @@ namespace Bit.Core.Models.Api
public EventType Type { get; set; }
public Guid? UserId { get; set; }
public Guid? OrganizationId { get; set; }
public Guid? ProviderId { get; set; }
public Guid? CipherId { get; set; }
public Guid? CollectionId { get; set; }
public Guid? GroupId { get; set; }
public Guid? PolicyId { get; set; }
public Guid? OrganizationUserId { get; set; }
public Guid? ProviderUserId { get; set; }
public Guid? ActingUserId { get; set; }
public DateTime Date { get; set; }
public DeviceType? DeviceType { get; set; }

View File

@ -1,12 +1,14 @@
using Bit.Core.Enums;
using Bit.Core.Models.Data;
using Bit.Core.Utilities;
namespace Bit.Core.Models.Api
{
public class ProfileOrganizationResponseModel : ResponseModel
{
public ProfileOrganizationResponseModel(OrganizationUserOrganizationDetails organization)
: base("profileOrganization")
public ProfileOrganizationResponseModel(string str) : base(str) {}
public ProfileOrganizationResponseModel(OrganizationUserOrganizationDetails organization) : this("profileOrganization")
{
Id = organization.OrganizationId.ToString();
Name = organization.Name;

View File

@ -1,10 +1,9 @@
using Bit.Core.Enums;
using Bit.Core.Models.Data;
using Bit.Core.Utilities;
namespace Bit.Core.Models.Api
{
public class ProfileProviderOrganizationResponseModel : ResponseModel
public class ProfileProviderOrganizationResponseModel : ProfileOrganizationResponseModel
{
public ProfileProviderOrganizationResponseModel(ProviderUserOrganizationDetails organization)
: base("profileProviderOrganization")
@ -27,46 +26,16 @@ namespace Bit.Core.Models.Api
MaxStorageGb = organization.MaxStorageGb;
Key = organization.Key;
HasPublicAndPrivateKeys = organization.PublicKey != null && organization.PrivateKey != null;
Status = organization.Status;
Type = organization.Type;
Status = OrganizationUserStatusType.Confirmed; // Provider users are always confirmed
Type = OrganizationUserType.Owner; // Provider users behave like Owners
Enabled = organization.Enabled;
SsoBound = !string.IsNullOrWhiteSpace(organization.SsoExternalId);
SsoBound = false;
Identifier = organization.Identifier;
Permissions = CoreHelpers.LoadClassFromJsonData<Permissions>(organization.Permissions);
ResetPasswordEnrolled = organization.ResetPasswordKey != null;
Permissions = new Permissions();
ResetPasswordEnrolled = false;
UserId = organization.UserId?.ToString();
ProviderId = organization.ProviderId?.ToString();
ProviderName = organization.ProviderName;
}
public string Id { get; set; }
public string Name { get; set; }
public bool UsePolicies { get; set; }
public bool UseSso { get; set; }
public bool UseGroups { get; set; }
public bool UseDirectory { get; set; }
public bool UseEvents { get; set; }
public bool UseTotp { get; set; }
public bool Use2fa { get; set; }
public bool UseApi { get; set; }
public bool UseResetPassword { get; set; }
public bool UseBusinessPortal => UsePolicies || UseSso; // TODO add events if needed
public bool UsersGetPremium { get; set; }
public bool SelfHost { get; set; }
public int Seats { get; set; }
public int MaxCollections { get; set; }
public short? MaxStorageGb { get; set; }
public string Key { get; set; }
public OrganizationUserStatusType Status { get; set; }
public OrganizationUserType Type { get; set; }
public bool Enabled { get; set; }
public bool SsoBound { get; set; }
public string Identifier { get; set; }
public Permissions Permissions { get; set; }
public bool ResetPasswordEnrolled { get; set; }
public string UserId { get; set; }
public bool HasPublicAndPrivateKeys { get; set; }
public string ProviderId { get; set; }
public string ProviderName { get; set; }
}
}

View File

@ -65,14 +65,16 @@ namespace Bit.Core.Models.Api
public class ProviderUserPublicKeyResponseModel : ResponseModel
{
public ProviderUserPublicKeyResponseModel(Guid id, string key,
public ProviderUserPublicKeyResponseModel(Guid id, Guid userId, string key,
string obj = "providerUserPublicKeyResponseModel") : base(obj)
{
Id = id;
UserId = userId;
Key = key;
}
public Guid Id { get; set; }
public Guid UserId { get; set; }
public string Key { get; set; }
}

View File

@ -16,11 +16,13 @@ namespace Bit.Core.Models.Data
Type = e.Type;
UserId = e.UserId;
OrganizationId = e.OrganizationId;
ProviderId = e.ProviderId;
CipherId = e.CipherId;
CollectionId = e.CollectionId;
PolicyId = e.PolicyId;
GroupId = e.GroupId;
OrganizationUserId = e.OrganizationUserId;
ProviderUserId = e.ProviderUserId;
DeviceType = e.DeviceType;
IpAddress = e.IpAddress;
ActingUserId = e.ActingUserId;
@ -30,11 +32,13 @@ namespace Bit.Core.Models.Data
public EventType Type { get; set; }
public Guid? UserId { get; set; }
public Guid? OrganizationId { get; set; }
public Guid? ProviderId { get; set; }
public Guid? CipherId { get; set; }
public Guid? CollectionId { get; set; }
public Guid? PolicyId { get; set; }
public Guid? GroupId { get; set; }
public Guid? OrganizationUserId { get; set; }
public Guid? ProviderUserId { get; set; }
public DeviceType? DeviceType { get; set; }
public string IpAddress { get; set; }
public Guid? ActingUserId { get; set; }
@ -87,7 +91,9 @@ namespace Bit.Core.Models.Data
public static List<EventTableEntity> IndexEvent(EventMessage e)
{
var uniquifier = e.IdempotencyId.GetValueOrDefault(Guid.NewGuid());
var pKey = e.OrganizationId.HasValue ? $"OrganizationId={e.OrganizationId}" : $"UserId={e.UserId}";
var pKey = GetPartitionKey(e);
var dateKey = CoreHelpers.DateTimeToTableStorageKey(e.Date);
var entities = new List<EventTableEntity>
@ -95,7 +101,7 @@ namespace Bit.Core.Models.Data
new EventTableEntity(e)
{
PartitionKey = pKey,
RowKey = string.Format("Date={0}__Uniquifier={1}", dateKey, uniquifier)
RowKey = $"Date={dateKey}__Uniquifier={uniquifier}"
}
};
@ -104,8 +110,16 @@ namespace Bit.Core.Models.Data
entities.Add(new EventTableEntity(e)
{
PartitionKey = pKey,
RowKey = string.Format("ActingUserId={0}__Date={1}__Uniquifier={2}",
e.ActingUserId, dateKey, uniquifier)
RowKey = $"ActingUserId={e.ActingUserId}__Date={dateKey}__Uniquifier={uniquifier}"
});
}
if (!e.OrganizationId.HasValue && e.ProviderId.HasValue && e.ActingUserId.HasValue)
{
entities.Add(new EventTableEntity(e)
{
PartitionKey = pKey,
RowKey = $"ActingUserId={e.ActingUserId}__Date={dateKey}__Uniquifier={uniquifier}"
});
}
@ -114,12 +128,26 @@ namespace Bit.Core.Models.Data
entities.Add(new EventTableEntity(e)
{
PartitionKey = pKey,
RowKey = string.Format("CipherId={0}__Date={1}__Uniquifier={2}",
e.CipherId, dateKey, uniquifier)
RowKey = $"CipherId={e.CipherId}__Date={dateKey}__Uniquifier={uniquifier}"
});
}
return entities;
}
private static string GetPartitionKey(EventMessage e)
{
if (e.OrganizationId.HasValue)
{
return $"OrganizationId={e.OrganizationId}";
}
if (e.ProviderId.HasValue)
{
return $"ProviderId={e.ProviderId}";
}
return $"UserId={e.UserId}";
}
}
}

View File

@ -8,11 +8,13 @@ namespace Bit.Core.Models.Data
EventType Type { get; set; }
Guid? UserId { get; set; }
Guid? OrganizationId { get; set; }
Guid? ProviderId { get; set; }
Guid? CipherId { get; set; }
Guid? CollectionId { get; set; }
Guid? GroupId { get; set; }
Guid? PolicyId { get; set; }
Guid? OrganizationUserId { get; set; }
Guid? ProviderUserId { get; set; }
Guid? ActingUserId { get; set; }
DeviceType? DeviceType { get; set; }
string IpAddress { get; set; }

View File

@ -1,4 +1,5 @@
using System;
using Bit.Core.Enums.Provider;
namespace Bit.Core.Models.Data
{
@ -19,20 +20,18 @@ namespace Bit.Core.Models.Data
public bool UseBusinessPortal => UsePolicies || UseSso;
public bool SelfHost { get; set; }
public bool UsersGetPremium { get; set; }
public int Seats { get; set; }
public int MaxCollections { get; set; }
public int? Seats { get; set; }
public short? MaxCollections { get; set; }
public short? MaxStorageGb { get; set; }
public string Key { get; set; }
public Enums.OrganizationUserStatusType Status { get; set; }
public Enums.OrganizationUserType Type { get; set; }
public ProviderUserStatusType Status { get; set; }
public ProviderUserType Type { get; set; }
public bool Enabled { get; set; }
public string SsoExternalId { get; set; }
public string Identifier { get; set; }
public string Permissions { get; set; }
public string ResetPasswordKey { get; set; }
public string PublicKey { get; set; }
public string PrivateKey { get; set; }
public Guid? ProviderId { get; set; }
public Guid? ProviderUserId { get; set; }
public string ProviderName { get; set; }
}
}

View File

@ -5,6 +5,7 @@ namespace Bit.Core.Models.Data
public class ProviderUserPublicKey
{
public Guid Id { get; set; }
public Guid UserId { get; set; }
public string PublicKey { get; set; }
}
}

View File

@ -1,5 +1,3 @@
using System.Collections.Generic;
using System.Text.Json;
using AutoMapper;
namespace Bit.Core.Models.EntityFramework

View File

@ -1,18 +0,0 @@
using AutoMapper;
namespace Bit.Core.Models.EntityFramework.Provider
{
public class ProviderOrganizationProviderUser : Table.Provider.ProviderOrganizationProviderUser
{
public virtual ProviderOrganization ProviderOrganization { get; set; }
public virtual ProviderUser ProviderUser { get; set; }
}
public class ProviderOrganizationProviderUserMapperProfile : Profile
{
public ProviderOrganizationProviderUserMapperProfile()
{
CreateMap<Table.Provider.ProviderOrganizationProviderUser, ProviderOrganizationProviderUser>().ReverseMap();
}
}
}

View File

@ -16,11 +16,13 @@ namespace Bit.Core.Models.Table
Type = e.Type;
UserId = e.UserId;
OrganizationId = e.OrganizationId;
ProviderId = e.ProviderId;
CipherId = e.CipherId;
CollectionId = e.CollectionId;
PolicyId = e.PolicyId;
GroupId = e.GroupId;
OrganizationUserId = e.OrganizationUserId;
ProviderUserId = e.ProviderUserId;
DeviceType = e.DeviceType;
IpAddress = e.IpAddress;
ActingUserId = e.ActingUserId;
@ -31,11 +33,13 @@ namespace Bit.Core.Models.Table
public EventType Type { get; set; }
public Guid? UserId { get; set; }
public Guid? OrganizationId { get; set; }
public Guid? ProviderId { get; set; }
public Guid? CipherId { get; set; }
public Guid? CollectionId { get; set; }
public Guid? PolicyId { get; set; }
public Guid? GroupId { get; set; }
public Guid? OrganizationUserId { get; set; }
public Guid? ProviderUserId { get; set; }
public DeviceType? DeviceType { get; set; }
[MaxLength(50)]
public string IpAddress { get; set; }

View File

@ -1,25 +0,0 @@
using System;
using Bit.Core.Enums.Provider;
using Bit.Core.Utilities;
namespace Bit.Core.Models.Table.Provider
{
public class ProviderOrganizationProviderUser : ITableObject<Guid>
{
public Guid Id { get; set; }
public Guid ProviderOrganizationId { get; set; }
public Guid ProviderUserId { get; set; }
public ProviderOrganizationProviderUserType Type { get; set; }
public string Permissions { get; set; }
public DateTime CreationDate { get; internal set; } = DateTime.UtcNow;
public DateTime RevisionDate { get; internal set; } = DateTime.UtcNow;
public void SetNewId()
{
if (Id == default)
{
Id = CoreHelpers.GenerateComb();
}
}
}
}

View File

@ -31,7 +31,6 @@ namespace Bit.Core.Repositories.EntityFramework
public DbSet<Provider> Providers { get; set; }
public DbSet<ProviderUser> ProviderUsers { get; set; }
public DbSet<ProviderOrganization> ProviderOrganizations { get; set; }
public DbSet<ProviderOrganizationProviderUser> ProviderOrganizationProviderUsers { get; set; }
public DbSet<Send> Sends { get; set; }
public DbSet<SsoConfig> SsoConfigs { get; set; }
public DbSet<SsoUser> SsoUsers { get; set; }
@ -61,7 +60,6 @@ namespace Bit.Core.Repositories.EntityFramework
var eProvider = builder.Entity<Provider>();
var eProviderUser = builder.Entity<ProviderUser>();
var eProviderOrganization = builder.Entity<ProviderOrganization>();
var eProviderOrganizationProviderUser = builder.Entity<ProviderOrganizationProviderUser>();
var eSend = builder.Entity<Send>();
var eSsoConfig = builder.Entity<SsoConfig>();
var eSsoUser = builder.Entity<SsoUser>();
@ -83,7 +81,6 @@ namespace Bit.Core.Repositories.EntityFramework
eProvider.Property(c => c.Id).ValueGeneratedNever();
eProviderUser.Property(c => c.Id).ValueGeneratedNever();
eProviderOrganization.Property(c => c.Id).ValueGeneratedNever();
eProviderOrganizationProviderUser.Property(c => c.Id).ValueGeneratedNever();
eSend.Property(c => c.Id).ValueGeneratedNever();
eTransaction.Property(c => c.Id).ValueGeneratedNever();
eUser.Property(c => c.Id).ValueGeneratedNever();
@ -123,7 +120,6 @@ namespace Bit.Core.Repositories.EntityFramework
eProvider.ToTable(nameof(Provider));
eProviderUser.ToTable(nameof(ProviderUser));
eProviderOrganization.ToTable(nameof(ProviderOrganization));
eProviderOrganizationProviderUser.ToTable(nameof(ProviderOrganizationProviderUser));
eSend.ToTable(nameof(Send));
eSsoConfig.ToTable(nameof(SsoConfig));
eSsoUser.ToTable(nameof(SsoUser));

View File

@ -49,6 +49,7 @@ namespace Bit.Core.Repositories.EntityFramework
var dbContext = GetDatabaseContext(scope);
var tableEvents = entities.Select(e => e as Event ?? new Event(e));
var entityEvents = Mapper.Map<List<EfModel.Event>>(tableEvents);
entityEvents.ForEach(e => e.SetNewId());
await dbContext.BulkCopyAsync(entityEvents);
}
}
@ -103,6 +104,57 @@ namespace Bit.Core.Repositories.EntityFramework
}
}
public async Task<PagedResult<IEvent>> GetManyByProviderAsync(Guid providerId, DateTime startDate, DateTime endDate, PageOptions pageOptions)
{
DateTime? beforeDate = null;
if (!string.IsNullOrWhiteSpace(pageOptions.ContinuationToken) &&
long.TryParse(pageOptions.ContinuationToken, out var binaryDate))
{
beforeDate = DateTime.SpecifyKind(DateTime.FromBinary(binaryDate), DateTimeKind.Utc);
}
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = new EventReadPageByProviderIdQuery(providerId, startDate,
endDate, beforeDate, pageOptions);
var events = await query.Run(dbContext).ToListAsync();
var result = new PagedResult<IEvent>();
if (events.Any() && events.Count >= pageOptions.PageSize)
{
result.ContinuationToken = events.Last().Date.ToBinary().ToString();
}
result.Data.AddRange(events);
return result;
}
}
public async Task<PagedResult<IEvent>> GetManyByProviderActingUserAsync(Guid providerId, Guid actingUserId,
DateTime startDate, DateTime endDate, PageOptions pageOptions)
{
DateTime? beforeDate = null;
if (!string.IsNullOrWhiteSpace(pageOptions.ContinuationToken) &&
long.TryParse(pageOptions.ContinuationToken, out var binaryDate))
{
beforeDate = DateTime.SpecifyKind(DateTime.FromBinary(binaryDate), DateTimeKind.Utc);
}
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = new EventReadPageByProviderIdActingUserIdQuery(providerId, actingUserId,
startDate, endDate, beforeDate, pageOptions);
var events = await query.Run(dbContext).ToListAsync();
var result = new PagedResult<IEvent>();
if (events.Any() && events.Count >= pageOptions.PageSize)
{
result.ContinuationToken = events.Last().Date.ToBinary().ToString();
}
result.Data.AddRange(events);
return result;
}
}
public async Task<PagedResult<IEvent>> GetManyByOrganizationAsync(Guid organizationId, DateTime startDate, DateTime endDate, PageOptions pageOptions)
{
DateTime? beforeDate = null;

View File

@ -1,20 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Bit.Core.Models.Table.Provider;
using Bit.Core.Repositories.EntityFramework;
using TableModel = Bit.Core.Models.Table;
using EfModel = Bit.Core.Models.EntityFramework;
using Microsoft.Extensions.DependencyInjection;
using AutoMapper;
namespace Bit.Core.Repositories.EntityFramework
{
public class ProviderOrganizationProviderUserRepository :
Repository<TableModel.Provider.ProviderOrganizationProviderUser, EfModel.Provider.ProviderOrganizationProviderUser, Guid>, IProviderOrganizationProviderUserRepository
{
public ProviderOrganizationProviderUserRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper)
: base(serviceScopeFactory, mapper, (DatabaseContext context) => context.ProviderOrganizationProviderUsers)
{ }
}
}

View File

@ -1,13 +1,13 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Bit.Core.Models.Table.Provider;
using Bit.Core.Repositories.EntityFramework;
using System.Linq;
using TableModel = Bit.Core.Models.Table;
using EfModel = Bit.Core.Models.EntityFramework;
using Microsoft.Extensions.DependencyInjection;
using AutoMapper;
using Bit.Core.Models.Data;
using Bit.Core.Models.Table.Provider;
using Bit.Core.Repositories.EntityFramework.Queries;
using Microsoft.EntityFrameworkCore;
@ -17,14 +17,9 @@ namespace Bit.Core.Repositories.EntityFramework
Repository<TableModel.Provider.ProviderOrganization, EfModel.Provider.ProviderOrganization, Guid>, IProviderOrganizationRepository
{
public ProviderOrganizationRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper)
: base(serviceScopeFactory, mapper, (DatabaseContext context) => context.ProviderOrganizations)
: base(serviceScopeFactory, mapper, context => context.ProviderOrganizations)
{ }
public Task<ICollection<ProviderOrganization>> GetManyByUserIdAsync(Guid userId)
{
throw new NotImplementedException();
}
public async Task<ICollection<ProviderOrganizationOrganizationDetails>> GetManyDetailsByProviderAsync(Guid providerId)
{
using (var scope = ServiceScopeFactory.CreateScope())
@ -35,5 +30,12 @@ namespace Bit.Core.Repositories.EntityFramework
return data;
}
}
public async Task<ProviderOrganization> GetByOrganizationId(Guid organizationId)
{
using var scope = ServiceScopeFactory.CreateScope();
var dbContext = GetDatabaseContext(scope);
return await GetDbSet(dbContext).Where(po => po.OrganizationId == organizationId).FirstOrDefaultAsync();
}
}
}

View File

@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Bit.Core.Models.Table.Provider;
using Bit.Core.Repositories.EntityFramework;
using TableModel = Bit.Core.Models.Table;
using EfModel = Bit.Core.Models.EntityFramework;
using Microsoft.Extensions.DependencyInjection;
@ -13,15 +12,13 @@ using Bit.Core.Models.Data;
namespace Bit.Core.Repositories.EntityFramework
{
public class ProviderRepository : Repository<TableModel.Provider.Provider, EfModel.Provider.Provider, Guid>, IProviderRepository
public class ProviderRepository : Repository<Provider, EfModel.Provider.Provider, Guid>, IProviderRepository
{
public ProviderRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper)
: base(serviceScopeFactory, mapper, (DatabaseContext context) => context.Providers)
: base(serviceScopeFactory, mapper, context => context.Providers)
{ }
public Task<ICollection<ProviderAbility>> GetManyAbilitiesAsync() => throw new NotImplementedException();
public async Task<ICollection<Provider>> SearchAsync(string name, string userEmail, int skip, int take)
{
using (var scope = ServiceScopeFactory.CreateScope())
@ -41,7 +38,23 @@ namespace Bit.Core.Repositories.EntityFramework
where string.IsNullOrWhiteSpace(name) || p.Name.Contains(name)
orderby p.CreationDate descending
select new { p }).Skip(skip).Take(take).Select(x => x.p);
return await query.ToArrayAsync();
var providers = await query.ToArrayAsync();
return Mapper.Map<List<Provider>>(providers);
}
}
public async Task<ICollection<ProviderAbility>> GetManyAbilitiesAsync()
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
return await GetDbSet(dbContext)
.Select(e => new ProviderAbility
{
Enabled = e.Enabled,
Id = e.Id,
UseEvents = e.UseEvents,
}).ToListAsync();
}
}
}

View File

@ -141,9 +141,19 @@ namespace Bit.Core.Repositories.EntityFramework
}
}
public Task<IEnumerable<ProviderUserOrganizationDetails>> GetManyOrganizationDetailsByUserAsync(Guid userId, ProviderUserStatusType? status = null)
public async Task<IEnumerable<ProviderUserOrganizationDetails>> GetManyOrganizationDetailsByUserAsync(Guid userId, ProviderUserStatusType? status = null)
{
throw new NotImplementedException();
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var view = new ProviderUserOrganizationDetailsViewQuery();
var query = from ou in view.Run(dbContext)
where ou.UserId == userId &&
(status == null || ou.Status == status)
select ou;
var organizationUsers = await query.ToListAsync();
return organizationUsers;
}
}
}
}

View File

@ -0,0 +1,41 @@
using System.Linq;
using Bit.Core.Models.EntityFramework;
using System;
using Bit.Core.Models.Data;
namespace Bit.Core.Repositories.EntityFramework.Queries
{
public class EventReadPageByProviderIdActingUserIdQuery : IQuery<Event>
{
private readonly Guid _providerId;
private readonly Guid _actingUserId;
private readonly DateTime _startDate;
private readonly DateTime _endDate;
private readonly DateTime? _beforeDate;
private readonly PageOptions _pageOptions;
public EventReadPageByProviderIdActingUserIdQuery(Guid providerId, Guid actingUserId,
DateTime startDate, DateTime endDate, DateTime? beforeDate, PageOptions pageOptions)
{
_providerId = providerId;
_actingUserId = actingUserId;
_startDate = startDate;
_endDate = endDate;
_beforeDate = beforeDate;
_pageOptions = pageOptions;
}
public IQueryable<Event> Run(DatabaseContext dbContext)
{
var q = from e in dbContext.Events
where e.Date >= _startDate &&
(_beforeDate != null || e.Date <= _endDate) &&
(_beforeDate == null || e.Date < _beforeDate.Value) &&
e.ProviderId == _providerId &&
e.ActingUserId == _actingUserId
orderby e.Date descending
select e;
return q.Skip(0).Take(_pageOptions.PageSize);
}
}
}

View File

@ -0,0 +1,38 @@
using System.Linq;
using Bit.Core.Models.EntityFramework;
using System;
using Bit.Core.Models.Data;
namespace Bit.Core.Repositories.EntityFramework.Queries
{
public class EventReadPageByProviderIdQuery: IQuery<Event>
{
private readonly Guid _providerId;
private readonly DateTime _startDate;
private readonly DateTime _endDate;
private readonly DateTime? _beforeDate;
private readonly PageOptions _pageOptions;
public EventReadPageByProviderIdQuery(Guid providerId, DateTime startDate,
DateTime endDate, DateTime? beforeDate, PageOptions pageOptions)
{
_providerId = providerId;
_startDate = startDate;
_endDate = endDate;
_beforeDate = beforeDate;
_pageOptions = pageOptions;
}
public IQueryable<Event> Run(DatabaseContext dbContext)
{
var q = from e in dbContext.Events
where e.Date >= _startDate &&
(_beforeDate != null || e.Date <= _endDate) &&
(_beforeDate == null || e.Date < _beforeDate.Value) &&
e.ProviderId == _providerId && e.OrganizationId == null
orderby e.Date descending
select e;
return q.Skip(0).Take(_pageOptions.PageSize);
}
}
}

View File

@ -12,8 +12,12 @@ namespace Bit.Core.Repositories.EntityFramework.Queries
join o in dbContext.Organizations on ou.OrganizationId equals o.Id
join su in dbContext.SsoUsers on ou.UserId equals su.UserId into su_g
from su in su_g.DefaultIfEmpty()
join po in dbContext.ProviderOrganizations on o.Id equals po.OrganizationId into po_g
from po in po_g.DefaultIfEmpty()
join p in dbContext.Providers on po.ProviderId equals p.Id into p_g
from p in p_g.DefaultIfEmpty()
where ((su == null || !su.OrganizationId.HasValue) || su.OrganizationId == ou.OrganizationId)
select new { ou, o, su };
select new { ou, o, su, p };
return query.Select(x => new OrganizationUserOrganizationDetails
{
OrganizationId = x.ou.OrganizationId,
@ -42,6 +46,8 @@ namespace Bit.Core.Repositories.EntityFramework.Queries
Permissions = x.ou.Permissions,
PublicKey = x.o.PublicKey,
PrivateKey = x.o.PrivateKey,
ProviderId = x.p.Id,
ProviderName = x.p.Name,
});
}
}

View File

@ -0,0 +1,46 @@
using System.Collections.Generic;
using System.Linq;
using Bit.Core.Models.Data;
namespace Bit.Core.Repositories.EntityFramework.Queries
{
public class ProviderUserOrganizationDetailsViewQuery : IQuery<ProviderUserOrganizationDetails>
{
public IQueryable<ProviderUserOrganizationDetails> Run(DatabaseContext dbContext)
{
var query = from pu in dbContext.ProviderUsers
join po in dbContext.ProviderOrganizations on pu.ProviderId equals po.ProviderId
join o in dbContext.Organizations on po.OrganizationId equals o.Id
join p in dbContext.Providers on pu.ProviderId equals p.Id
select new { pu, po, o, p };
return query.Select(x => new ProviderUserOrganizationDetails
{
OrganizationId = x.po.OrganizationId,
UserId = x.pu.UserId,
Name = x.o.Name,
Enabled = x.o.Enabled,
UsePolicies = x.o.UsePolicies,
UseSso = x.o.UseSso,
UseGroups = x.o.UseGroups,
UseDirectory = x.o.UseDirectory,
UseEvents = x.o.UseEvents,
UseTotp = x.o.UseTotp,
Use2fa = x.o.Use2fa,
UseApi = x.o.UseApi,
SelfHost = x.o.SelfHost,
UsersGetPremium = x.o.UsersGetPremium,
Seats = x.o.Seats,
MaxCollections = x.o.MaxCollections,
MaxStorageGb = x.o.MaxStorageGb,
Identifier = x.o.Identifier,
Key = x.po.Key,
Status = x.pu.Status,
Type = x.pu.Type,
PublicKey = x.o.PublicKey,
PrivateKey = x.o.PrivateKey,
ProviderId = x.p.Id,
ProviderName = x.p.Name,
});
}
}
}

View File

@ -33,6 +33,7 @@ namespace Bit.Core.Repositories.EntityFramework.Queries
Type = x.pu.Type,
Enabled = x.p.Enabled,
Permissions = x.pu.Permissions,
UseEvents = x.p.UseEvents,
});
}
}

View File

@ -14,6 +14,10 @@ namespace Bit.Core.Repositories
PageOptions pageOptions);
Task<PagedResult<IEvent>> GetManyByOrganizationActingUserAsync(Guid organizationId, Guid actingUserId,
DateTime startDate, DateTime endDate, PageOptions pageOptions);
Task<PagedResult<IEvent>> GetManyByProviderAsync(Guid providerId, DateTime startDate, DateTime endDate,
PageOptions pageOptions);
Task<PagedResult<IEvent>> GetManyByProviderActingUserAsync(Guid providerId, Guid actingUserId,
DateTime startDate, DateTime endDate, PageOptions pageOptions);
Task<PagedResult<IEvent>> GetManyByCipherAsync(Cipher cipher, DateTime startDate, DateTime endDate,
PageOptions pageOptions);
Task CreateAsync(IEvent e);

View File

@ -1,9 +0,0 @@
using System;
using Bit.Core.Models.Table.Provider;
namespace Bit.Core.Repositories
{
public interface IProviderOrganizationProviderUserRepository : IRepository<ProviderOrganizationProviderUser, Guid>
{
}
}

View File

@ -9,6 +9,6 @@ namespace Bit.Core.Repositories
public interface IProviderOrganizationRepository : IRepository<ProviderOrganization, Guid>
{
Task<ICollection<ProviderOrganizationOrganizationDetails>> GetManyDetailsByProviderAsync(Guid providerId);
Task<ICollection<ProviderOrganization>> GetManyByUserIdAsync(Guid userId);
Task<ProviderOrganization> GetByOrganizationId(Guid organizationId);
}
}

View File

@ -52,6 +52,27 @@ namespace Bit.Core.Repositories.SqlServer
}, startDate, endDate, pageOptions);
}
public async Task<PagedResult<IEvent>> GetManyByProviderAsync(Guid providerId,
DateTime startDate, DateTime endDate, PageOptions pageOptions)
{
return await GetManyAsync($"[{Schema}].[Event_ReadPageByProviderId]",
new Dictionary<string, object>
{
["@ProviderId"] = providerId
}, startDate, endDate, pageOptions);
}
public async Task<PagedResult<IEvent>> GetManyByProviderActingUserAsync(Guid providerId, Guid actingUserId,
DateTime startDate, DateTime endDate, PageOptions pageOptions)
{
return await GetManyAsync($"[{Schema}].[Event_ReadPageByProviderIdActingUserId]",
new Dictionary<string, object>
{
["@ProviderId"] = providerId,
["@ActingUserId"] = actingUserId
}, startDate, endDate, pageOptions);
}
public async Task<PagedResult<IEvent>> GetManyByCipherAsync(Cipher cipher, DateTime startDate, DateTime endDate,
PageOptions pageOptions)
{

View File

@ -1,17 +0,0 @@
using System;
using Bit.Core.Models.Table.Provider;
using Bit.Core.Settings;
namespace Bit.Core.Repositories.SqlServer
{
public class ProviderOrganizationProviderUserRepository : Repository<ProviderOrganizationProviderUser, Guid>, IProviderOrganizationProviderUserRepository
{
public ProviderOrganizationProviderUserRepository(GlobalSettings globalSettings)
: this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
{ }
public ProviderOrganizationProviderUserRepository(string connectionString, string readOnlyConnectionString)
: base(connectionString, readOnlyConnectionString)
{ }
}
}

View File

@ -34,16 +34,16 @@ namespace Bit.Core.Repositories.SqlServer
}
}
public async Task<ICollection<ProviderOrganization>> GetManyByUserIdAsync(Guid userId)
public async Task<ProviderOrganization> GetByOrganizationId(Guid organizationId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<ProviderOrganization>(
"[dbo].[ProviderOrganization_ReadByUserId]",
new { UserId = userId },
"[dbo].[ProviderOrganization_ReadByOrganizationId]",
new { OrganizationId = organizationId },
commandType: CommandType.StoredProcedure);
return results.ToList();
return results.SingleOrDefault();
}
}
}

View File

@ -1,5 +1,4 @@
using System;
using Bit.Core.Models.Table;
using System.Threading.Tasks;
using System.Data.SqlClient;
using System.Data;

View File

@ -44,6 +44,19 @@ namespace Bit.Core.Repositories.TableStorage
$"ActingUserId={actingUserId}__Date={{0}}", startDate, endDate, pageOptions);
}
public async Task<PagedResult<IEvent>> GetManyByProviderAsync(Guid providerId,
DateTime startDate, DateTime endDate, PageOptions pageOptions)
{
return await GetManyAsync($"ProviderId={providerId}", "Date={0}", startDate, endDate, pageOptions);
}
public async Task<PagedResult<IEvent>> GetManyByProviderActingUserAsync(Guid providerId, Guid actingUserId,
DateTime startDate, DateTime endDate, PageOptions pageOptions)
{
return await GetManyAsync($"ProviderId={providerId}",
$"ActingUserId={actingUserId}__Date={{0}}", startDate, endDate, pageOptions);
}
public async Task<PagedResult<IEvent>> GetManyByCipherAsync(Cipher cipher, DateTime startDate, DateTime endDate,
PageOptions pageOptions)
{

View File

@ -59,5 +59,6 @@ namespace Bit.Core.Services
Task RotateApiKeyAsync(Organization organization);
Task DeleteSsoUserAsync(Guid userId, Guid? organizationId);
Task<Organization> UpdateOrganizationKeysAsync(Guid orgId, string publicKey, string privateKey);
Task<bool> HasConfirmedOwnersExceptAsync(Guid organizationId, IEnumerable<Guid> organizationUsersId);
}
}

View File

@ -26,6 +26,6 @@ namespace Bit.Core.Services
Task AddOrganization(Guid providerId, Guid organizationId, Guid addingUserId, string key);
Task<ProviderOrganization> CreateOrganizationAsync(Guid providerId, OrganizationSignup organizationSignup, User user);
Task RemoveOrganization(Guid providerOrganizationId, Guid removingUserId);
Task RemoveOrganization(Guid providerId, Guid providerOrganizationId, Guid removingUserId);
}
}

View File

@ -16,6 +16,7 @@ namespace Bit.Core.Services
{
private readonly IEventWriteService _eventWriteService;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IProviderUserRepository _providerUserRepository;
private readonly IApplicationCacheService _applicationCacheService;
private readonly ICurrentContext _currentContext;
private readonly GlobalSettings _globalSettings;
@ -23,12 +24,14 @@ namespace Bit.Core.Services
public EventService(
IEventWriteService eventWriteService,
IOrganizationUserRepository organizationUserRepository,
IProviderUserRepository providerUserRepository,
IApplicationCacheService applicationCacheService,
ICurrentContext currentContext,
GlobalSettings globalSettings)
{
_eventWriteService = eventWriteService;
_organizationUserRepository = organizationUserRepository;
_providerUserRepository = providerUserRepository;
_applicationCacheService = applicationCacheService;
_currentContext = currentContext;
_globalSettings = globalSettings;
@ -58,10 +61,23 @@ namespace Bit.Core.Services
Type = type,
Date = DateTime.UtcNow
});
var providerAbilities = await _applicationCacheService.GetProviderAbilitiesAsync();
var providers = await _currentContext.ProviderMembershipAsync(_providerUserRepository, userId);
var providerEvents = providers.Where(o => CanUseProviderEvents(providerAbilities, o.Id))
.Select(p => new EventMessage(_currentContext)
{
ProviderId = p.Id,
UserId = userId,
ActingUserId = userId,
Type = type,
Date = DateTime.UtcNow
});
if (orgEvents.Any())
if (orgEvents.Any() || providerEvents.Any())
{
events.AddRange(orgEvents);
events.AddRange(providerEvents);
await _eventWriteService.CreateManyAsync(events);
}
else
@ -117,6 +133,7 @@ namespace Bit.Core.Services
CipherId = cipher.Id,
Type = type,
ActingUserId = _currentContext?.UserId,
ProviderId = await GetProviderIdAsync(cipher.OrganizationId),
Date = date.GetValueOrDefault(DateTime.UtcNow)
};
}
@ -135,6 +152,7 @@ namespace Bit.Core.Services
CollectionId = collection.Id,
Type = type,
ActingUserId = _currentContext?.UserId,
ProviderId = await GetProviderIdAsync(collection.OrganizationId),
Date = date.GetValueOrDefault(DateTime.UtcNow)
};
await _eventWriteService.CreateAsync(e);
@ -154,6 +172,7 @@ namespace Bit.Core.Services
GroupId = group.Id,
Type = type,
ActingUserId = _currentContext?.UserId,
ProviderId = await GetProviderIdAsync(@group.OrganizationId),
Date = date.GetValueOrDefault(DateTime.UtcNow)
};
await _eventWriteService.CreateAsync(e);
@ -173,6 +192,7 @@ namespace Bit.Core.Services
PolicyId = policy.Id,
Type = type,
ActingUserId = _currentContext?.UserId,
ProviderId = await GetProviderIdAsync(policy.OrganizationId),
Date = date.GetValueOrDefault(DateTime.UtcNow)
};
await _eventWriteService.CreateAsync(e);
@ -192,11 +212,13 @@ namespace Bit.Core.Services
{
continue;
}
eventMessages.Add(new EventMessage
{
OrganizationId = organizationUser.OrganizationId,
UserId = organizationUser.UserId,
OrganizationUserId = organizationUser.Id,
ProviderId = await GetProviderIdAsync(organizationUser.OrganizationId),
Type = type,
ActingUserId = _currentContext?.UserId,
Date = date.GetValueOrDefault(DateTime.UtcNow)
@ -216,6 +238,7 @@ namespace Bit.Core.Services
var e = new EventMessage(_currentContext)
{
OrganizationId = organization.Id,
ProviderId = await GetProviderIdAsync(organization.Id),
Type = type,
ActingUserId = _currentContext?.UserId,
Date = date.GetValueOrDefault(DateTime.UtcNow)
@ -251,6 +274,16 @@ namespace Bit.Core.Services
await _eventWriteService.CreateManyAsync(eventMessages);
}
private async Task<Guid?> GetProviderIdAsync(Guid? orgId)
{
if (_currentContext == null || !orgId.HasValue)
{
return null;
}
return await _currentContext.ProviderIdForOrg(orgId.Value);
}
private bool CanUseEvents(IDictionary<Guid, OrganizationAbility> orgAbilities, Guid orgId)
{

View File

@ -1685,7 +1685,7 @@ namespace Bit.Core.Services
return result;
}
private async Task<bool> HasConfirmedOwnersExceptAsync(Guid organizationId, IEnumerable<Guid> organizationUsersId)
public async Task<bool> HasConfirmedOwnersExceptAsync(Guid organizationId, IEnumerable<Guid> organizationUsersId)
{
var confirmedOwners = await GetConfirmedOwnersAsync(organizationId);
var confirmedOwnersIds = confirmedOwners.Select(u => u.Id);

View File

@ -31,6 +31,6 @@ namespace Bit.Core.Services
public Task AddOrganization(Guid providerId, Guid organizationId, Guid addingUserId, string key) => throw new NotImplementedException();
public Task<ProviderOrganization> CreateOrganizationAsync(Guid providerId, OrganizationSignup organizationSignup, User user) => throw new NotImplementedException();
public Task RemoveOrganization(Guid providerOrganizationId, Guid removingUserId) => throw new NotImplementedException();
public Task RemoveOrganization(Guid providerId, Guid providerOrganizationId, Guid removingUserId) => throw new NotImplementedException();
}
}

View File

@ -113,7 +113,6 @@ namespace Bit.Core.Utilities
services.AddSingleton<IProviderRepository, EntityFrameworkRepos.ProviderRepository>();
services.AddSingleton<IProviderUserRepository, EntityFrameworkRepos.ProviderUserRepository>();
services.AddSingleton<IProviderOrganizationRepository, EntityFrameworkRepos.ProviderOrganizationRepository>();
services.AddSingleton<IProviderOrganizationProviderUserRepository, EntityFrameworkRepos.ProviderOrganizationProviderUserRepository>();
}
else
{
@ -138,7 +137,6 @@ namespace Bit.Core.Utilities
services.AddSingleton<IProviderRepository, SqlServerRepos.ProviderRepository>();
services.AddSingleton<IProviderUserRepository, SqlServerRepos.ProviderUserRepository>();
services.AddSingleton<IProviderOrganizationRepository, SqlServerRepos.ProviderOrganizationRepository>();
services.AddSingleton<IProviderOrganizationProviderUserRepository, SqlServerRepos.ProviderOrganizationProviderUserRepository>();
services.AddSingleton<ITransactionRepository, SqlServerRepos.TransactionRepository>();
services.AddSingleton<IU2fRepository, SqlServerRepos.U2fRepository>();
services.AddSingleton<IUserRepository, SqlServerRepos.UserRepository>();

View File

@ -10,21 +10,21 @@ namespace Bit.Notifications
[Authorize("Application")]
public class NotificationsHub : Microsoft.AspNetCore.SignalR.Hub
{
private readonly IProviderOrganizationRepository _providerOrganizationRepository;
private readonly IProviderUserRepository _providerUserRepository;
private readonly ConnectionCounter _connectionCounter;
private readonly GlobalSettings _globalSettings;
public NotificationsHub(IProviderOrganizationRepository providerOrganizationRepository,
public NotificationsHub(IProviderUserRepository providerUserRepository,
ConnectionCounter connectionCounter, GlobalSettings globalSettings)
{
_providerOrganizationRepository = providerOrganizationRepository;
_providerUserRepository = providerUserRepository;
_connectionCounter = connectionCounter;
_globalSettings = globalSettings;
}
public override async Task OnConnectedAsync()
{
var currentContext = new CurrentContext(_providerOrganizationRepository);
var currentContext = new CurrentContext(_providerUserRepository);
await currentContext.BuildAsync(Context.User, _globalSettings);
if (currentContext.Organizations != null)
{
@ -39,7 +39,7 @@ namespace Bit.Notifications
public override async Task OnDisconnectedAsync(Exception exception)
{
var currentContext = new CurrentContext(_providerOrganizationRepository);
var currentContext = new CurrentContext(_providerUserRepository);
await currentContext.BuildAsync(Context.User, _globalSettings);
if (currentContext.Organizations != null)
{

View File

@ -70,7 +70,9 @@
</ItemGroup>
<ItemGroup>
<Build Include="dbo\Stored Procedures\EmergencyAccessDetails_ReadByIdGrantorId.sql" />
<Build Include="dbo\Stored Procedures\ProviderOrganization_ReadByUserId.sql" />
<Build Include="dbo\Stored Procedures\Event_ReadPageByProviderId.sql" />
<Build Include="dbo\Stored Procedures\Event_ReadPageByProviderIdActingUserId.sql" />
<Build Include="dbo\Stored Procedures\ProviderOrganization_ReadByOrganizationId.sql" />
<Build Include="dbo\Stored Procedures\ProviderUserProviderOrganizationDetails_ReadByUserIdStatus.sql" />
<Build Include="dbo\Stored Procedures\SsoConfig_Create.sql" />
<Build Include="dbo\Stored Procedures\SsoConfig_ReadByIdentifier.sql" />
@ -363,10 +365,5 @@
<Build Include="dbo\Stored Procedures\ProviderOrganizationOrganizationDetails_ReadByProviderId.sql" />
<Build Include="dbo\Stored Procedures\User_BumpAccountRevisionDateByProviderId.sql" />
<Build Include="dbo\Stored Procedures\User_BumpAccountRevisionDateByProviderUserId.sql" />
<Build Include="dbo\Tables\ProviderOrganizationProviderUser.sql" />
<Build Include="dbo\Stored Procedures\ProviderOrganizationProviderUser_Create.sql" />
<Build Include="dbo\Stored Procedures\ProviderOrganizationProviderUser_DeleteById.sql" />
<Build Include="dbo\Stored Procedures\ProviderOrganizationProviderUser_ReadById.sql" />
<Build Include="dbo\Stored Procedures\ProviderOrganizationProviderUser_Update.sql" />
</ItemGroup>
</Project>

View File

@ -1,13 +1,15 @@
CREATE PROCEDURE [dbo].[Event_Create]
@Id UNIQUEIDENTIFIER,
@Id UNIQUEIDENTIFIER OUTPUT,
@Type INT,
@UserId UNIQUEIDENTIFIER,
@OrganizationId UNIQUEIDENTIFIER,
@ProviderId UNIQUEIDENTIFIER,
@CipherId UNIQUEIDENTIFIER,
@CollectionId UNIQUEIDENTIFIER,
@PolicyId UNIQUEIDENTIFIER,
@GroupId UNIQUEIDENTIFIER,
@OrganizationUserId UNIQUEIDENTIFIER,
@ProviderUserId UNIQUEIDENTIFIER,
@ActingUserId UNIQUEIDENTIFIER,
@DeviceType SMALLINT,
@IpAddress VARCHAR(50),
@ -22,11 +24,13 @@ BEGIN
[Type],
[UserId],
[OrganizationId],
[ProviderId],
[CipherId],
[CollectionId],
[PolicyId],
[GroupId],
[OrganizationUserId],
[ProviderUserId],
[ActingUserId],
[DeviceType],
[IpAddress],
@ -38,11 +42,13 @@ BEGIN
@Type,
@UserId,
@OrganizationId,
@ProviderId,
@CipherId,
@CollectionId,
@PolicyId,
@GroupId,
@OrganizationUserId,
@ProviderUserId,
@ActingUserId,
@DeviceType,
@IpAddress,

View File

@ -0,0 +1,23 @@
CREATE PROCEDURE [dbo].[Event_ReadPageByProviderId]
@ProviderId UNIQUEIDENTIFIER,
@StartDate DATETIME2(7),
@EndDate DATETIME2(7),
@BeforeDate DATETIME2(7),
@PageSize INT
AS
BEGIN
SET NOCOUNT ON
SELECT
*
FROM
[dbo].[EventView]
WHERE
[Date] >= @StartDate
AND (@BeforeDate IS NOT NULL OR [Date] <= @EndDate)
AND (@BeforeDate IS NULL OR [Date] < @BeforeDate)
AND [Providerid] = @ProviderId
ORDER BY [Date] DESC
OFFSET 0 ROWS
FETCH NEXT @PageSize ROWS ONLY
END

View File

@ -0,0 +1,25 @@
CREATE PROCEDURE [dbo].[Event_ReadPageByProviderIdActingUserId]
@ProviderId UNIQUEIDENTIFIER,
@ActingUserId UNIQUEIDENTIFIER,
@StartDate DATETIME2(7),
@EndDate DATETIME2(7),
@BeforeDate DATETIME2(7),
@PageSize INT
AS
BEGIN
SET NOCOUNT ON
SELECT
*
FROM
[dbo].[EventView]
WHERE
[Date] >= @StartDate
AND (@BeforeDate IS NOT NULL OR [Date] <= @EndDate)
AND (@BeforeDate IS NULL OR [Date] < @BeforeDate)
AND [ProviderId] = @ProviderId
AND [ActingUserId] = @ActingUserId
ORDER BY [Date] DESC
OFFSET 0 ROWS
FETCH NEXT @PageSize ROWS ONLY
END

View File

@ -1,33 +0,0 @@
CREATE PROCEDURE [dbo].[ProviderOrganizationProviderUser_Create]
@Id UNIQUEIDENTIFIER,
@ProviderOrganizationId UNIQUEIDENTIFIER,
@ProviderUserId UNIQUEIDENTIFIER,
@Type TINYINT,
@Permissions NVARCHAR(MAX),
@CreationDate DATETIME2(7),
@RevisionDate DATETIME2(7)
AS
BEGIN
SET NOCOUNT ON
INSERT INTO [dbo].[ProviderOrganizationProviderUser]
(
[Id],
[ProviderOrganizationId],
[ProviderUserId],
[Type],
[Permissions],
[CreationDate],
[RevisionDate]
)
VALUES
(
@Id,
@ProviderOrganizationId,
@ProviderUserId,
@Type,
@Permissions,
@CreationDate,
@RevisionDate
)
END

View File

@ -1,27 +0,0 @@
CREATE PROCEDURE [dbo].[ProviderOrganizationProviderUser_DeleteById]
@Id UNIQUEIDENTIFIER
AS
BEGIN
SET NOCOUNT ON
BEGIN TRANSACTION POPU_DeleteById
DECLARE @ProviderUserId UNIQUEIDENTIFIER
SELECT
@ProviderUserId = [ProviderUserId]
FROM
[dbo].[ProviderOrganizationProviderUser]
WHERE
[Id] = @Id
DELETE
FROM
[dbo].[ProviderOrganizationProviderUser]
WHERE
[Id] = @Id
EXEC [dbo].[User_BumpAccountRevisionDateByProviderUserId] @ProviderUserId
COMMIT TRANSACTION POPU_DeleteById
END

View File

@ -1,13 +0,0 @@
CREATE PROCEDURE [dbo].[ProviderOrganizationProviderUser_ReadById]
@Id UNIQUEIDENTIFIER
AS
BEGIN
SET NOCOUNT ON
SELECT
*
FROM
[dbo].[ProviderOrganizationProviderUser]
WHERE
[Id] = @Id
END

View File

@ -1,26 +0,0 @@
CREATE PROCEDURE [dbo].[ProviderOrganizationProviderUser_Update]
@Id UNIQUEIDENTIFIER,
@ProviderOrganizationId UNIQUEIDENTIFIER,
@ProviderUserId UNIQUEIDENTIFIER,
@Type TINYINT,
@Permissions NVARCHAR(MAX),
@CreationDate DATETIME2(7),
@RevisionDate DATETIME2(7)
AS
BEGIN
SET NOCOUNT ON
UPDATE
[dbo].[ProviderOrganizationProviderUser]
SET
[ProviderOrganizationId] = @ProviderOrganizationId,
[ProviderUserId] = @ProviderUserId,
[Type] = @Type,
[Permissions] = @Permissions,
[CreationDate] = @CreationDate,
[RevisionDate] = @RevisionDate
WHERE
[Id] = @Id
EXEC [dbo].[User_BumpAccountRevisionDateByProviderUserId] @ProviderUserId
END

View File

@ -1,5 +1,5 @@
CREATE PROCEDURE [dbo].[ProviderOrganization_Create]
@Id UNIQUEIDENTIFIER,
@Id UNIQUEIDENTIFIER OUTPUT,
@ProviderId UNIQUEIDENTIFIER,
@OrganizationId UNIQUEIDENTIFIER,
@Key VARCHAR(MAX),

View File

@ -0,0 +1,13 @@
CREATE PROCEDURE [dbo].[ProviderOrganization_ReadByOrganizationId]
@OrganizationId UNIQUEIDENTIFIER
AS
BEGIN
SET NOCOUNT ON
SELECT
*
FROM
[dbo].[ProviderOrganizationView]
WHERE
[OrganizationId] = @OrganizationId
END

View File

@ -1,17 +0,0 @@
CREATE PROCEDURE [dbo].[ProviderOrganization_ReadByUserId]
@UserId UNIQUEIDENTIFIER
AS
BEGIN
SET NOCOUNT ON
SELECT
PO.*
FROM
[dbo].[ProviderOrganizationView] PO
INNER JOIN
[dbo].[Provider] P ON PO.[ProviderId] = P.[Id]
INNER JOIN
[dbo].[ProviderUser] PU ON P.[Id] = PU.[ProviderId]
WHERE
PU.[UserId] = @UserId
END

View File

@ -1,5 +1,5 @@
CREATE PROCEDURE [dbo].[ProviderUser_Create]
@Id UNIQUEIDENTIFIER,
@Id UNIQUEIDENTIFIER OUTPUT,
@ProviderId UNIQUEIDENTIFIER,
@UserId UNIQUEIDENTIFIER,
@Email NVARCHAR(256),

View File

@ -1,5 +1,5 @@
CREATE PROCEDURE [dbo].[Provider_Create]
@Id UNIQUEIDENTIFIER,
@Id UNIQUEIDENTIFIER OUTPUT,
@Name NVARCHAR(50),
@BusinessName NVARCHAR(50),
@BusinessAddress1 NVARCHAR(50),

View File

@ -7,6 +7,7 @@ BEGIN
SELECT
PU.[Id],
PU.[UserId],
U.[PublicKey]
FROM
@ProviderUserIds PUIDs

View File

@ -12,6 +12,8 @@
[DeviceType] SMALLINT NULL,
[IpAddress] VARCHAR(50) NULL,
[Date] DATETIME2 (7) NOT NULL,
[ProviderId] UNIQUEIDENTIFIER NULL,
[ProviderUserId] UNIQUEIDENTIFIER NULL,
CONSTRAINT [PK_Event] PRIMARY KEY CLUSTERED ([Id] ASC)
);

View File

@ -1,12 +0,0 @@
CREATE TABLE [dbo].[ProviderOrganizationProviderUser] (
[Id] UNIQUEIDENTIFIER NOT NULL,
[ProviderOrganizationId] UNIQUEIDENTIFIER NOT NULL,
[ProviderUserId] UNIQUEIDENTIFIER NULL,
[Type] TINYINT NOT NULL,
[Permissions] NVARCHAR (MAX) NULL,
[CreationDate] DATETIME2 (7) NOT NULL,
[RevisionDate] DATETIME2 (7) NOT NULL,
CONSTRAINT [PK_ProviderOrganizationProviderUser] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_ProviderOrganizationProviderUser_Provider] FOREIGN KEY ([ProviderOrganizationId]) REFERENCES [dbo].[ProviderOrganization] ([Id]) ON DELETE CASCADE,
CONSTRAINT [FK_ProviderOrganizationProviderUser_User] FOREIGN KEY ([ProviderUserId]) REFERENCES [dbo].[ProviderUser] ([Id])
);

View File

@ -26,6 +26,7 @@ SELECT
PU.[Status],
PU.[Type],
PO.[ProviderId],
PU.[Id] ProviderUserId,
P.[Name] ProviderName
FROM
[dbo].[ProviderUser] PU

View File

@ -14,6 +14,7 @@ namespace Bit.Core.Test.Services
private readonly IEventWriteService _eventWriteService;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IProviderUserRepository _providerUserRepository;
private readonly IApplicationCacheService _applicationCacheService;
private readonly CurrentContext _currentContext;
private readonly GlobalSettings _globalSettings;
@ -22,6 +23,7 @@ namespace Bit.Core.Test.Services
{
_eventWriteService = Substitute.For<IEventWriteService>();
_organizationUserRepository = Substitute.For<IOrganizationUserRepository>();
_providerUserRepository = Substitute.For<IProviderUserRepository>();
_applicationCacheService = Substitute.For<IApplicationCacheService>();
_currentContext = new CurrentContext(null);
_globalSettings = new GlobalSettings();
@ -29,6 +31,7 @@ namespace Bit.Core.Test.Services
_sut = new EventService(
_eventWriteService,
_organizationUserRepository,
_providerUserRepository,
_applicationCacheService,
_currentContext,
_globalSettings

View File

@ -146,7 +146,7 @@ END
GO
CREATE PROCEDURE [dbo].[Provider_Create]
@Id UNIQUEIDENTIFIER,
@Id UNIQUEIDENTIFIER OUTPUT,
@Name NVARCHAR(50),
@BusinessName NVARCHAR(50),
@BusinessAddress1 NVARCHAR(50),
@ -341,7 +341,7 @@ END
GO
CREATE PROCEDURE [dbo].[ProviderUser_Create]
@Id UNIQUEIDENTIFIER,
@Id UNIQUEIDENTIFIER OUTPUT,
@ProviderId UNIQUEIDENTIFIER,
@UserId UNIQUEIDENTIFIER,
@Email NVARCHAR(256),
@ -564,7 +564,7 @@ END
GO
CREATE PROCEDURE [dbo].[ProviderOrganization_Create]
@Id UNIQUEIDENTIFIER,
@Id UNIQUEIDENTIFIER OUTPUT,
@ProviderId UNIQUEIDENTIFIER,
@OrganizationId UNIQUEIDENTIFIER,
@Key VARCHAR(MAX),
@ -687,154 +687,6 @@ BEGIN
END
GO
IF OBJECT_ID('[dbo].[ProviderOrganizationProviderUser]') IS NULL
BEGIN
CREATE TABLE [dbo].[ProviderOrganizationProviderUser] (
[Id] UNIQUEIDENTIFIER NOT NULL,
[ProviderOrganizationId] UNIQUEIDENTIFIER NOT NULL,
[ProviderUserId] UNIQUEIDENTIFIER NULL,
[Type] TINYINT NOT NULL,
[Permissions] NVARCHAR (MAX) NULL,
[CreationDate] DATETIME2 (7) NOT NULL,
[RevisionDate] DATETIME2 (7) NOT NULL,
CONSTRAINT [PK_ProviderOrganizationProviderUser] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_ProviderOrganizationProviderUser_Provider] FOREIGN KEY ([ProviderOrganizationId]) REFERENCES [dbo].[ProviderOrganization] ([Id]) ON DELETE CASCADE,
CONSTRAINT [FK_ProviderOrganizationProviderUser_User] FOREIGN KEY ([ProviderUserId]) REFERENCES [dbo].[ProviderUser] ([Id])
);
END
GO
IF OBJECT_ID('[dbo].[ProviderOrganizationProviderUser_Create]') IS NOT NULL
BEGIN
DROP PROCEDURE [dbo].[ProviderOrganizationProviderUser_Create]
END
GO
CREATE PROCEDURE [dbo].[ProviderOrganizationProviderUser_Create]
@Id UNIQUEIDENTIFIER,
@ProviderOrganizationId UNIQUEIDENTIFIER,
@ProviderUserId UNIQUEIDENTIFIER,
@Type TINYINT,
@Permissions NVARCHAR(MAX),
@CreationDate DATETIME2(7),
@RevisionDate DATETIME2(7)
AS
BEGIN
SET NOCOUNT ON
INSERT INTO [dbo].[ProviderOrganizationProviderUser]
(
[Id],
[ProviderOrganizationId],
[ProviderUserId],
[Type],
[Permissions],
[CreationDate],
[RevisionDate]
)
VALUES
(
@Id,
@ProviderOrganizationId,
@ProviderUserId,
@Type,
@Permissions,
@CreationDate,
@RevisionDate
)
END
GO
IF OBJECT_ID('[dbo].[ProviderOrganizationProviderUser_DeleteById]') IS NOT NULL
BEGIN
DROP PROCEDURE [dbo].[ProviderOrganizationProviderUser_DeleteById]
END
GO
CREATE PROCEDURE [dbo].[ProviderOrganizationProviderUser_DeleteById]
@Id UNIQUEIDENTIFIER
AS
BEGIN
SET NOCOUNT ON
BEGIN TRANSACTION POPU_DeleteById
DECLARE @ProviderUserId UNIQUEIDENTIFIER
SELECT
@ProviderUserId = [ProviderUserId]
FROM
[dbo].[ProviderOrganizationProviderUser]
WHERE
[Id] = @Id
DELETE
FROM
[dbo].[ProviderOrganizationProviderUser]
WHERE
[Id] = @Id
EXEC [dbo].[User_BumpAccountRevisionDateByProviderUserId] @ProviderUserId
COMMIT TRANSACTION POPU_DeleteById
END
GO
IF OBJECT_ID('[dbo].[ProviderOrganizationProviderUser_ReadById]') IS NOT NULL
BEGIN
DROP PROCEDURE [dbo].[ProviderOrganizationProviderUser_ReadById]
END
GO
CREATE PROCEDURE [dbo].[ProviderOrganizationProviderUser_ReadById]
@Id UNIQUEIDENTIFIER
AS
BEGIN
SET NOCOUNT ON
SELECT
*
FROM
[dbo].[ProviderOrganizationProviderUser]
WHERE
[Id] = @Id
END
GO
IF OBJECT_ID('[dbo].[ProviderOrganizationProviderUser_Update]') IS NOT NULL
BEGIN
DROP PROCEDURE [dbo].[ProviderOrganizationProviderUser_Update]
END
GO
CREATE PROCEDURE [dbo].[ProviderOrganizationProviderUser_Update]
@Id UNIQUEIDENTIFIER,
@ProviderOrganizationId UNIQUEIDENTIFIER,
@ProviderUserId UNIQUEIDENTIFIER,
@Type TINYINT,
@Permissions NVARCHAR(MAX),
@CreationDate DATETIME2(7),
@RevisionDate DATETIME2(7)
AS
BEGIN
SET NOCOUNT ON
UPDATE
[dbo].[ProviderOrganizationProviderUser]
SET
[ProviderOrganizationId] = @ProviderOrganizationId,
[ProviderUserId] = @ProviderUserId,
[Type] = @Type,
[Permissions] = @Permissions,
[CreationDate] = @CreationDate,
[RevisionDate] = @RevisionDate
WHERE
[Id] = @Id
EXEC [dbo].[User_BumpAccountRevisionDateByProviderUserId] @ProviderUserId
END
GO
IF OBJECT_ID('[dbo].[ProviderUser_ReadCountByProviderIdEmail]') IS NOT NULL
BEGIN
DROP PROCEDURE [dbo].[ProviderUser_ReadCountByProviderIdEmail]
@ -1163,6 +1015,7 @@ BEGIN
SELECT
PU.[Id],
PU.[UserId],
U.[PublicKey]
FROM
@ProviderUserIds PUIDs
@ -1269,31 +1122,6 @@ LEFT JOIN
[dbo].[Provider] P ON P.[Id] = PO.[ProviderId]
GO
IF OBJECT_ID('[dbo].[ProviderOrganization_ReadByUserId]') IS NOT NULL
BEGIN
DROP PROCEDURE [dbo].[ProviderOrganization_ReadByUserId]
END
GO
CREATE PROCEDURE [dbo].[ProviderOrganization_ReadByUserId]
@UserId UNIQUEIDENTIFIER
AS
BEGIN
SET NOCOUNT ON
SELECT
PO.*
FROM
[dbo].[ProviderOrganizationView] PO
INNER JOIN
[dbo].[Provider] P ON PO.[ProviderId] = P.[Id]
INNER JOIN
[dbo].[ProviderUser] PU ON P.[Id] = PU.[ProviderId]
WHERE
PU.[UserId] = @UserId
END
GO
IF EXISTS(SELECT * FROM sys.views WHERE [Name] = 'ProviderUserProviderOrganizationDetailsView')
BEGIN
DROP VIEW [dbo].[ProviderUserProviderOrganizationDetailsView];
@ -1328,6 +1156,7 @@ SELECT
PU.[Status],
PU.[Type],
PO.[ProviderId],
PU.[Id] ProviderUserId,
P.[Name] ProviderName
FROM
[dbo].[ProviderUser] PU
@ -1360,3 +1189,222 @@ BEGIN
[UserId] = @UserId
AND (@Status IS NULL OR [Status] = @Status)
END
GO
IF OBJECT_ID('[dbo].[ProviderOrganizationProviderUser_Create]') IS NOT NULL
BEGIN
DROP PROCEDURE [dbo].[ProviderOrganizationProviderUser_Create]
END
GO
IF OBJECT_ID('[dbo].[ProviderOrganizationProviderUser_Update]') IS NOT NULL
BEGIN
DROP PROCEDURE [dbo].[ProviderOrganizationProviderUser_Update]
END
GO
IF OBJECT_ID('[dbo].[ProviderOrganizationProviderUser_DeleteById]') IS NOT NULL
BEGIN
DROP PROCEDURE [dbo].[ProviderOrganizationProviderUser_DeleteById]
END
GO
IF OBJECT_ID('[dbo].[ProviderOrganizationProviderUser_ReadById]') IS NOT NULL
BEGIN
DROP PROCEDURE [dbo].[ProviderOrganizationProviderUser_ReadById]
END
GO
IF OBJECT_ID('[dbo].[ProviderOrganizationProviderUser]') IS NOT NULL
BEGIN
DROP TABLE [dbo].[ProviderOrganizationProviderUser];
END
GO
IF OBJECT_ID('[dbo].[ProviderOrganization_ReadByUserId]') IS NOT NULL
BEGIN
DROP PROCEDURE [dbo].[ProviderOrganization_ReadByUserId]
END
GO
IF COL_LENGTH('[dbo].[OrganizationUser]', 'ResetPasswordKey') IS NULL
BEGIN
ALTER TABLE
[dbo].[OrganizationUser]
ADD
[ResetPasswordKey] VARCHAR(MAX) NULL
END
GO
IF COL_LENGTH('[dbo].[Event]', 'ProviderId') IS NULL
BEGIN
ALTER TABLE
[dbo].[Event]
ADD
[ProviderId] UNIQUEIDENTIFIER NULL
END
GO
IF COL_LENGTH('[dbo].[Event]', 'ProviderUserId') IS NULL
BEGIN
ALTER TABLE
[dbo].[Event]
ADD
[ProviderUserId] UNIQUEIDENTIFIER NULL
END
GO
IF OBJECT_ID('[dbo].[Event_Create]') IS NOT NULL
BEGIN
DROP PROCEDURE [dbo].[Event_Create]
END
GO
CREATE PROCEDURE [dbo].[Event_Create]
@Id UNIQUEIDENTIFIER OUTPUT,
@Type INT,
@UserId UNIQUEIDENTIFIER,
@OrganizationId UNIQUEIDENTIFIER,
@ProviderId UNIQUEIDENTIFIER,
@CipherId UNIQUEIDENTIFIER,
@CollectionId UNIQUEIDENTIFIER,
@PolicyId UNIQUEIDENTIFIER,
@GroupId UNIQUEIDENTIFIER,
@OrganizationUserId UNIQUEIDENTIFIER,
@ProviderUserId UNIQUEIDENTIFIER,
@ActingUserId UNIQUEIDENTIFIER,
@DeviceType SMALLINT,
@IpAddress VARCHAR(50),
@Date DATETIME2(7)
AS
BEGIN
SET NOCOUNT ON
INSERT INTO [dbo].[Event]
(
[Id],
[Type],
[UserId],
[OrganizationId],
[ProviderId],
[CipherId],
[CollectionId],
[PolicyId],
[GroupId],
[OrganizationUserId],
[ProviderUserId],
[ActingUserId],
[DeviceType],
[IpAddress],
[Date]
)
VALUES
(
@Id,
@Type,
@UserId,
@OrganizationId,
@ProviderId,
@CipherId,
@CollectionId,
@PolicyId,
@GroupId,
@OrganizationUserId,
@ProviderUserId,
@ActingUserId,
@DeviceType,
@IpAddress,
@Date
)
END
GO
IF OBJECT_ID('[dbo].[EventView]') IS NOT NULL
BEGIN
EXECUTE sp_refreshview N'[dbo].[EventView]';
END
GO
IF OBJECT_ID('[dbo].[Event_ReadPageByProviderId]') IS NOT NULL
BEGIN
DROP PROCEDURE [dbo].[Event_ReadPageByProviderId]
END
GO
CREATE PROCEDURE [dbo].[Event_ReadPageByProviderId]
@ProviderId UNIQUEIDENTIFIER,
@StartDate DATETIME2(7),
@EndDate DATETIME2(7),
@BeforeDate DATETIME2(7),
@PageSize INT
AS
BEGIN
SET NOCOUNT ON
SELECT
*
FROM
[dbo].[EventView]
WHERE
[Date] >= @StartDate
AND (@BeforeDate IS NOT NULL OR [Date] <= @EndDate)
AND (@BeforeDate IS NULL OR [Date] < @BeforeDate)
AND [Providerid] = @ProviderId
ORDER BY [Date] DESC
OFFSET 0 ROWS
FETCH NEXT @PageSize ROWS ONLY
END
GO
IF OBJECT_ID('[dbo].[Event_ReadPageByProviderIdActingUserId]') IS NOT NULL
BEGIN
DROP PROCEDURE [dbo].[Event_ReadPageByProviderIdActingUserId]
END
GO
CREATE PROCEDURE [dbo].[Event_ReadPageByProviderIdActingUserId]
@ProviderId UNIQUEIDENTIFIER,
@ActingUserId UNIQUEIDENTIFIER,
@StartDate DATETIME2(7),
@EndDate DATETIME2(7),
@BeforeDate DATETIME2(7),
@PageSize INT
AS
BEGIN
SET NOCOUNT ON
SELECT
*
FROM
[dbo].[EventView]
WHERE
[Date] >= @StartDate
AND (@BeforeDate IS NOT NULL OR [Date] <= @EndDate)
AND (@BeforeDate IS NULL OR [Date] < @BeforeDate)
AND [ProviderId] = @ProviderId
AND [ActingUserId] = @ActingUserId
ORDER BY [Date] DESC
OFFSET 0 ROWS
FETCH NEXT @PageSize ROWS ONLY
END
GO
IF OBJECT_ID('[dbo].[ProviderOrganization_ReadByOrganizationId]') IS NOT NULL
BEGIN
DROP PROCEDURE [dbo].[ProviderOrganization_ReadByOrganizationId]
END
GO
CREATE PROCEDURE [dbo].[ProviderOrganization_ReadByOrganizationId]
@OrganizationId UNIQUEIDENTIFIER
AS
BEGIN
SET NOCOUNT ON
SELECT
*
FROM
[dbo].[ProviderOrganizationView]
WHERE
[OrganizationId] = @OrganizationId
END

View File

@ -0,0 +1,91 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Bit.MySqlMigrations.Migrations
{
public partial class RemoveProviderOrganizationProviderUser : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "ProviderOrganizationProviderUser");
migrationBuilder.AddColumn<bool>(
name: "UseEvents",
table: "Provider",
type: "tinyint(1)",
nullable: false,
defaultValue: false);
migrationBuilder.AddColumn<Guid>(
name: "ProviderId",
table: "Event",
type: "char(36)",
nullable: true,
collation: "ascii_general_ci");
migrationBuilder.AddColumn<Guid>(
name: "ProviderUserId",
table: "Event",
type: "char(36)",
nullable: true,
collation: "ascii_general_ci");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "UseEvents",
table: "Provider");
migrationBuilder.DropColumn(
name: "ProviderId",
table: "Event");
migrationBuilder.DropColumn(
name: "ProviderUserId",
table: "Event");
migrationBuilder.CreateTable(
name: "ProviderOrganizationProviderUser",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
CreationDate = table.Column<DateTime>(type: "datetime(6)", nullable: false),
Permissions = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
ProviderOrganizationId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
ProviderUserId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
RevisionDate = table.Column<DateTime>(type: "datetime(6)", nullable: false),
Type = table.Column<byte>(type: "tinyint unsigned", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ProviderOrganizationProviderUser", x => x.Id);
table.ForeignKey(
name: "FK_ProviderOrganizationProviderUser_ProviderOrganization_Provid~",
column: x => x.ProviderOrganizationId,
principalTable: "ProviderOrganization",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_ProviderOrganizationProviderUser_ProviderUser_ProviderUserId",
column: x => x.ProviderUserId,
principalTable: "ProviderUser",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateIndex(
name: "IX_ProviderOrganizationProviderUser_ProviderOrganizationId",
table: "ProviderOrganizationProviderUser",
column: "ProviderOrganizationId");
migrationBuilder.CreateIndex(
name: "IX_ProviderOrganizationProviderUser_ProviderUserId",
table: "ProviderOrganizationProviderUser",
column: "ProviderUserId");
}
}
}

View File

@ -275,6 +275,12 @@ namespace Bit.MySqlMigrations.Migrations
b.Property<Guid?>("PolicyId")
.HasColumnType("char(36)");
b.Property<Guid?>("ProviderId")
.HasColumnType("char(36)");
b.Property<Guid?>("ProviderUserId")
.HasColumnType("char(36)");
b.Property<int>("Type")
.HasColumnType("int");
@ -693,6 +699,9 @@ namespace Bit.MySqlMigrations.Migrations
b.Property<byte>("Status")
.HasColumnType("tinyint unsigned");
b.Property<bool>("UseEvents")
.HasColumnType("tinyint(1)");
b.HasKey("Id");
b.ToTable("Provider");
@ -730,38 +739,6 @@ namespace Bit.MySqlMigrations.Migrations
b.ToTable("ProviderOrganization");
});
modelBuilder.Entity("Bit.Core.Models.EntityFramework.Provider.ProviderOrganizationProviderUser", b =>
{
b.Property<Guid>("Id")
.HasColumnType("char(36)");
b.Property<DateTime>("CreationDate")
.HasColumnType("datetime(6)");
b.Property<string>("Permissions")
.HasColumnType("longtext");
b.Property<Guid>("ProviderOrganizationId")
.HasColumnType("char(36)");
b.Property<Guid>("ProviderUserId")
.HasColumnType("char(36)");
b.Property<DateTime>("RevisionDate")
.HasColumnType("datetime(6)");
b.Property<byte>("Type")
.HasColumnType("tinyint unsigned");
b.HasKey("Id");
b.HasIndex("ProviderOrganizationId");
b.HasIndex("ProviderUserId");
b.ToTable("ProviderOrganizationProviderUser");
});
modelBuilder.Entity("Bit.Core.Models.EntityFramework.Provider.ProviderUser", b =>
{
b.Property<Guid>("Id")
@ -1350,25 +1327,6 @@ namespace Bit.MySqlMigrations.Migrations
b.Navigation("Provider");
});
modelBuilder.Entity("Bit.Core.Models.EntityFramework.Provider.ProviderOrganizationProviderUser", b =>
{
b.HasOne("Bit.Core.Models.EntityFramework.Provider.ProviderOrganization", "ProviderOrganization")
.WithMany()
.HasForeignKey("ProviderOrganizationId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Bit.Core.Models.EntityFramework.Provider.ProviderUser", "ProviderUser")
.WithMany()
.HasForeignKey("ProviderUserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("ProviderOrganization");
b.Navigation("ProviderUser");
});
modelBuilder.Entity("Bit.Core.Models.EntityFramework.Provider.ProviderUser", b =>
{
b.HasOne("Bit.Core.Models.EntityFramework.Provider.Provider", "Provider")

View File

@ -0,0 +1,76 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Bit.PostgresMigrations.Migrations
{
public partial class RemoveProviderOrganizationProviderUser : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "ProviderOrganizationProviderUser");
migrationBuilder.AddColumn<Guid>(
name: "ProviderId",
table: "Event",
type: "uuid",
nullable: true);
migrationBuilder.AddColumn<Guid>(
name: "ProviderUserId",
table: "Event",
type: "uuid",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "ProviderId",
table: "Event");
migrationBuilder.DropColumn(
name: "ProviderUserId",
table: "Event");
migrationBuilder.CreateTable(
name: "ProviderOrganizationProviderUser",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
CreationDate = table.Column<DateTime>(type: "timestamp without time zone", nullable: false),
Permissions = table.Column<string>(type: "text", nullable: true),
ProviderOrganizationId = table.Column<Guid>(type: "uuid", nullable: false),
ProviderUserId = table.Column<Guid>(type: "uuid", nullable: false),
RevisionDate = table.Column<DateTime>(type: "timestamp without time zone", nullable: false),
Type = table.Column<byte>(type: "smallint", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ProviderOrganizationProviderUser", x => x.Id);
table.ForeignKey(
name: "FK_ProviderOrganizationProviderUser_ProviderOrganization_Provi~",
column: x => x.ProviderOrganizationId,
principalTable: "ProviderOrganization",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_ProviderOrganizationProviderUser_ProviderUser_ProviderUserId",
column: x => x.ProviderUserId,
principalTable: "ProviderUser",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_ProviderOrganizationProviderUser_ProviderOrganizationId",
table: "ProviderOrganizationProviderUser",
column: "ProviderOrganizationId");
migrationBuilder.CreateIndex(
name: "IX_ProviderOrganizationProviderUser_ProviderUserId",
table: "ProviderOrganizationProviderUser",
column: "ProviderUserId");
}
}
}

View File

@ -278,6 +278,12 @@ namespace Bit.PostgresMigrations.Migrations
b.Property<Guid?>("PolicyId")
.HasColumnType("uuid");
b.Property<Guid?>("ProviderId")
.HasColumnType("uuid");
b.Property<Guid?>("ProviderUserId")
.HasColumnType("uuid");
b.Property<int>("Type")
.HasColumnType("integer");
@ -737,38 +743,6 @@ namespace Bit.PostgresMigrations.Migrations
b.ToTable("ProviderOrganization");
});
modelBuilder.Entity("Bit.Core.Models.EntityFramework.Provider.ProviderOrganizationProviderUser", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<DateTime>("CreationDate")
.HasColumnType("timestamp without time zone");
b.Property<string>("Permissions")
.HasColumnType("text");
b.Property<Guid>("ProviderOrganizationId")
.HasColumnType("uuid");
b.Property<Guid>("ProviderUserId")
.HasColumnType("uuid");
b.Property<DateTime>("RevisionDate")
.HasColumnType("timestamp without time zone");
b.Property<byte>("Type")
.HasColumnType("smallint");
b.HasKey("Id");
b.HasIndex("ProviderOrganizationId");
b.HasIndex("ProviderUserId");
b.ToTable("ProviderOrganizationProviderUser");
});
modelBuilder.Entity("Bit.Core.Models.EntityFramework.Provider.ProviderUser", b =>
{
b.Property<Guid>("Id")
@ -1362,25 +1336,6 @@ namespace Bit.PostgresMigrations.Migrations
b.Navigation("Provider");
});
modelBuilder.Entity("Bit.Core.Models.EntityFramework.Provider.ProviderOrganizationProviderUser", b =>
{
b.HasOne("Bit.Core.Models.EntityFramework.Provider.ProviderOrganization", "ProviderOrganization")
.WithMany()
.HasForeignKey("ProviderOrganizationId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Bit.Core.Models.EntityFramework.Provider.ProviderUser", "ProviderUser")
.WithMany()
.HasForeignKey("ProviderUserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("ProviderOrganization");
b.Navigation("ProviderUser");
});
modelBuilder.Entity("Bit.Core.Models.EntityFramework.Provider.ProviderUser", b =>
{
b.HasOne("Bit.Core.Models.EntityFramework.Provider.Provider", "Provider")