1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-12 21:27:35 -05: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
74 changed files with 4007 additions and 635 deletions

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)
{