mirror of https://github.com/Squidex/squidex.git
Browse Source
* Batch update for elastic. * Cleanup * Fix bulk updates. * Lets encryptpull/593/head
committed by
GitHub
26 changed files with 665 additions and 247 deletions
@ -1,23 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using MongoDB.Bson.Serialization.Attributes; |
|||
|
|||
namespace Squidex.Domain.Users.MongoDb |
|||
{ |
|||
public sealed class MongoKey |
|||
{ |
|||
[BsonId] |
|||
public string Id { get; set; } |
|||
|
|||
[BsonElement] |
|||
public string Key { get; set; } |
|||
|
|||
[BsonElement] |
|||
public MongoKeyParameters Parameters { get; set; } |
|||
} |
|||
} |
|||
@ -1,64 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Security.Cryptography; |
|||
|
|||
#pragma warning disable IDE0017 // Simplify object initialization
|
|||
|
|||
namespace Squidex.Domain.Users.MongoDb |
|||
{ |
|||
public sealed class MongoKeyParameters |
|||
{ |
|||
public byte[] D { get; set; } |
|||
|
|||
public byte[] DP { get; set; } |
|||
|
|||
public byte[] DQ { get; set; } |
|||
|
|||
public byte[] Exponent { get; set; } |
|||
|
|||
public byte[] InverseQ { get; set; } |
|||
|
|||
public byte[] Modulus { get; set; } |
|||
|
|||
public byte[] P { get; set; } |
|||
|
|||
public byte[] Q { get; set; } |
|||
|
|||
public static MongoKeyParameters Create(RSAParameters source) |
|||
{ |
|||
var mongoParameters = new MongoKeyParameters(); |
|||
|
|||
mongoParameters.D = source.D; |
|||
mongoParameters.DP = source.DP; |
|||
mongoParameters.DQ = source.DQ; |
|||
mongoParameters.Exponent = source.Exponent; |
|||
mongoParameters.InverseQ = source.InverseQ; |
|||
mongoParameters.Modulus = source.Modulus; |
|||
mongoParameters.P = source.P; |
|||
mongoParameters.Q = source.Q; |
|||
|
|||
return mongoParameters; |
|||
} |
|||
|
|||
public RSAParameters ToParameters() |
|||
{ |
|||
var parameters = default(RSAParameters); |
|||
|
|||
parameters.D = D; |
|||
parameters.DP = DP; |
|||
parameters.DQ = DQ; |
|||
parameters.Exponent = Exponent; |
|||
parameters.InverseQ = InverseQ; |
|||
parameters.Modulus = Modulus; |
|||
parameters.P = P; |
|||
parameters.Q = Q; |
|||
|
|||
return parameters; |
|||
} |
|||
} |
|||
} |
|||
@ -1,20 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using MongoDB.Bson.Serialization.Attributes; |
|||
|
|||
namespace Squidex.Domain.Users.MongoDb |
|||
{ |
|||
public sealed class MongoXmlEntity |
|||
{ |
|||
[BsonId] |
|||
public string FriendlyName { get; set; } |
|||
|
|||
[BsonRequired] |
|||
public string Xml { get; set; } |
|||
} |
|||
} |
|||
@ -1,51 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Xml.Linq; |
|||
using Microsoft.AspNetCore.DataProtection.Repositories; |
|||
using MongoDB.Bson; |
|||
using MongoDB.Driver; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Domain.Users.MongoDb |
|||
{ |
|||
public sealed class MongoXmlRepository : IXmlRepository |
|||
{ |
|||
private static readonly ReplaceOptions UpsertReplace = new ReplaceOptions { IsUpsert = true }; |
|||
private readonly IMongoCollection<MongoXmlEntity> collection; |
|||
|
|||
public MongoXmlRepository(IMongoDatabase mongoDatabase) |
|||
{ |
|||
Guard.NotNull(mongoDatabase, nameof(mongoDatabase)); |
|||
|
|||
collection = mongoDatabase.GetCollection<MongoXmlEntity>("States_Repository"); |
|||
} |
|||
|
|||
public IReadOnlyCollection<XElement> GetAllElements() |
|||
{ |
|||
var documents = collection.Find(new BsonDocument()).ToList(); |
|||
|
|||
var elements = documents.Select(x => XElement.Parse(x.Xml)).ToList(); |
|||
|
|||
return elements; |
|||
} |
|||
|
|||
public void StoreElement(XElement element, string friendlyName) |
|||
{ |
|||
var document = new MongoXmlEntity |
|||
{ |
|||
FriendlyName = friendlyName |
|||
}; |
|||
|
|||
document.Xml = element.ToString(); |
|||
|
|||
collection.ReplaceOne(x => x.FriendlyName == friendlyName, document, UpsertReplace); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,59 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using LettuceEncrypt.Accounts; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.States; |
|||
|
|||
namespace Squidex.Domain.Users |
|||
{ |
|||
public sealed class DefaultCertificateAccountStore : IAccountStore |
|||
{ |
|||
private readonly ISnapshotStore<State, Guid> store; |
|||
|
|||
[CollectionName("Identity_certificateAccount")] |
|||
public sealed class State |
|||
{ |
|||
public AccountModel Account { get; set; } |
|||
|
|||
public State() |
|||
{ |
|||
} |
|||
|
|||
public State(AccountModel account) |
|||
{ |
|||
Account = account; |
|||
} |
|||
} |
|||
|
|||
public DefaultCertificateAccountStore(ISnapshotStore<State, Guid> store) |
|||
{ |
|||
Guard.NotNull(store, nameof(store)); |
|||
|
|||
this.store = store; |
|||
} |
|||
|
|||
public async Task<AccountModel?> GetAccountAsync(CancellationToken cancellationToken) |
|||
{ |
|||
var (value, _) = await store.ReadAsync(default); |
|||
|
|||
return value?.Account; |
|||
} |
|||
|
|||
public Task SaveAccountAsync(AccountModel account, CancellationToken cancellationToken) |
|||
{ |
|||
Guard.NotNull(account, nameof(account)); |
|||
|
|||
var state = new State(account); |
|||
|
|||
return store.WriteAsync(default, state, EtagVersion.Any, 0); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,73 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Security.Cryptography.X509Certificates; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using LettuceEncrypt; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.States; |
|||
|
|||
namespace Squidex.Domain.Users |
|||
{ |
|||
public sealed class DefaultCertificateStore : ICertificateRepository, ICertificateSource |
|||
{ |
|||
private readonly ISnapshotStore<State, Guid> store; |
|||
|
|||
[CollectionName("Identity_Certificates")] |
|||
public sealed class State |
|||
{ |
|||
public byte[] Certificate { get; set; } |
|||
|
|||
public State() |
|||
{ |
|||
} |
|||
|
|||
public State(X509Certificate2 certificate) |
|||
{ |
|||
Certificate = certificate.Export(X509ContentType.Pfx); |
|||
} |
|||
|
|||
public X509Certificate2 ToCertificate() |
|||
{ |
|||
return new X509Certificate2(Certificate); |
|||
} |
|||
} |
|||
|
|||
public DefaultCertificateStore(ISnapshotStore<State, Guid> store) |
|||
{ |
|||
Guard.NotNull(store, nameof(store)); |
|||
|
|||
this.store = store; |
|||
} |
|||
|
|||
public async Task<IEnumerable<X509Certificate2>> GetCertificatesAsync(CancellationToken cancellationToken = default) |
|||
{ |
|||
var result = new List<X509Certificate2>(); |
|||
|
|||
await store.ReadAllAsync((state, _) => |
|||
{ |
|||
result.Add(state.ToCertificate()); |
|||
|
|||
return Task.CompletedTask; |
|||
}, cancellationToken); |
|||
|
|||
return result; |
|||
} |
|||
|
|||
public Task SaveAsync(X509Certificate2 certificate, CancellationToken cancellationToken = default) |
|||
{ |
|||
Guard.NotNull(certificate, nameof(certificate)); |
|||
|
|||
var state = new State(certificate); |
|||
|
|||
return store.WriteAsync(Guid.NewGuid(), state, EtagVersion.Any, 0); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,69 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using System.Xml.Linq; |
|||
using Microsoft.AspNetCore.DataProtection.Repositories; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.States; |
|||
|
|||
namespace Squidex.Domain.Users |
|||
{ |
|||
public sealed class DefaultXmlRepository : IXmlRepository |
|||
{ |
|||
private readonly ISnapshotStore<State, string> store; |
|||
|
|||
[CollectionName("Identity_Xml")] |
|||
public sealed class State |
|||
{ |
|||
public string Xml { get; set; } |
|||
|
|||
public State() |
|||
{ |
|||
} |
|||
|
|||
public State(XElement xml) |
|||
{ |
|||
Xml = xml.ToString(); |
|||
} |
|||
|
|||
public XElement ToXml() |
|||
{ |
|||
return XElement.Parse(Xml); |
|||
} |
|||
} |
|||
|
|||
public DefaultXmlRepository(ISnapshotStore<State, string> store) |
|||
{ |
|||
Guard.NotNull(store, nameof(store)); |
|||
|
|||
this.store = store; |
|||
} |
|||
|
|||
public IReadOnlyCollection<XElement> GetAllElements() |
|||
{ |
|||
var result = new List<XElement>(); |
|||
|
|||
store.ReadAllAsync((state, _) => |
|||
{ |
|||
result.Add(state.ToXml()); |
|||
|
|||
return Task.CompletedTask; |
|||
}).Wait(); |
|||
|
|||
return result; |
|||
} |
|||
|
|||
public void StoreElement(XElement element, string friendlyName) |
|||
{ |
|||
var state = new State(element); |
|||
|
|||
store.WriteAsync(friendlyName, state, EtagVersion.Any, 0); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,51 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using FakeItEasy; |
|||
using LettuceEncrypt.Accounts; |
|||
using Squidex.Infrastructure.States; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Users |
|||
{ |
|||
public class DefaultCertificateAccountStoreTests |
|||
{ |
|||
private readonly ISnapshotStore<DefaultCertificateAccountStore.State, Guid> store = A.Fake<ISnapshotStore<DefaultCertificateAccountStore.State, Guid>>(); |
|||
private readonly DefaultCertificateAccountStore sut; |
|||
|
|||
public DefaultCertificateAccountStoreTests() |
|||
{ |
|||
sut = new DefaultCertificateAccountStore(store); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_read_from_store() |
|||
{ |
|||
var model = new AccountModel(); |
|||
|
|||
A.CallTo(() => store.ReadAsync(default)) |
|||
.Returns((new DefaultCertificateAccountStore.State { Account = model }, 0)); |
|||
|
|||
var result = await sut.GetAccountAsync(default); |
|||
|
|||
Assert.Same(model, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_write_to_store() |
|||
{ |
|||
var model = new AccountModel(); |
|||
|
|||
await sut.SaveAccountAsync(model, default); |
|||
|
|||
A.CallTo(() => store.WriteAsync(A<Guid>._, A<DefaultCertificateAccountStore.State>._, A<long>._, 0)) |
|||
.MustHaveHappened(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,72 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Linq; |
|||
using System.Security.Cryptography; |
|||
using System.Security.Cryptography.X509Certificates; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using FakeItEasy; |
|||
using Squidex.Infrastructure.States; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Users |
|||
{ |
|||
public sealed class DefaultCertificateStoreTests |
|||
{ |
|||
private readonly ISnapshotStore<DefaultCertificateStore.State, Guid> store = A.Fake<ISnapshotStore<DefaultCertificateStore.State, Guid>>(); |
|||
private readonly DefaultCertificateStore sut; |
|||
|
|||
public DefaultCertificateStoreTests() |
|||
{ |
|||
sut = new DefaultCertificateStore(store); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_read_from_store() |
|||
{ |
|||
A.CallTo(() => store.ReadAllAsync(A<Func<DefaultCertificateStore.State, long, Task>>._, A<CancellationToken>._)) |
|||
.Invokes((Func<DefaultCertificateStore.State, long, Task> callback, CancellationToken _) => |
|||
{ |
|||
callback(new DefaultCertificateStore.State |
|||
{ |
|||
Certificate = MakeCert().Export(X509ContentType.Pfx) |
|||
}, 0); |
|||
|
|||
callback(new DefaultCertificateStore.State |
|||
{ |
|||
Certificate = MakeCert().Export(X509ContentType.Pfx) |
|||
}, 0); |
|||
}); |
|||
|
|||
var xml = await sut.GetCertificatesAsync(); |
|||
|
|||
Assert.Equal(2, xml.Count()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_write_to_store() |
|||
{ |
|||
var certificate = MakeCert(); |
|||
|
|||
await sut.SaveAsync(certificate, default); |
|||
|
|||
A.CallTo(() => store.WriteAsync(A<Guid>._, A<DefaultCertificateStore.State>._, A<long>._, 0)) |
|||
.MustHaveHappened(); |
|||
} |
|||
|
|||
private static X509Certificate2 MakeCert() |
|||
{ |
|||
var ecdsa = ECDsa.Create(); |
|||
|
|||
var certificateRequest = new CertificateRequest("cn=foobar", ecdsa, HashAlgorithmName.SHA256); |
|||
|
|||
return certificateRequest.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(5)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,62 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using FakeItEasy; |
|||
using Squidex.Infrastructure.States; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Users |
|||
{ |
|||
public class DefaultKeyStoreTests |
|||
{ |
|||
private readonly ISnapshotStore<DefaultKeyStore.State, Guid> store = A.Fake<ISnapshotStore<DefaultKeyStore.State, Guid>>(); |
|||
private readonly DefaultKeyStore sut; |
|||
|
|||
public DefaultKeyStoreTests() |
|||
{ |
|||
sut = new DefaultKeyStore(store); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_generate_signing_credentials_once() |
|||
{ |
|||
A.CallTo(() => store.ReadAsync(A<Guid>._)) |
|||
.Returns((null!, 0)); |
|||
|
|||
var credentials1 = await sut.GetSigningCredentialsAsync(); |
|||
var credentials2 = await sut.GetSigningCredentialsAsync(); |
|||
|
|||
Assert.Same(credentials1, credentials2); |
|||
|
|||
A.CallTo(() => store.ReadAsync(A<Guid>._)) |
|||
.MustHaveHappenedOnceExactly(); |
|||
|
|||
A.CallTo(() => store.WriteAsync(A<Guid>._, A<DefaultKeyStore.State>._, 0, 0)) |
|||
.MustHaveHappenedOnceExactly(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_generate_validation_keys_once() |
|||
{ |
|||
A.CallTo(() => store.ReadAsync(A<Guid>._)) |
|||
.Returns((null!, 0)); |
|||
|
|||
var credentials1 = await sut.GetValidationKeysAsync(); |
|||
var credentials2 = await sut.GetValidationKeysAsync(); |
|||
|
|||
Assert.Same(credentials1, credentials2); |
|||
|
|||
A.CallTo(() => store.ReadAsync(A<Guid>._)) |
|||
.MustHaveHappenedOnceExactly(); |
|||
|
|||
A.CallTo(() => store.WriteAsync(A<Guid>._, A<DefaultKeyStore.State>._, 0, 0)) |
|||
.MustHaveHappenedOnceExactly(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,61 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using System.Xml.Linq; |
|||
using FakeItEasy; |
|||
using Squidex.Infrastructure.States; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Users |
|||
{ |
|||
public sealed class DefaultXmlRepositoryTests |
|||
{ |
|||
private readonly ISnapshotStore<DefaultXmlRepository.State, string> store = A.Fake<ISnapshotStore<DefaultXmlRepository.State, string>>(); |
|||
private readonly DefaultXmlRepository sut; |
|||
|
|||
public DefaultXmlRepositoryTests() |
|||
{ |
|||
sut = new DefaultXmlRepository(store); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_read_from_store() |
|||
{ |
|||
A.CallTo(() => store.ReadAllAsync(A< Func<DefaultXmlRepository.State, long, Task>>._, A<CancellationToken>._)) |
|||
.Invokes((Func<DefaultXmlRepository.State, long, Task> callback, CancellationToken _) => |
|||
{ |
|||
callback(new DefaultXmlRepository.State |
|||
{ |
|||
Xml = new XElement("a").ToString() |
|||
}, 0); |
|||
|
|||
callback(new DefaultXmlRepository.State |
|||
{ |
|||
Xml = new XElement("b").ToString() |
|||
}, 0); |
|||
}); |
|||
|
|||
var xml = sut.GetAllElements(); |
|||
|
|||
Assert.Equal(2, xml.Count); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_write_to_store() |
|||
{ |
|||
var xml = new XElement("x"); |
|||
|
|||
sut.StoreElement(xml, "name"); |
|||
|
|||
A.CallTo(() => store.WriteAsync("name", A<DefaultXmlRepository.State>._, A<long>._, 0)) |
|||
.MustHaveHappened(); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue