mirror of
https://github.com/bitwarden/server
synced 2025-12-26 21:23:39 +00:00
initial commit of source
This commit is contained in:
67
src/Core/Repositories/DocumentDB/BaseRepository.cs
Normal file
67
src/Core/Repositories/DocumentDB/BaseRepository.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using Microsoft.Azure.Documents.Client;
|
||||
|
||||
namespace Bit.Core.Repositories.DocumentDB
|
||||
{
|
||||
public abstract class BaseRepository<T> where T : IDataObject
|
||||
{
|
||||
public BaseRepository(DocumentClient client, string databaseId, string documentType = null)
|
||||
{
|
||||
Client = client;
|
||||
DatabaseId = databaseId;
|
||||
DatabaseUri = UriFactory.CreateDatabaseUri(databaseId);
|
||||
PartitionResolver = client.PartitionResolvers[DatabaseUri.OriginalString];
|
||||
|
||||
if(string.IsNullOrWhiteSpace(documentType))
|
||||
{
|
||||
DocumentType = typeof(T).Name.ToLower();
|
||||
}
|
||||
else
|
||||
{
|
||||
DocumentType = documentType;
|
||||
}
|
||||
}
|
||||
|
||||
protected DocumentClient Client { get; private set; }
|
||||
protected string DatabaseId { get; private set; }
|
||||
protected Uri DatabaseUri { get; private set; }
|
||||
protected IPartitionResolver PartitionResolver { get; private set; }
|
||||
protected string DocumentType { get; private set; }
|
||||
|
||||
protected string ResolveSprocIdLink(T obj, string sprocId)
|
||||
{
|
||||
return string.Format("{0}/sprocs/{1}", ResolveCollectionIdLink(obj), sprocId);
|
||||
}
|
||||
|
||||
protected string ResolveSprocIdLink(string partitionKey, string sprocId)
|
||||
{
|
||||
return string.Format("{0}/sprocs/{1}", ResolveCollectionIdLink(partitionKey), sprocId);
|
||||
}
|
||||
|
||||
protected string ResolveDocumentIdLink(T obj)
|
||||
{
|
||||
return string.Format("{0}/docs/{1}", ResolveCollectionIdLink(obj), obj.Id);
|
||||
}
|
||||
|
||||
protected string ResolveDocumentIdLink(string id)
|
||||
{
|
||||
return ResolveDocumentIdLink(id, id);
|
||||
}
|
||||
|
||||
protected string ResolveDocumentIdLink(string partitionKey, string id)
|
||||
{
|
||||
return string.Format("{0}/docs/{1}", ResolveCollectionIdLink(partitionKey), id);
|
||||
}
|
||||
|
||||
protected string ResolveCollectionIdLink(T obj)
|
||||
{
|
||||
var partitionKey = PartitionResolver.GetPartitionKey(obj);
|
||||
return ResolveCollectionIdLink(partitionKey);
|
||||
}
|
||||
|
||||
protected string ResolveCollectionIdLink(object partitionKey)
|
||||
{
|
||||
return PartitionResolver.ResolveForCreate(partitionKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
40
src/Core/Repositories/DocumentDB/CipherRepository.cs
Normal file
40
src/Core/Repositories/DocumentDB/CipherRepository.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Azure.Documents.Client;
|
||||
using Bit.Core.Domains;
|
||||
|
||||
namespace Bit.Core.Repositories.DocumentDB
|
||||
{
|
||||
public class CipherRepository : BaseRepository<Cipher>, ICipherRepository
|
||||
{
|
||||
public CipherRepository(DocumentClient client, string databaseId, string documentType = null)
|
||||
: base(client, databaseId, documentType)
|
||||
{ }
|
||||
|
||||
public async Task UpdateDirtyCiphersAsync(IEnumerable<dynamic> ciphers)
|
||||
{
|
||||
// Make sure we are dealing with cipher types since we accept any via dynamic.
|
||||
var cleanedCiphers = ciphers.Where(c => c is Cipher);
|
||||
if(cleanedCiphers.Count() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var userId = ((Cipher)cleanedCiphers.First()).UserId;
|
||||
StoredProcedureResponse<int> sprocResponse = await Client.ExecuteStoredProcedureAsync<int>(
|
||||
ResolveSprocIdLink(userId, "bulkUpdateDirtyCiphers"),
|
||||
// Do sets of 50. Recursion will handle the rest below.
|
||||
cleanedCiphers.Take(50),
|
||||
userId,
|
||||
Cipher.TypeValue);
|
||||
|
||||
var replacedCount = sprocResponse.Response;
|
||||
if(replacedCount != cleanedCiphers.Count())
|
||||
{
|
||||
await UpdateDirtyCiphersAsync(cleanedCiphers.Skip(replacedCount));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
49
src/Core/Repositories/DocumentDB/FolderRepository.cs
Normal file
49
src/Core/Repositories/DocumentDB/FolderRepository.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Azure.Documents.Client;
|
||||
using Bit.Core.Domains;
|
||||
using Bit.Core.Enums;
|
||||
|
||||
namespace Bit.Core.Repositories.DocumentDB
|
||||
{
|
||||
public class FolderRepository : Repository<Folder>, IFolderRepository
|
||||
{
|
||||
public FolderRepository(DocumentClient client, string databaseId)
|
||||
: base(client, databaseId)
|
||||
{ }
|
||||
|
||||
public async Task<Folder> GetByIdAsync(string id, string userId)
|
||||
{
|
||||
var doc = await Client.ReadDocumentAsync(ResolveDocumentIdLink(userId, id));
|
||||
if(doc?.Resource == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var folder = (Folder)((dynamic)doc.Resource);
|
||||
if(folder.UserId != userId)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return folder;
|
||||
}
|
||||
|
||||
public Task<ICollection<Folder>> GetManyByUserIdAsync(string userId)
|
||||
{
|
||||
var docs = Client.CreateDocumentQuery<Folder>(DatabaseUri, null, userId)
|
||||
.Where(d => d.Type == Cipher.TypeValue && d.CipherType == CipherType.Folder && d.UserId == userId).AsEnumerable();
|
||||
|
||||
return Task.FromResult<ICollection<Folder>>(docs.ToList());
|
||||
}
|
||||
|
||||
public Task<ICollection<Folder>> GetManyByUserIdAsync(string userId, bool dirty)
|
||||
{
|
||||
var docs = Client.CreateDocumentQuery<Folder>(DatabaseUri, null, userId)
|
||||
.Where(d => d.Type == Cipher.TypeValue && d.CipherType == CipherType.Folder && d.UserId == userId && d.Dirty == dirty).AsEnumerable();
|
||||
|
||||
return Task.FromResult<ICollection<Folder>>(docs.ToList());
|
||||
}
|
||||
}
|
||||
}
|
||||
76
src/Core/Repositories/DocumentDB/Repository.cs
Normal file
76
src/Core/Repositories/DocumentDB/Repository.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Azure.Documents.Client;
|
||||
|
||||
namespace Bit.Core.Repositories.DocumentDB
|
||||
{
|
||||
public abstract class Repository<T> : BaseRepository<T>, IRepository<T> where T : IDataObject
|
||||
{
|
||||
public Repository(DocumentClient client, string databaseId, string documentType = null)
|
||||
: base(client, databaseId, documentType)
|
||||
{ }
|
||||
|
||||
public virtual Task<T> GetByIdAsync(string id)
|
||||
{
|
||||
// NOTE: Not an ideal condition, scanning all collections.
|
||||
// Override this method if you can implement a direct partition lookup based on the id.
|
||||
// Use the inherited GetByPartitionIdAsync method to implement your override.
|
||||
var docs = Client.CreateDocumentQuery<T>(DatabaseUri, new FeedOptions { MaxItemCount = 1 })
|
||||
.Where(d => d.Id == id).AsEnumerable();
|
||||
|
||||
return Task.FromResult(docs.FirstOrDefault());
|
||||
}
|
||||
|
||||
public virtual async Task CreateAsync(T obj)
|
||||
{
|
||||
var result = await Client.CreateDocumentAsync(DatabaseUri, obj);
|
||||
obj.Id = result.Resource.Id;
|
||||
}
|
||||
|
||||
public virtual async Task ReplaceAsync(T obj)
|
||||
{
|
||||
await Client.ReplaceDocumentAsync(ResolveDocumentIdLink(obj), obj);
|
||||
}
|
||||
|
||||
public virtual async Task UpsertAsync(T obj)
|
||||
{
|
||||
await Client.UpsertDocumentAsync(ResolveDocumentIdLink(obj), obj);
|
||||
}
|
||||
|
||||
public virtual async Task DeleteAsync(T obj)
|
||||
{
|
||||
await Client.DeleteDocumentAsync(ResolveDocumentIdLink(obj));
|
||||
}
|
||||
|
||||
public virtual async Task DeleteByIdAsync(string id)
|
||||
{
|
||||
// NOTE: Not an ideal condition, scanning all collections.
|
||||
// Override this method if you can implement a direct partition lookup based on the id.
|
||||
// Use the inherited DeleteByPartitionIdAsync method to implement your override.
|
||||
var docs = Client.CreateDocumentQuery(DatabaseUri, new FeedOptions { MaxItemCount = 1 })
|
||||
.Where(d => d.Id == id).AsEnumerable();
|
||||
|
||||
if(docs.Count() > 0)
|
||||
{
|
||||
await Client.DeleteDocumentAsync(docs.First().SelfLink);
|
||||
}
|
||||
}
|
||||
|
||||
protected async Task<T> GetByPartitionIdAsync(string id)
|
||||
{
|
||||
var doc = await Client.ReadDocumentAsync(ResolveDocumentIdLink(id));
|
||||
if(doc?.Resource == null)
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
|
||||
return (T)((dynamic)doc.Resource);
|
||||
}
|
||||
|
||||
protected async Task DeleteByPartitionIdAsync(string id)
|
||||
{
|
||||
await Client.DeleteDocumentAsync(ResolveDocumentIdLink(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
50
src/Core/Repositories/DocumentDB/SiteRepository.cs
Normal file
50
src/Core/Repositories/DocumentDB/SiteRepository.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Azure.Documents.Client;
|
||||
using Bit.Core.Domains;
|
||||
using Bit.Core.Enums;
|
||||
|
||||
namespace Bit.Core.Repositories.DocumentDB
|
||||
{
|
||||
public class SiteRepository : Repository<Site>, ISiteRepository
|
||||
{
|
||||
public SiteRepository(DocumentClient client, string databaseId)
|
||||
: base(client, databaseId)
|
||||
{ }
|
||||
|
||||
public async Task<Site> GetByIdAsync(string id, string userId)
|
||||
{
|
||||
var doc = await Client.ReadDocumentAsync(ResolveDocumentIdLink(userId, id));
|
||||
if(doc?.Resource == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var site = (Site)((dynamic)doc.Resource);
|
||||
if(site.UserId != userId)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return site;
|
||||
}
|
||||
|
||||
public Task<ICollection<Site>> GetManyByUserIdAsync(string userId)
|
||||
{
|
||||
var docs = Client.CreateDocumentQuery<Site>(DatabaseUri, null, userId)
|
||||
.Where(d => d.Type == Cipher.TypeValue && d.CipherType == CipherType.Site && d.UserId == userId).AsEnumerable();
|
||||
|
||||
return Task.FromResult<ICollection<Site>>(docs.ToList());
|
||||
}
|
||||
|
||||
public Task<ICollection<Site>> GetManyByUserIdAsync(string userId, bool dirty)
|
||||
{
|
||||
var docs = Client.CreateDocumentQuery<Site>(DatabaseUri, null, userId)
|
||||
.Where(d => d.Type == Cipher.TypeValue && d.CipherType == CipherType.Site && d.UserId == userId && d.Dirty == dirty).AsEnumerable();
|
||||
|
||||
return Task.FromResult<ICollection<Site>>(docs.ToList());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
// Update an array of dirty ciphers for a user.
|
||||
|
||||
function bulkUpdateDirtyCiphers(ciphers, userId) {
|
||||
var context = getContext();
|
||||
var collection = context.getCollection();
|
||||
var collectionLink = collection.getSelfLink();
|
||||
var response = context.getResponse();
|
||||
|
||||
var count = 0;
|
||||
|
||||
// Validate input.
|
||||
if (!ciphers) {
|
||||
throw new Error("The ciphers array is undefined or null.");
|
||||
}
|
||||
|
||||
var ciphersLength = ciphers.length;
|
||||
if (ciphersLength == 0) {
|
||||
response.setBody(0);
|
||||
return;
|
||||
}
|
||||
|
||||
queryAndReplace(ciphers[count]);
|
||||
|
||||
function queryAndReplace(cipher, continuation) {
|
||||
var query = {
|
||||
query: "SELECT * FROM root r WHERE r.id = @id AND r.UserId = @userId AND r.type = 'cipher' AND r.Dirty = true",
|
||||
parameters: [{ name: '@id', value: cipher.id }, { name: '@userId', value: userId }]
|
||||
};
|
||||
|
||||
var requestOptions = { continuation: continuation };
|
||||
var accepted = collection.queryDocuments(collectionLink, query, requestOptions, function (err, documents, responseOptions) {
|
||||
if (err) throw err;
|
||||
|
||||
if (documents.length > 0) {
|
||||
replace(documents[0], cipher);
|
||||
}
|
||||
else if (responseOptions.continuation) {
|
||||
// try again
|
||||
queryAndReplace(cipher, responseOptions.continuation);
|
||||
}
|
||||
else {
|
||||
// doc not found, skip it
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
if (!accepted) {
|
||||
response.setBody(count);
|
||||
}
|
||||
}
|
||||
|
||||
function replace(doc, placementCipher) {
|
||||
// site
|
||||
if (doc.CipherType == 1) {
|
||||
doc.Username = placementCipher.Username;
|
||||
doc.Password = placementCipher.Password;
|
||||
doc.Notes = placementCipher.Notes;
|
||||
doc.Uri = placementCipher.Uri;
|
||||
}
|
||||
|
||||
doc.Name = placementCipher.Name;
|
||||
doc.RevisionDate = placementCipher.RevisionDate;
|
||||
// no longer dirty
|
||||
doc.Dirty = false;
|
||||
|
||||
var accepted = collection.replaceDocument(doc._self, doc, function (err) {
|
||||
if (err) throw err;
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
if (!accepted) {
|
||||
response.setBody(count);
|
||||
}
|
||||
}
|
||||
|
||||
function next() {
|
||||
count++;
|
||||
|
||||
if (count >= ciphersLength) {
|
||||
response.setBody(count);
|
||||
}
|
||||
else {
|
||||
queryAndReplace(ciphers[count]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
// Replace user document and mark all related ciphers as dirty.
|
||||
|
||||
function replaceUserAndDirtyCiphers(user) {
|
||||
var context = getContext();
|
||||
var collection = context.getCollection();
|
||||
var collectionLink = collection.getSelfLink();
|
||||
var response = context.getResponse();
|
||||
|
||||
// Validate input.
|
||||
if (!user) {
|
||||
throw new Error('The user is undefined or null.');
|
||||
}
|
||||
|
||||
getUser(function (userDoc) {
|
||||
replaceUser(userDoc, function (replacedDoc) {
|
||||
queryAndDirtyCiphers(function () {
|
||||
response.setBody(replacedDoc);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function getUser(callback, continuation) {
|
||||
var query = {
|
||||
query: 'SELECT * FROM root r WHERE r.id = @id',
|
||||
parameters: [{ name: '@id', value: user.id }]
|
||||
};
|
||||
|
||||
var requestOptions = { continuation: continuation };
|
||||
var accepted = collection.queryDocuments(collectionLink, query, requestOptions, function (err, documents, responseOptions) {
|
||||
if (err) throw err;
|
||||
|
||||
if (documents.length > 0) {
|
||||
callback(documents[0]);
|
||||
}
|
||||
else if (responseOptions.continuation) {
|
||||
getUser(responseOptions.continuation);
|
||||
}
|
||||
else {
|
||||
throw new Error('User not found.');
|
||||
}
|
||||
});
|
||||
|
||||
if (!accepted) {
|
||||
throw new Error('The stored procedure timed out.');
|
||||
}
|
||||
}
|
||||
|
||||
function replaceUser(userDoc, callback) {
|
||||
var accepted = collection.replaceDocument(userDoc._self, user, {}, function (err, replacedDoc) {
|
||||
if (err) throw err;
|
||||
|
||||
callback(replacedDoc);
|
||||
});
|
||||
|
||||
if (!accepted) {
|
||||
throw new Error('The stored procedure timed out.');
|
||||
}
|
||||
}
|
||||
|
||||
function queryAndDirtyCiphers(callback, continuation) {
|
||||
var query = {
|
||||
query: 'SELECT * FROM root r WHERE r.type = @type AND r.UserId = @userId',
|
||||
parameters: [{ name: '@type', value: 'cipher' }, { name: '@userId', value: user.id }]
|
||||
};
|
||||
|
||||
var requestOptions = { continuation: continuation };
|
||||
var accepted = collection.queryDocuments(collectionLink, query, requestOptions, function (err, documents, responseOptions) {
|
||||
if (err) throw err;
|
||||
|
||||
if (documents.length > 0) {
|
||||
dirtyCiphers(documents, callback);
|
||||
}
|
||||
else if (responseOptions.continuation) {
|
||||
queryAndDirtyCiphers(callback, responseOptions.continuation);
|
||||
}
|
||||
else {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
|
||||
if (!accepted) {
|
||||
throw new Error('The stored procedure timed out.');
|
||||
}
|
||||
}
|
||||
|
||||
function dirtyCiphers(documents, callback) {
|
||||
if (documents.length > 0) {
|
||||
// dirty the cipher
|
||||
documents[0].Dirty = true;
|
||||
|
||||
var requestOptions = { etag: documents[0]._etag };
|
||||
var accepted = collection.replaceDocument(documents[0]._self, documents[0], requestOptions, function (err) {
|
||||
if (err) throw err;
|
||||
|
||||
documents.shift();
|
||||
dirtyCiphers(documents, callback);
|
||||
});
|
||||
|
||||
if (!accepted) {
|
||||
throw new Error('The stored procedure timed out.');
|
||||
}
|
||||
}
|
||||
else {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
37
src/Core/Repositories/DocumentDB/UserRepository.cs
Normal file
37
src/Core/Repositories/DocumentDB/UserRepository.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Azure.Documents.Client;
|
||||
|
||||
namespace Bit.Core.Repositories.DocumentDB
|
||||
{
|
||||
public class UserRepository : Repository<Domains.User>, IUserRepository
|
||||
{
|
||||
public UserRepository(DocumentClient client, string databaseId)
|
||||
: base(client, databaseId)
|
||||
{ }
|
||||
|
||||
public override async Task<Domains.User> GetByIdAsync(string id)
|
||||
{
|
||||
return await GetByPartitionIdAsync(id);
|
||||
}
|
||||
|
||||
public Task<Domains.User> GetByEmailAsync(string email)
|
||||
{
|
||||
var docs = Client.CreateDocumentQuery<Domains.User>(DatabaseUri, new FeedOptions { MaxItemCount = 1 })
|
||||
.Where(d => d.Type == Domains.User.TypeValue && d.Email == email).AsEnumerable();
|
||||
|
||||
return Task.FromResult(docs.FirstOrDefault());
|
||||
}
|
||||
|
||||
public async Task ReplaceAndDirtyCiphersAsync(Domains.User user)
|
||||
{
|
||||
await Client.ExecuteStoredProcedureAsync<Domains.User>(ResolveSprocIdLink(user, "replaceUserAndDirtyCiphers"), user);
|
||||
}
|
||||
|
||||
public override async Task DeleteByIdAsync(string id)
|
||||
{
|
||||
await DeleteByPartitionIdAsync(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using Microsoft.Azure.Documents.Client;
|
||||
|
||||
namespace Bit.Core.Repositories.DocumentDB.Utilities
|
||||
{
|
||||
public class DocumentClientHelpers
|
||||
{
|
||||
public static DocumentClient InitClient(GlobalSettings.DocumentDBSettings settings)
|
||||
{
|
||||
var client = new DocumentClient(
|
||||
new Uri(settings.Uri),
|
||||
settings.Key,
|
||||
new ConnectionPolicy
|
||||
{
|
||||
ConnectionMode = ConnectionMode.Direct,
|
||||
ConnectionProtocol = Protocol.Tcp
|
||||
});
|
||||
|
||||
var hashResolver = new ManagedHashPartitionResolver(
|
||||
GetPartitionKeyExtractor(),
|
||||
settings.DatabaseId,
|
||||
settings.CollectionIdPrefix,
|
||||
settings.NumberOfCollections,
|
||||
null);
|
||||
|
||||
client.PartitionResolvers[UriFactory.CreateDatabaseUri(settings.DatabaseId).OriginalString] = hashResolver;
|
||||
client.OpenAsync().Wait();
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
private static Func<object, string> GetPartitionKeyExtractor()
|
||||
{
|
||||
return doc =>
|
||||
{
|
||||
if(doc is Domains.User)
|
||||
{
|
||||
return ((Domains.User)doc).Id;
|
||||
}
|
||||
|
||||
if(doc is Domains.Cipher)
|
||||
{
|
||||
return ((Domains.Cipher)doc).UserId;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Document type is not resolvable for the partition key extractor.");
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Azure.Documents.Client;
|
||||
using Microsoft.Azure.Documents.Partitioning;
|
||||
|
||||
namespace Bit.Core.Repositories.DocumentDB.Utilities
|
||||
{
|
||||
public class ManagedHashPartitionResolver : HashPartitionResolver
|
||||
{
|
||||
public ManagedHashPartitionResolver(
|
||||
Func<object, string> partitionKeyExtractor,
|
||||
string databaseId,
|
||||
string collectionIdPrefix,
|
||||
int numberOfCollections,
|
||||
IHashGenerator hashGenerator = null)
|
||||
: base(
|
||||
partitionKeyExtractor,
|
||||
GetCollectionIds(databaseId, collectionIdPrefix, numberOfCollections),
|
||||
128,
|
||||
hashGenerator)
|
||||
{ }
|
||||
|
||||
private static List<string> GetCollectionIds(string databaseId, string collectionIdPrefix, int numberOfCollections)
|
||||
{
|
||||
var collections = new List<string>();
|
||||
for(int i = 0; i < numberOfCollections; i++)
|
||||
{
|
||||
var collectionIdUri = UriFactory.CreateDocumentCollectionUri(databaseId, string.Concat(collectionIdPrefix, i));
|
||||
collections.Add(collectionIdUri.OriginalString);
|
||||
}
|
||||
|
||||
return collections;
|
||||
}
|
||||
}
|
||||
}
|
||||
10
src/Core/Repositories/ICipherRepository.cs
Normal file
10
src/Core/Repositories/ICipherRepository.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Bit.Core.Repositories
|
||||
{
|
||||
public interface ICipherRepository
|
||||
{
|
||||
Task UpdateDirtyCiphersAsync(IEnumerable<dynamic> ciphers);
|
||||
}
|
||||
}
|
||||
13
src/Core/Repositories/IFolderRepository.cs
Normal file
13
src/Core/Repositories/IFolderRepository.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Domains;
|
||||
|
||||
namespace Bit.Core.Repositories
|
||||
{
|
||||
public interface IFolderRepository : IRepository<Folder>
|
||||
{
|
||||
Task<Folder> GetByIdAsync(string id, string userId);
|
||||
Task<ICollection<Folder>> GetManyByUserIdAsync(string userId);
|
||||
Task<ICollection<Folder>> GetManyByUserIdAsync(string userId, bool dirty);
|
||||
}
|
||||
}
|
||||
14
src/Core/Repositories/IRepository.cs
Normal file
14
src/Core/Repositories/IRepository.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Bit.Core.Repositories
|
||||
{
|
||||
public interface IRepository<T> where T : IDataObject
|
||||
{
|
||||
Task<T> GetByIdAsync(string id);
|
||||
Task CreateAsync(T obj);
|
||||
Task ReplaceAsync(T obj);
|
||||
Task UpsertAsync(T obj);
|
||||
Task DeleteByIdAsync(string id);
|
||||
Task DeleteAsync(T obj);
|
||||
}
|
||||
}
|
||||
14
src/Core/Repositories/ISiteRepository.cs
Normal file
14
src/Core/Repositories/ISiteRepository.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Domains;
|
||||
|
||||
namespace Bit.Core.Repositories
|
||||
{
|
||||
public interface ISiteRepository : IRepository<Site>
|
||||
{
|
||||
Task<Site> GetByIdAsync(string id, string userId);
|
||||
Task<ICollection<Site>> GetManyByUserIdAsync(string userId);
|
||||
Task<ICollection<Site>> GetManyByUserIdAsync(string userId, bool dirty);
|
||||
}
|
||||
}
|
||||
13
src/Core/Repositories/IUserRepository.cs
Normal file
13
src/Core/Repositories/IUserRepository.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Domains;
|
||||
|
||||
namespace Bit.Core.Repositories
|
||||
{
|
||||
public interface IUserRepository : IRepository<User>
|
||||
{
|
||||
Task<User> GetByEmailAsync(string email);
|
||||
Task ReplaceAndDirtyCiphersAsync(User user);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user