Compare commits

..
7 changed files with 16 additions and 86 deletions

View file

@ -9,12 +9,12 @@ include .$(APP_INSTALL_ENV).env
reinstall: copy-binaries
@for project in $(APP_PROJECT_NAMES); do \
echo "🚀 Starting $(APP_INSTALL_PREFIX)yavsc$${project}" ; \
echo "🚀 Starting $(APP_INSTALL_PREFIX)yavsc$${project}" \
sudo systemctl start $(APP_INSTALL_PREFIX)yavsc$${project} ; done
@echo "💯 backend started"
reinstall-prod:
make reinstall APP_INSTALL_ENV= APP_INSTALL_PREFIX= APP_INSTALL_ENV=production
make APP_INSTALL_ENV= APP_INSTALL_PREFIX= APP_INSTALL_ENV=production
generated/$(APP_INSTALL_PREFIX)yavscOrg.service:
generated/$(APP_INSTALL_PREFIX)yavscBlogs.service:
@ -82,16 +82,14 @@ install: copy-binaries copy-services
@sudo systemctl daemon-reload
@for project in $(APP_PROJECT_NAMES); \
do \
echo "🖒 enable $(APP_INSTALL_PREFIX)yavsc$${project}" ; \
sudo systemctl enable $(APP_INSTALL_PREFIX)yavsc$${project} ; \
echo "🚀 start $(APP_INSTALL_PREFIX)yavsc$${project}" ; \
sudo systemctl start $(APP_INSTALL_PREFIX)yavsc$${project} ; \
sudo systemctl enable yavsc$${project} ; \
sudo systemctl start yavsc$${project} ; \
done
stop-services:
@for project in $(APP_PROJECT_NAMES); \
do \
echo "🔥 Stopping $(APP_INSTALL_PREFIX)yavsc$${project}" ; \
sudo systemctl stop $(APP_INSTALL_PREFIX)yavsc$${project} ; \
done

View file

@ -209,12 +209,9 @@ public sealed class EstimateApiControllerTests : IClassFixture<ApiWebServerFixtu
/// <summary>
/// Seeds an estimate owned by alice (the provider) for the given
/// client, optionally already validated by the client. By default the
/// estimate is also provider-validated (i.e. the provider has submitted
/// it); pass <paramref name="providerValidated"/> = false to seed a
/// freshly inserted request the provider hasn't worked on yet.
/// client, optionally already validated by the client.
/// </summary>
private long SeedEstimate(string clientId, bool clientValidated, bool providerValidated = true)
private long SeedEstimate(string clientId, bool clientValidated)
{
using var scope = _fixture.Services.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
@ -232,7 +229,6 @@ public sealed class EstimateApiControllerTests : IClassFixture<ApiWebServerFixtu
{
new() { Name = "Prestation", Description = "Prestation de base", Count = 1, UnitaryCost = 120m, Currency = "EUR" },
},
ProviderValidationDate = providerValidated ? DateTime.UtcNow : default,
ClientValidationDate = clientValidated ? DateTime.UtcNow : default,
};
db.Estimates.Add(estimate);
@ -263,26 +259,6 @@ public sealed class EstimateApiControllerTests : IClassFixture<ApiWebServerFixtu
Assert.Equal("alice", item.GetProperty("ownerId").GetString());
}
[Fact]
public async Task GetOngoingEstimatesAsClient_excludes_inserted_requests_not_yet_provider_validated()
{
_fixture.ResetAndSeedActivityGraph();
var submittedId = SeedEstimate(clientId: "bob", clientValidated: false); // provider submitted: visible
SeedEstimate(clientId: "bob", clientValidated: false, providerValidated: false); // inserted, not worked on: excluded
using var http = NewClient("bob");
var response = await http.GetAsync("/api/v1/estimate/asclient", TestContext.Current.CancellationToken);
var body = await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken);
Assert.True(response.StatusCode == HttpStatusCode.OK, $"Unexpected status {(int)response.StatusCode} ({response.StatusCode}): {body}");
using var doc = JsonDocument.Parse(body);
var items = doc.RootElement.EnumerateArray().ToArray();
var item = Assert.Single(items);
Assert.Equal(submittedId, item.GetProperty("id").GetInt64());
}
[Fact]
public async Task GetOngoingEstimatesAsProvider_returns_only_ongoing_estimates_established_by_the_provider()
{

View file

@ -201,18 +201,12 @@ namespace Yavsc.Controllers
// GET: api/estimate/asclient
// Ongoing estimates (not yet validated by the client) where the
// current user is the client who initiated the request.
// An estimate only becomes visible to the client once the provider
// has actually established and submitted it (ProviderValidationDate);
// a freshly inserted request the provider hasn't worked on yet is
// not something the client can validate, so it's excluded.
[HttpGet("asclient")]
public IActionResult GetOngoingEstimatesAsClient()
{
var uid = User.GetUserId();
return Ok(_context.Estimates.Include(e => e.Bill)
.Where(e => e.ClientId == uid
&& e.ProviderValidationDate != default
&& e.ClientValidationDate == default));
.Where(e => e.ClientId == uid && e.ClientValidationDate == default));
}
// GET: api/estimate/asprovider

View file

@ -1,7 +1,4 @@
using System.Net;
using System.Net.Http;
using IdentityServer8.Stores;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
@ -43,26 +40,4 @@ public class AccountSmokeTests : IClassFixture<TestWebApplicationFactory>
Assert.Null(exception);
}
// Regression: anonymous access to a restricted page used to redirect to
// the ASP.NET Identity default LoginPath "/Account/Login", which has no
// controller here. The application cookie must redirect to the real
// sign-in endpoint at /signin instead.
[Fact]
public async Task Anonymous_access_to_protected_page_redirects_to_signin()
{
var client = _factory.CreateClient(new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false,
});
// EstimateController is [Authorize]; GET /Estimate hits Index().
var response = await client.GetAsync("/Estimate", TestContext.Current.CancellationToken);
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
var location = response.Headers.Location;
Assert.NotNull(location);
Assert.Equal("/signin", location.AbsolutePath);
Assert.Contains("ReturnUrl=%2FEstimate", location.Query);
}
}

View file

@ -215,18 +215,6 @@ public static class HostingExtensions
services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>>();
// The default application cookie LoginPath is "/Account/Login", which
// has no controller here — anonymous access to a restricted page must
// redirect to the actual sign-in endpoint instead. The paths mirror
// the route attributes on AccountController (Constants.SigninPath etc.,
// which carry a "~/" prefix that the cookie middleware does not want).
services.ConfigureApplicationCookie(options =>
{
options.LoginPath = "/signin";
options.LogoutPath = "/signout";
options.AccessDeniedPath = "/Account/AccessDenied";
});
// Dev-only: Chromium rejects SameSite=None without Secure on http://
// (e.g. http://localhost:5000). The default Identity cookie policy
// sets SameSite=None, which is invalid without Secure. Force Lax in

View file

@ -3,14 +3,14 @@
}
<markdown>
# La confidentialité
= La confidentialité
:hide-uri-scheme:
À aucun moment, aucune adresse postale,
aucune adresse e-mail ni aucun numéro de téléphone
ne sont transmis à personne. Seul le système
et son link:Contact[possesseur] ont accès
à ces informations.
xref:Contact[Get Report]
À aucun moment, aucune adresse postale, aucune adresse e-mail ni aucun numéro de téléphone
ne sont transmis à personne. Seul le système et son link:Contact[possesseur] ont accès à ces informations.
De plus, le droit de retrait est permanent et sa mise en oeuvre link:/Account/Delete[immédiate].
De plus, le droit de retrait est permanent
et sa [mise en oeuvre est immédiate](/Account/Delete).
</markdown>

View file

@ -113,7 +113,6 @@ public static class ServiceExtensions
options.ClaimsIssuer = authority;
options.Audience = audiences[0];
// Dev: every Yavsc resource service (Yavsc.Api, Yavsc.Blogs,
// Yavsc.Org itself) validates JWTs against the OP that runs
// on https://localhost:5001 with a self-signed dev cert.