mirror of https://github.com/Squidex/squidex.git
16 changed files with 5 additions and 494 deletions
@ -1,59 +0,0 @@ |
|||
// ==========================================================================
|
|||
// 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); |
|||
} |
|||
} |
|||
} |
|||
@ -1,73 +0,0 @@ |
|||
// ==========================================================================
|
|||
// 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); |
|||
} |
|||
} |
|||
} |
|||
@ -1,49 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Http; |
|||
using Microsoft.Extensions.Options; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Web.Pipeline |
|||
{ |
|||
public sealed class EnforceHttpsMiddleware : IMiddleware |
|||
{ |
|||
private readonly UrlsOptions urlsOptions; |
|||
|
|||
public EnforceHttpsMiddleware(IOptions<UrlsOptions> urlsOptions) |
|||
{ |
|||
Guard.NotNull(urlsOptions, nameof(urlsOptions)); |
|||
|
|||
this.urlsOptions = urlsOptions.Value; |
|||
} |
|||
|
|||
public async Task InvokeAsync(HttpContext context, RequestDelegate next) |
|||
{ |
|||
if (!urlsOptions.EnforceHTTPS) |
|||
{ |
|||
await next(context); |
|||
} |
|||
else |
|||
{ |
|||
var hostName = context.Request.Host.ToString().ToLowerInvariant(); |
|||
|
|||
if (!context.Request.IsHttps) |
|||
{ |
|||
var newUrl = string.Concat("https://", hostName, context.Request.Path, context.Request.QueryString); |
|||
|
|||
context.Response.Redirect(newUrl, true); |
|||
} |
|||
else |
|||
{ |
|||
await next(context); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,51 +0,0 @@ |
|||
// ==========================================================================
|
|||
// 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(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,72 +0,0 @@ |
|||
// ==========================================================================
|
|||
// 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)); |
|||
} |
|||
} |
|||
} |
|||
@ -1,85 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Http; |
|||
using Xunit; |
|||
using Options = Microsoft.Extensions.Options.Options; |
|||
|
|||
namespace Squidex.Web.Pipeline |
|||
{ |
|||
public class EnforceHttpsMiddlewareTests |
|||
{ |
|||
private readonly RequestDelegate next; |
|||
private readonly UrlsOptions options = new UrlsOptions(); |
|||
private readonly EnforceHttpsMiddleware sut; |
|||
private bool isNextCalled; |
|||
|
|||
public EnforceHttpsMiddlewareTests() |
|||
{ |
|||
next = context => |
|||
{ |
|||
isNextCalled = true; |
|||
|
|||
return Task.CompletedTask; |
|||
}; |
|||
|
|||
sut = new EnforceHttpsMiddleware(Options.Create(options)); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_make_permanent_redirect_if_redirect_is_required() |
|||
{ |
|||
var httpContext = CreateHttpContext(); |
|||
|
|||
options.EnforceHTTPS = true; |
|||
|
|||
await sut.InvokeAsync(httpContext, next); |
|||
|
|||
Assert.False(isNextCalled); |
|||
Assert.Equal("https://squidex.local/path?query=1", httpContext.Response.Headers["Location"]); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_not_redirect_if_already_on_https() |
|||
{ |
|||
var httpContext = CreateHttpContext("https"); |
|||
|
|||
options.EnforceHTTPS = true; |
|||
|
|||
await sut.InvokeAsync(httpContext, next); |
|||
|
|||
Assert.True(isNextCalled); |
|||
Assert.Null((string)httpContext.Response.Headers["Location"]); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_not_redirect_if_not_required() |
|||
{ |
|||
var httpContext = CreateHttpContext(); |
|||
|
|||
options.EnforceHTTPS = false; |
|||
|
|||
await sut.InvokeAsync(httpContext, next); |
|||
|
|||
Assert.True(isNextCalled); |
|||
Assert.Null((string)httpContext.Response.Headers["Location"]); |
|||
} |
|||
|
|||
private static DefaultHttpContext CreateHttpContext(string scheme = "http") |
|||
{ |
|||
var httpContext = new DefaultHttpContext(); |
|||
|
|||
httpContext.Request.QueryString = new QueryString("?query=1"); |
|||
httpContext.Request.Host = new HostString("squidex.local"); |
|||
httpContext.Request.Path = new PathString("/path"); |
|||
httpContext.Request.Scheme = scheme; |
|||
|
|||
return httpContext; |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue